├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bootstrap ├── comp.c ├── io.h ├── sections.h └── trie.h ├── meta └── cloc_definition.txt └── src ├── builtins.co ├── codegen.co ├── compiler.co ├── elf.co ├── identifier_types.co ├── identifiers.co ├── main.co ├── printer.co ├── source.co ├── strings.co ├── syscalls.co ├── tokenizer.co ├── writer.co └── x86_64.co /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2022 by Hannes Bredberg 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: build/comp.s3 2 | 3 | .PHONY: test clean 4 | .SECONDARY:; 5 | 6 | os_name = $(shell uname -s) 7 | all_co_files = $(shell find src -name '*.co') 8 | 9 | clean: 10 | rm -rf build 11 | 12 | build/comp.s3: build/comp.s2 $(all_co_files) 13 | $< src/main.co 0x100000 -o $@ 14 | 15 | build/comp.s2: build/comp.s1 $(all_co_files) 16 | #$< src/main.co 0x100000 -o $@ 17 | strace $< src/main.co 0x100000 -o $@ 18 | #gdb $< -ex 'starti src/main.co 0x100000 -o $@' 19 | 20 | build/comp.s1: build/$(os_name)_bootstrap $(all_co_files) 21 | $< src/main.co 0x100000 -o $@ 22 | 23 | build/$(os_name)_bootstrap: bootstrap/comp.c $(shell find bootstrap -name '*.h') Makefile 24 | @mkdir -p $(@D) 25 | $(CC) $< -o $@ -Wall -g -ggdb -Werror 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Comp 2 | 3 | Comp is a typeless language that tries to still provide modern facilities such as imports and compile time evaluation. 4 | 5 | It is designed to be very easy to process, the compiler doing both parsing and code generation in a single pass. 6 | 7 | ## Example code 8 | 9 | ```zig 10 | var my_global_var; 11 | 12 | import "src/my_module.co" my_module; 13 | 14 | fn my_fun(arg) [my_local_var] { 15 | // Treats `my_global_var` as a pointer and dereferences it 16 | my_local_var = my_global_var[arg]; 17 | 18 | if(my_local_var) { 19 | // Asserts `arg`, on assertion failure, the `panic` function 20 | // in the root file is called: 21 | // if(!arg) @root.panic("Assertion failure: ", 0, @filename(), @line()) 22 | @assert(arg); 23 | 24 | // if(!arg) @root.panic("Assertion failure: ", "has argument", @filename(), @line()) 25 | @assert(arg, "has argument"); 26 | } else { 27 | loop { 28 | switch(my_global_var) { 29 | // Implicit default case right after `switch() {` 30 | // @root.panic("PANIC: ", "What is this?", @filename(), @line()) 31 | @panic("What is this?"); 32 | 33 | // Case ranges work like expected... 34 | case 'a'...'z': 35 | @todo("letter"); // @root.panic("TODO: ", "letter", @filename(), @line()) 36 | 37 | // Except that you can override cases in ranges listed before 38 | case 'a': 39 | case 'e': 40 | case 'i': 41 | case 'o': 42 | case 'u': 43 | print("Vowel!"); 44 | // `endcase;` to end a switch case 45 | endcase; 46 | 47 | case 'q': 48 | // Now you can break your loops from within a switch! 49 | break; 50 | 51 | case '0': 52 | // Continue works as expected, just continues the loop outside 53 | continue; 54 | 55 | case '.': 56 | // Fall through out of switch for everything else 57 | } 58 | } 59 | 60 | return my_module.hello(); 61 | } 62 | } 63 | ``` 64 | 65 | ## Notes 66 | There are a few limitations to simplify the implementation, namely: 67 | * Circular imports are disallowed 68 | * You can only access things previously declared/imported 69 | * Array subscript `my_buffer[idx]` parses the subscript as a byte offset 70 | * Array subscript `my_buffer[idx]` is always a pointer-length memory reference. For accesses of other sizes, check `@{read,write}{8,16,32}` in the builtin list 71 | 72 | ## Builtins 73 | Builtins are available as `builtin.` and `@` from every file. 74 | 75 | Vital for the language: 76 | * `read{8,16,32}(ptr)` 77 | 78 | Read from `ptr` with the specified bit size, returns a zero extended pointer length value for every read size. 79 | 80 | * `write{8,16,32}(ptr, value)` 81 | 82 | Write `value` to the address specified in `ptr`. Does the write with the size specified. 83 | 84 | * `syscall(arg...)` 85 | 86 | Does whatever a syscall means for your target. 87 | 88 | Linux example: `retval = @syscall(SYS_READ, fd, buf, len);` 89 | 90 | * `call(ptr, arg...)` 91 | 92 | Calls the function pointer `ptr` with any arguments you supply 93 | 94 | Would be missed if we didn't have them: 95 | * `root` 96 | 97 | A way to access the root source file (the one specified on the command line) in the project 98 | 99 | * `size_of(identifier)` 100 | 101 | Evaluates to the byte size of the object referenced by the identifier at compile time 102 | 103 | * `memcpy(dst, src, size)` 104 | 105 | Good ol' memcpy. Does what it says on the tin, **but returns an undefined value**. 106 | 107 | How we can have nice things: 108 | * `line()`: 109 | 110 | Evaluates to the current line number at the invocation site at compile time 111 | 112 | * `filename()`: 113 | 114 | Evaluates to the current source file path at the invocation site at compile time 115 | 116 | * `todo()`: 117 | 118 | Equivalent to `@root.panic("TODO: ", 0, @filename(), @line());` 119 | 120 | * `todo(message)`: 121 | 122 | Equivalent to `@root.panic("TODO: ", message, @filename(), @line());` 123 | 124 | * `panic()`: 125 | 126 | Equivalent to `@root.panic("PANIC: ", 0, @filename(), @line());` 127 | 128 | * `panic(message)`: 129 | 130 | Equivalent to `@root.panic("PANIC: ", message, @filename(), @line());` 131 | 132 | * `assert(expr)`: 133 | 134 | Equivalent to `if(!expr) { @root.panic("Assetion failure: ", 0, @filename(), @line()); }` 135 | 136 | * `assert(expr, message)`: 137 | 138 | Equivalent to `if(!expr) { @root.panic("Assetion failure: ", message, @filename(), @line()); }` 139 | 140 | ## Expressions 141 | The expressions are designed in such a way where there are no ambigous expressions, so no operator precedence or associativity is needed. 142 | * Complex expressions 143 | * Binary expressions 144 | 145 | ` ` 146 | 147 | Standard binary operators: 148 | * `+` Addition 149 | * `-` Subtraction 150 | * `*` Multiplication 151 | * `/` Division 152 | * `%` Modulus 153 | * `&` Bitwise and 154 | * `^` Bitwise xor 155 | * `|` Bitwise or 156 | * Unary expression 157 | 158 | ` ` 159 | 160 | Unary operators: 161 | * `~` bitwise not 162 | * `-` 2's complement negate 163 | 164 | * Ternary expression 165 | 166 | ` ? : ` 167 | 168 | If the first expression is nonzero, evaluates to the first expression, otherwise the second one 169 | 170 | ` ?: ` 171 | 172 | Evaluates to the first expression if nonzero, otherwise the second 173 | 174 | * Simple expressions 175 | * Parenthesis 176 | 177 | `()` 178 | 179 | * Integral literals 180 | 181 | `420` or `0x69` 182 | 183 | * Identifiers 184 | 185 | `my_var` 186 | 187 | * Call expressions 188 | 189 | `(, ...)` 190 | 191 | * Subscript expressions 192 | 193 | `[]` 194 | 195 | * In-place expression 196 | 197 | ` ` 198 | 199 | In-place binary operators: 200 | * `=` Assignment 201 | * `+=` Addition 202 | * `-=` Subtraction 203 | * `*=` Multiplication 204 | * `/=` Division 205 | * `%=` Modulus 206 | * `&=` Bitwise and 207 | * `^=` Bitwise xor 208 | * `|=` Bitwise or 209 | 210 | ## Types of statements 211 | All statements are followed either by a block or a semicolon. 212 | * `break` 213 | 214 | Jumps past the end of the current `loop` block 215 | 216 | * `continue` 217 | 218 | Jumps back to the start of the current `loop` block 219 | 220 | * `endcase` 221 | 222 | Jumps past the end of the current `switch` block 223 | 224 | * `if` 225 | 226 | Runs either the first or the second branch based on the condition. 227 | 228 | Can optionally be followed by an `else` keyword and another block. 229 | 230 | * `loop` 231 | 232 | Reruns the code within the block until a `break` or `return` statement. 233 | 234 | You can go to the top of the loop using `continue`. 235 | 236 | * `return` 237 | 238 | Optionally return a value to the calling function. 239 | 240 | * `switch` 241 | 242 | Jumps to the case matching the value switched on. 243 | 244 | Default case is the first piece of code within the following block, no label needed. 245 | 246 | If control flow reaches the end of the switch block it falls through. 247 | 248 | * `unreachable` 249 | 250 | Assumes the statement is in dead code, a hint for compiler optimization 251 | 252 | * Expression statements 253 | 254 | Any expression above (but only in-place and call expressions are useful) 255 | 256 | ## Keywords 257 | Keywords can never be used as identifiers 258 | * `break` 259 | * `case` 260 | * `comptime` 261 | * `continue` 262 | * `else` 263 | * `endcase` 264 | * `enum` 265 | * `fn` 266 | * `if` 267 | * `import` 268 | * `loop` 269 | * `return` 270 | * `switch` 271 | * `undefined` 272 | * `unreachable` 273 | * `zeroes` 274 | -------------------------------------------------------------------------------- /bootstrap/io.h: -------------------------------------------------------------------------------- 1 | u64 write_generic(u64 value, int section, int len) { 2 | u64 section_offset = current_section_bytes[section]; 3 | 4 | if(section != SECTION_BSS) { 5 | *(u64*)¤t_section_buf[section][section_offset] = value; 6 | } 7 | 8 | current_section_bytes[section] += len; 9 | 10 | return section_base_addr[section] + section_offset; 11 | } 12 | 13 | u64 write8(u8 value, int section) { 14 | return write_generic(value, section, 1); 15 | } 16 | 17 | u64 write16(u16 value, int section) { 18 | return write_generic(value, section, 2); 19 | } 20 | 21 | u64 write32(u32 value, int section) { 22 | return write_generic(value, section, 4); 23 | } 24 | 25 | u64 write64(u64 value, int section) { 26 | return write_generic(value, section, 8); 27 | } 28 | 29 | int current_reading_file = 0; 30 | 31 | char buffer_chr; 32 | char next_chr; 33 | 34 | int has_buffer = 0; 35 | int has_next_buffer = 0; 36 | 37 | int switch_file(char const *filename) { 38 | assert(!has_buffer); 39 | 40 | int f = current_reading_file; 41 | current_reading_file = open(filename, O_RDONLY); 42 | if(current_reading_file < 0) { 43 | printf("Cannot open imported filename '%s': %d!", filename, current_reading_file); 44 | exit(1); 45 | } 46 | return f; 47 | } 48 | 49 | void restore_file(int old_value) { 50 | current_reading_file = old_value; 51 | if(has_buffer) { 52 | assert(buffer_chr == 0); 53 | has_buffer = 0; 54 | } 55 | } 56 | 57 | u8 peek() { 58 | if(!has_buffer) { 59 | if(!has_next_buffer) { 60 | if(read(current_reading_file, &buffer_chr, 1) != 1) 61 | buffer_chr = 0; 62 | } else { 63 | buffer_chr = next_chr; 64 | has_next_buffer = 0; 65 | has_buffer = 1; 66 | } 67 | 68 | has_buffer = 1; 69 | } 70 | //printf("peek() = '%c'\n", buffer_chr); 71 | return buffer_chr; 72 | } 73 | 74 | u8 peek_next() { 75 | peek(); 76 | if(!has_next_buffer) { 77 | if(read(current_reading_file, &next_chr, 1) != 1) 78 | next_chr = 0; 79 | has_next_buffer = 1; 80 | } 81 | //printf("peek_next() = '%c'\n", next_chr); 82 | return next_chr; 83 | } 84 | 85 | u8 consume() { 86 | u8 result = peek(); 87 | has_buffer = 0; 88 | printf("consume() = '%c'\n", result); 89 | return result; 90 | } 91 | -------------------------------------------------------------------------------- /bootstrap/sections.h: -------------------------------------------------------------------------------- 1 | #define MAX_SECTION_BYTES 0x100000 2 | 3 | #define SECTION_TEXT 0 4 | #define SECTION_DATA 1 5 | #define SECTION_RODATA 2 6 | #define SECTION_BSS 3 7 | #define LAST_SECTION SECTION_BSS 8 | 9 | u64 section_base_addr[4] = {}; 10 | u64 current_section_bytes[4] = {}; 11 | u8 current_section_buf[3][MAX_SECTION_BYTES] = {}; 12 | -------------------------------------------------------------------------------- /bootstrap/trie.h: -------------------------------------------------------------------------------- 1 | struct trie_node_value { 2 | int type; 3 | int attribute; 4 | 5 | union { 6 | struct { 7 | u64 value; 8 | }; 9 | 10 | struct { 11 | int file_scope_trie; 12 | }; 13 | }; 14 | }; 15 | 16 | #define MAX_TRIE_NODES 0x10000 17 | 18 | struct trie_node { 19 | int next[0x100]; 20 | struct trie_node_value value; 21 | }; 22 | 23 | struct trie_node trie_node_storage[MAX_TRIE_NODES] = {}; 24 | int last_node_alloced = 0; 25 | int current_file_root = 0; 26 | 27 | enum { 28 | // Nothing on that node 29 | TRIE_TYPE_NONE = 0, 30 | 31 | // Has a compile time value 32 | TRIE_TYPE_COMPTIME, 33 | 34 | // Points to another trie node with the scope for the imported file 35 | TRIE_TYPE_FILE_SCOPE, 36 | 37 | // For member lookups, acts just like FILE_SCOPE, but is not 38 | // valid for any other purpose. 39 | TRIE_TYPE_FILE_DUP, 40 | 41 | // File that has not finished analyzing 42 | TRIE_TYPE_FILE_UNANALYZED, 43 | 44 | // Function offset 45 | TRIE_TYPE_FUNCTION_OFFSET, 46 | 47 | // Global variable 48 | TRIE_TYPE_GLOBAL_VARIABLE, 49 | 50 | // Local variable in function, stack pointer relative offset 51 | TRIE_TYPE_FUNCTION_LOCAL, 52 | 53 | // Local buffer in function, stack pointer relative offset, attribute is size 54 | TRIE_TYPE_FUNCTION_LOCAL_BUFFER, 55 | 56 | // Global buffer, attribute is size 57 | TRIE_TYPE_GLOBAL_BUFFER, 58 | 59 | // Builtin function 60 | TRIE_TYPE_BUILTIN_FUNCTION, 61 | }; 62 | 63 | int alloc_trie_node(void) { 64 | return ++last_node_alloced; 65 | } 66 | 67 | int lookup_node(char const *name, int current_trie_node) { 68 | if(!*name) { 69 | return current_trie_node; 70 | } 71 | 72 | struct trie_node *curr = &trie_node_storage[current_trie_node]; 73 | if(!curr->next[(unsigned char)*name]) { 74 | curr->next[(unsigned char)*name] = alloc_trie_node(); 75 | } 76 | 77 | return lookup_node(name + 1, curr->next[(unsigned char)*name]); 78 | } 79 | 80 | void add_shorthand(int scope, char shorthand, int target) { 81 | trie_node_storage[scope].next[(unsigned char)shorthand] = target; 82 | } 83 | 84 | struct trie_node_value *get_or_create_node_value(char const *name, int root) { 85 | return &trie_node_storage[lookup_node(name, root)].value; 86 | } 87 | -------------------------------------------------------------------------------- /meta/cloc_definition.txt: -------------------------------------------------------------------------------- 1 | Comp 2 | filter remove_matches ^\s*// 3 | extension co 4 | 3rd_gen_scale 1.8 5 | -------------------------------------------------------------------------------- /src/builtins.co: -------------------------------------------------------------------------------- 1 | import "src/codegen.co" codegen; 2 | import "src/identifier_types.co" itypes; 3 | import "src/identifiers.co" idents; 4 | import "src/tokenizer.co" tokenizer; 5 | 6 | enum { 7 | eval_comptime, 8 | eval_codegen, 9 | }; 10 | 11 | zeroes builtin_root[8]; 12 | 13 | fn builtin_size_of() [node] { 14 | node = codegen.current_file_root[0]; 15 | node = tokenizer.expect_token(tokenizer.identifier , "Expected identifier after `@size_of(`", node); 16 | 17 | tokenizer.discard(); 18 | 19 | switch(idents.node_get_type(node)) { 20 | tokenizer.error("Invalid identifier for `@size_of()`!"); 21 | 22 | case itypes.global_variable: 23 | case itypes.local_variable: 24 | return 8; 25 | 26 | case itypes.global_buffer: 27 | case itypes.local_buffer: 28 | return idents.node_get_attribute(node); 29 | } 30 | } 31 | 32 | fn builtin_embed() { 33 | @todo("builtin embed"); 34 | } 35 | 36 | fn add_builtin(name, fptr, type) [node] { 37 | node = builtin_root[0]; 38 | node = idents.lookup(name, node); 39 | 40 | @assert(idents.node_get_type(node) == itypes.none); 41 | idents.node_set_type(node, type); 42 | idents.node_set_value(node, fptr); 43 | } 44 | 45 | fn create_builtin_node() [val] { 46 | // Create the node itself 47 | builtin_root[0] = idents.alloc(); 48 | 49 | val = builtin_root[0]; 50 | 51 | add_builtin("size_of", builtin_size_of, itypes.builtin_function_comptime); 52 | add_builtin("embed", builtin_embed, itypes.builtin_function_comptime); 53 | } 54 | -------------------------------------------------------------------------------- /src/codegen.co: -------------------------------------------------------------------------------- 1 | import "src/writer.co" writer; 2 | import "src/tokenizer.co" tokenizer; 3 | import "src/identifier_types.co" itypes; 4 | import "src/identifiers.co" idents; 5 | 6 | zeroes target_arch[8]; 7 | zeroes target_os[8]; 8 | 9 | comptime target_arch_x86_64 = 0; 10 | comptime target_arch_aarch64 = 1; 11 | 12 | comptime target_os_linux = 0; 13 | comptime target_os_florence = 1; 14 | 15 | zeroes current_file_root[8]; 16 | 17 | fn peek_type() [node] { 18 | node = current_file_root[0]; 19 | tokenizer.peek_type(node); 20 | } 21 | 22 | fn peek_value() [node] { 23 | node = current_file_root[0]; 24 | tokenizer.peek_value(node); 25 | } 26 | 27 | fn discard() [node] { 28 | node = current_file_root[0]; 29 | tokenizer.discard(node); 30 | } 31 | 32 | fn expect_token(tok, str) [node] { 33 | node = current_file_root[0]; 34 | tokenizer.expect_token(tok, str, node); 35 | } 36 | 37 | zeroes continue_addr[8]; 38 | 39 | zeroes break_addr[8]; 40 | zeroes break_was_used[8]; 41 | 42 | zeroes switch_table[8]; // offset into rodata 43 | zeroes switch_default_addr[8]; 44 | zeroes endcase_addr[8]; 45 | zeroes endcase_was_used[8]; 46 | zeroes return_was_used[8]; 47 | 48 | 49 | comptime rewind_buffer_local_size = 0 50 | + (@size_of(continue_addr) 51 | + (@size_of(break_addr) 52 | + (@size_of(break_was_used) 53 | + (@size_of(switch_table) 54 | + (@size_of(switch_default_addr) 55 | + (@size_of(endcase_addr) 56 | + (@size_of(endcase_was_used) 57 | ))))))); 58 | 59 | comptime rewind_buffer_size = rewind_buffer_local_size + (8 * 2); 60 | 61 | fn rewind_save(rewind_buffer) { 62 | @memcpy(rewind_buffer, continue_addr, rewind_buffer_local_size); 63 | rewind_buffer[rewind_buffer_local_size + (8 * 0)] = writer.section_offset(writer.text); 64 | rewind_buffer[rewind_buffer_local_size + (8 * 1)] = writer.section_offset(writer.rodata); 65 | } 66 | 67 | fn rewind_restore(rewind_buffer) [ptr, len] { 68 | @memcpy(continue_addr, rewind_buffer, rewind_buffer_local_size); 69 | 70 | ptr = writer.current_section_buf[writer.text * 8]; 71 | len = writer.section_offset(writer.text) - rewind_buffer[rewind_buffer_local_size + (8 * 0)]; 72 | @memset(ptr, 0, len); 73 | 74 | writer.current_section_bytes[writer.text * 8] = rewind_buffer[rewind_buffer_local_size + (8 * 0)]; 75 | 76 | ptr = writer.current_section_buf[writer.rodata * 8]; 77 | len = writer.section_offset(writer.rodata) - rewind_buffer[rewind_buffer_local_size + (8 * 1)]; 78 | @memset(ptr, 0, len); 79 | 80 | writer.current_section_bytes[writer.rodata * 8] = rewind_buffer[rewind_buffer_local_size + (8 * 1)]; 81 | } 82 | 83 | zeroes expr_returns[8]; 84 | 85 | fn itype_is_in_reg(itype) { 86 | if(itype == itypes.runtime_reference) { 87 | return 1; 88 | } else { } 89 | if(itype == itypes.runtime_int) { 90 | return 1; 91 | } else { } 92 | return 0; 93 | } 94 | 95 | fn itype_is_mem_deref(itype) { 96 | switch(itype) { 97 | return 0; 98 | case itypes.local_variable: 99 | case itypes.local_buffer: 100 | case itypes.global_variable: 101 | case itypes.runtime_reference: 102 | return 1; 103 | } 104 | } 105 | 106 | fn lhs_not_saved(lhs_type, rhs_type) { 107 | if(itype_is_in_reg(lhs_type)) { 108 | if(itype_is_mem_deref(lhs_type)) { 109 | if(itype_is_mem_deref(rhs_type)) { 110 | // Both are memrefs, we can't use 111 | // mov [rax], [rbp - offset] 112 | // so we might as well make it 113 | // push rax 114 | // mov rax, [rbp - offset] 115 | // pop rcx 116 | // mov [rcx], rax 117 | // (until we have proper reg allocation) 118 | return 0; 119 | } else { } 120 | } else { } 121 | 122 | // We should rewind the save if the rhs never clobbered 123 | return itype_is_in_reg(rhs_type) == 0; 124 | } else { 125 | return 0; 126 | } 127 | } 128 | 129 | zeroes add_symbol_ptr[8]; 130 | 131 | fn add_symbol(num_args, local_var_space, total_stack_space) { 132 | if(add_symbol_ptr[0]) { 133 | @call(add_symbol_ptr[0], num_args); 134 | } else { 135 | tokenizer.error("add_symbol not implemented!"); 136 | } 137 | } 138 | 139 | zeroes function_prologue_ptr[8]; 140 | 141 | fn function_prologue(num_args, local_var_space, total_stack_space) { 142 | if(function_prologue_ptr[0]) { 143 | @call(function_prologue_ptr[0], num_args); 144 | } else { 145 | tokenizer.error("function_prologue not implemented!"); 146 | } 147 | } 148 | 149 | zeroes return_evaluated_ptr[8]; 150 | 151 | fn return_evaluated() { 152 | if(return_evaluated_ptr[0]) { 153 | @call(return_evaluated_ptr[0]); 154 | } else { 155 | tokenizer.error("return_evaluated not implemented!"); 156 | } 157 | } 158 | 159 | zeroes jmp_to_ptr[8]; 160 | 161 | fn jmp_to(addr) { 162 | if(jmp_to_ptr[0]) { 163 | @call(jmp_to_ptr[0], addr); 164 | } else { 165 | tokenizer.error("jmp_to not implemented!"); 166 | } 167 | } 168 | 169 | zeroes do_call_addr_ptr[8]; 170 | 171 | fn do_call_addr(addr) { 172 | if(do_call_addr_ptr[0]) { 173 | @call(do_call_addr_ptr[0], addr); 174 | } else { 175 | tokenizer.error("do_call_addr not implemented!"); 176 | } 177 | } 178 | 179 | zeroes make_switch_ptr[8]; 180 | 181 | fn make_switch() { 182 | if(make_switch_ptr[0]) { 183 | @call(make_switch_ptr[0]); 184 | } else { 185 | tokenizer.error("make_switch not implemented!"); 186 | } 187 | } 188 | 189 | zeroes endcase_to_here_ptr[8]; 190 | 191 | fn endcase_to_here(endcase_jmp_addr) { 192 | if(endcase_to_here_ptr[0]) { 193 | @call(endcase_to_here_ptr[0], endcase_jmp_addr); 194 | } else { 195 | tokenizer.error("endcase_to_here not implemented!"); 196 | } 197 | } 198 | 199 | zeroes make_loop_break_ptr[8]; 200 | 201 | fn make_loop_break() { 202 | if(make_loop_break_ptr[0]) { 203 | @call(make_loop_break_ptr[0]); 204 | } else { 205 | tokenizer.error("make_loop_break not implemented!"); 206 | } 207 | } 208 | 209 | zeroes loop_break_to_here_ptr[8]; 210 | 211 | fn loop_break_to_here(loop_break) { 212 | if(loop_break_to_here_ptr[0]) { 213 | @call(loop_break_to_here_ptr[0], loop_break); 214 | } else { 215 | tokenizer.error("loop_break_to_here not implemented!"); 216 | } 217 | } 218 | 219 | zeroes if_condition_ptr[8]; 220 | 221 | fn if_condition() { 222 | if(if_condition_ptr[0]) { 223 | @call(if_condition_ptr[0]); 224 | } else { 225 | tokenizer.error("if_condition not implemented!"); 226 | } 227 | } 228 | 229 | zeroes else_block_start_ptr[8]; 230 | 231 | fn else_block_start() { 232 | if(else_block_start_ptr[0]) { 233 | @call(else_block_start_ptr[0]); 234 | } else { 235 | tokenizer.error("else_block_start not implemented!"); 236 | } 237 | } 238 | 239 | zeroes else_block_end_ptr[8]; 240 | 241 | fn else_block_end() { 242 | if(else_block_end_ptr[0]) { 243 | @call(else_block_end_ptr[0]); 244 | } else { 245 | tokenizer.error("else_block_end not implemented!"); 246 | } 247 | } 248 | 249 | zeroes no_else_block_ptr[8]; 250 | 251 | fn no_else_block() { 252 | if(no_else_block_ptr[0]) { 253 | @call(no_else_block_ptr[0]); 254 | } else { 255 | tokenizer.error("no_else_block not implemented!"); 256 | } 257 | } 258 | 259 | zeroes save_eval_value_ptr[8]; 260 | 261 | fn save_eval_value() { 262 | if(save_eval_value_ptr[0]) { 263 | @call(save_eval_value_ptr[0]); 264 | } else { 265 | tokenizer.error("save_eval_value not implemented!"); 266 | } 267 | } 268 | 269 | zeroes add_elf_entry_point_ptr[8]; 270 | 271 | fn add_elf_entry_point(entry_point) { 272 | if(add_elf_entry_point_ptr[0]) { 273 | @call(add_elf_entry_point_ptr[0], entry_point); 274 | } else { 275 | tokenizer.error("add_elf_entry_point not implemented!"); 276 | } 277 | } 278 | 279 | zeroes do_inplace_op_ptr[8]; 280 | 281 | fn do_inplace_op(lhs_type, lhs_value, op, rhs_type, rhs_value) { 282 | if(do_inplace_op_ptr[0]) { 283 | @call(do_inplace_op_ptr[0], lhs_type, lhs_value, op, rhs_type, rhs_value); 284 | } else { 285 | tokenizer.error("do_inplace_op not implemented!"); 286 | } 287 | } 288 | 289 | zeroes do_binary_op_ptr[8]; 290 | 291 | fn do_binary_op(lhs_type, lhs_value, op, rhs_type, rhs_value) { 292 | if(do_binary_op_ptr[0]) { 293 | @call(do_binary_op_ptr[0], lhs_type, lhs_value, op, rhs_type, rhs_value); 294 | } else { 295 | tokenizer.error("do_binary_op not implemented!"); 296 | } 297 | } 298 | 299 | zeroes do_unary_op_ptr[8]; 300 | 301 | fn do_unary_op(op, operand_type, operand_value) { 302 | if(do_unary_op_ptr[0]) { 303 | @call(do_unary_op_ptr[0], op, operand_type, operand_value); 304 | } else { 305 | tokenizer.error("do_unary_op not implemented!"); 306 | } 307 | } 308 | 309 | fn collapse_lhs(ident) { 310 | switch(peek_type()) { 311 | case tokenizer.dot: 312 | if(idents.node_get_type(ident) == itypes.variable_scope) { 313 | discard(); 314 | } else { 315 | tokenizer.error("Expected a variable scope before `.`"); 316 | unreachable; 317 | } 318 | } 319 | } 320 | 321 | zeroes eval_type[8]; 322 | // Only if comptime int 323 | zeroes eval_value[8]; 324 | 325 | comptime primary_expr_only = 0; 326 | comptime any_unambigous_expr = 1; 327 | 328 | fn eval_inplace_op(lhs_type, lhs_value, op, rhs_type, rhs_value) { 329 | switch(lhs_type) { 330 | tokenizer.error("Invalid lhs for inplace op"); 331 | 332 | case itypes.global_variable: 333 | case itypes.local_variable: 334 | case itypes.runtime_reference: 335 | do_inplace_op(lhs_type, lhs_value, op, rhs_type, rhs_value); 336 | return; 337 | } 338 | } 339 | 340 | fn eval_binary_op(lhs_type, lhs_value, op, rhs_type, rhs_value) { 341 | eval_type[0] = itypes.runtime_int; 342 | 343 | loop { // loop only so that we can break it 344 | if(lhs_type == itypes.comptime_int) { 345 | if(rhs_type == itypes.comptime_int) { 346 | eval_type[0] = itypes.comptime_int; 347 | 348 | switch(op) { 349 | @todo("eval_binary_op comptime eval default op"); 350 | 351 | case tokenizer.less_than: 352 | eval_value[0] = lhs_value < rhs_value; 353 | return; 354 | 355 | case tokenizer.greater_than: 356 | eval_value[0] = lhs_value > rhs_value; 357 | return; 358 | 359 | case tokenizer.less_than_equal: 360 | eval_value[0] = lhs_value <= rhs_value; 361 | return; 362 | 363 | case tokenizer.greater_than_equal: 364 | eval_value[0] = lhs_value >= rhs_value; 365 | return; 366 | 367 | case tokenizer.bitand: 368 | eval_value[0] = lhs_value & rhs_value; 369 | return; 370 | 371 | case tokenizer.bitor: 372 | eval_value[0] = lhs_value | rhs_value; 373 | return; 374 | 375 | case tokenizer.bitxor: 376 | eval_value[0] = lhs_value ^ rhs_value; 377 | return; 378 | 379 | case tokenizer.addition: 380 | eval_value[0] = lhs_value + rhs_value; 381 | return; 382 | 383 | case tokenizer.subtraction: 384 | eval_value[0] = lhs_value - rhs_value; 385 | return; 386 | 387 | case tokenizer.multiplication: 388 | eval_value[0] = lhs_value * rhs_value; 389 | return; 390 | 391 | case tokenizer.modulus: 392 | eval_value[0] = lhs_value % rhs_value; 393 | return; 394 | 395 | case tokenizer.division: 396 | eval_value[0] = lhs_value / rhs_value; 397 | return; 398 | } 399 | } else { 400 | // Operand inversion can help with eliminating comptime_ints from 401 | // the lhs, which allows the backend to generate better code 402 | switch(op) { 403 | @todo("do_binary_op invert default"); 404 | 405 | case tokenizer.less_than: 406 | op = tokenizer.greater_than; 407 | endcase; 408 | 409 | case tokenizer.less_than_equal: 410 | op = tokenizer.greater_than_equal; 411 | endcase; 412 | 413 | case tokenizer.greater_than: 414 | op = tokenizer.less_than; 415 | endcase; 416 | 417 | case tokenizer.greater_than_equal: 418 | op = tokenizer.less_than_equal; 419 | endcase; 420 | 421 | case tokenizer.subtraction: 422 | // First do the inversion 423 | do_binary_op(rhs_type, rhs_value, op, lhs_type, lhs_value); 424 | 425 | // Then negate the value 426 | do_unary_op(tokenizer.subtraction, itypes.runtime_int); 427 | return; 428 | 429 | // Cannot be inverted 430 | case tokenizer.shift_right: 431 | case tokenizer.shift_left: 432 | case tokenizer.modulus: 433 | case tokenizer.division: 434 | break; 435 | 436 | // Commutative, keep operator same 437 | case tokenizer.addition: 438 | case tokenizer.multiplication: 439 | case tokenizer.bitand: 440 | case tokenizer.bitor: 441 | case tokenizer.bitxor: 442 | case tokenizer.equals: 443 | case tokenizer.not_equals: 444 | // endcase; 445 | } 446 | return do_binary_op(rhs_type, rhs_value, op, lhs_type, lhs_value); 447 | } 448 | } else { 449 | switch(rhs_type) { 450 | endcase; 451 | 452 | case itypes.comptime_int: 453 | switch(lhs_type) { 454 | endcase; 455 | 456 | case itypes.local_buffer_addr: 457 | case itypes.global_buffer_addr: 458 | case itypes.function_addr: 459 | eval_type[0] = lhs_type; 460 | eval_value[0] = lhs_value; 461 | 462 | switch(op) { 463 | endcase; 464 | 465 | case tokenizer.addition: 466 | eval_value[0] += rhs_value; 467 | return; 468 | 469 | case tokenizer.subtraction: 470 | eval_value[0] -= rhs_value; 471 | return; 472 | 473 | case tokenizer.bitor: 474 | case tokenizer.bitand: 475 | case tokenizer.bitxor: 476 | case tokenizer.multiplication: 477 | case tokenizer.division: 478 | case tokenizer.modulus: 479 | case tokenizer.bitnot: 480 | // @TODO: Do these at comptime if PIE disabled 481 | } 482 | } 483 | // endcase; 484 | } 485 | } 486 | } 487 | return do_binary_op(lhs_type, lhs_value, op, rhs_type, rhs_value); 488 | } 489 | 490 | fn eval_unary_op(op, operand_type, operand_value) { 491 | if(operand_type == itypes.comptime_int) { 492 | eval_type[0] = itypes.comptime_int; 493 | switch(op) { 494 | @todo("eval_unary_op comptime_int default op"); 495 | 496 | case tokenizer.subtraction: // Unary arithmetic negation 497 | eval_value[0] = -operand_value; 498 | return; 499 | 500 | case tokenizer.bitnot: // Unary bitwise negation 501 | eval_value[0] = ~operand_value; 502 | return; 503 | } 504 | } else { 505 | do_unary_op(op, operand_type, operand_value); 506 | eval_type[0] = itypes.runtime_int; 507 | return; 508 | } 509 | } 510 | 511 | fn eval_expr(expr_mode) [lhs_type, lhs_value, lhs_attr, op, rhs_type, rhs_value, rewind_buffer[rewind_buffer_size]] { 512 | op = peek_type(); 513 | 514 | switch(op) { 515 | tokenizer.error("Expected primary expression"); 516 | endcase; 517 | 518 | case tokenizer.bitnot: 519 | case tokenizer.subtraction: 520 | discard(); 521 | eval_expr(primary_expr_only); 522 | 523 | lhs_type = eval_type[0]; 524 | lhs_value = eval_value[0]; 525 | 526 | eval_unary_op(op, lhs_type, lhs_value); 527 | 528 | lhs_type = eval_type[0]; 529 | lhs_value = eval_value[0]; 530 | endcase; 531 | 532 | case tokenizer.open_paren: 533 | discard(); 534 | eval_expr(any_unambigous_expr); 535 | 536 | lhs_type = eval_type[0]; 537 | lhs_value = eval_value[0]; 538 | 539 | expect_token(tokenizer.closing_paren, "Expected `)` after expression"); 540 | discard(); 541 | endcase; 542 | 543 | case tokenizer.int_literal: 544 | lhs_value = peek_value(); 545 | discard(); 546 | lhs_type = itypes.comptime_int; 547 | endcase; 548 | 549 | case tokenizer.string_literal: 550 | // Length, include null terminator 551 | lhs_value = peek_value() + 1; 552 | 553 | lhs_value = writer.intern_string(tokenizer.buffer, lhs_value, writer.rodata); 554 | lhs_type = itypes.global_buffer_addr; 555 | discard(); 556 | 557 | endcase; 558 | 559 | case tokenizer.identifier: 560 | lhs_value = peek_value(); 561 | lhs_type = idents.node_get_type(lhs_value); 562 | lhs_attr = idents.node_get_attribute(lhs_value); 563 | 564 | discard(); 565 | 566 | if(lhs_type == itypes.variable_scope) { 567 | loop { 568 | lhs_value = idents.node_get_attribute(lhs_value); 569 | if(peek_type() == tokenizer.dot) { 570 | discard(); 571 | 572 | lhs_value = tokenizer.expect_token(tokenizer.identifier, "Expected identifier after `.`", lhs_value); 573 | discard(); 574 | 575 | lhs_type = idents.node_get_type(lhs_value); 576 | lhs_attr = idents.node_get_attribute(lhs_value); 577 | 578 | switch(lhs_type) { 579 | break; 580 | case itypes.none: 581 | tokenizer.error("Unknown identifier!"); 582 | case itypes.variable_scope: 583 | continue; 584 | } 585 | } else { 586 | break; 587 | } 588 | } 589 | } else { } 590 | 591 | switch(lhs_type) { 592 | lhs_value = idents.node_get_value(lhs_value); 593 | endcase; 594 | 595 | case itypes.none: 596 | tokenizer.error("Unknown identifier!"); 597 | 598 | case itypes.local_buffer: 599 | lhs_type = itypes.local_buffer_addr; 600 | lhs_value = idents.node_get_value(lhs_value); 601 | endcase; 602 | 603 | case itypes.global_buffer: 604 | lhs_type = itypes.global_buffer_addr; 605 | lhs_value = idents.node_get_value(lhs_value); 606 | endcase; 607 | // endcase; 608 | } 609 | 610 | switch(peek_type()) { 611 | endcase; 612 | 613 | case tokenizer.open_square_bracket: 614 | discard(); 615 | 616 | eval_expr(any_unambigous_expr); 617 | 618 | switch(eval_type[0]) { 619 | // Runtime index. Darn. 620 | rhs_type = eval_type[0]; 621 | rhs_value = eval_value[0]; 622 | do_binary_op(lhs_type, lhs_value, tokenizer.addition, rhs_type, rhs_value); 623 | lhs_type = itypes.runtime_reference; 624 | endcase; 625 | 626 | case itypes.comptime_int: 627 | switch(lhs_type) { 628 | case itypes.runtime_int: 629 | case itypes.local_variable: 630 | case itypes.global_variable: 631 | rhs_value = eval_value[0]; 632 | do_binary_op(lhs_type, lhs_value, tokenizer.addition, itypes.comptime_int, rhs_value); 633 | lhs_type = itypes.runtime_reference; 634 | endcase; 635 | 636 | case itypes.local_buffer_addr: 637 | lhs_type = itypes.local_variable; 638 | lhs_value += eval_value[0]; 639 | endcase; 640 | 641 | case itypes.global_buffer_addr: 642 | lhs_type = itypes.global_variable; 643 | lhs_value += eval_value[0]; 644 | //endcase; 645 | } 646 | // endcase; 647 | } 648 | 649 | tokenizer.expect_token(tokenizer.closing_square_bracket, "Expected `]` after subscript index"); 650 | tokenizer.discard(); 651 | endcase; 652 | 653 | case tokenizer.open_paren: 654 | discard(); 655 | 656 | switch(lhs_type) { 657 | @todo("call ident default case"); 658 | 659 | case itypes.local_variable: 660 | @todo("local_variable call"); 661 | 662 | case itypes.local_buffer_addr: 663 | @todo("local_buffer_addr call"); 664 | 665 | case itypes.global_variable: 666 | @todo("global_variable call"); 667 | 668 | case itypes.global_buffer_addr: 669 | @todo("global_buffer_addr call"); 670 | 671 | case itypes.function_addr: 672 | expr_returns[0] &= lhs_attr; // Expression returns if the function does 673 | case itypes.comptime_int: 674 | do_call_addr(lhs_value); 675 | lhs_type = itypes.runtime_int; 676 | endcase; 677 | 678 | case itypes.builtin_function_codegen: 679 | @call(lhs_value); 680 | 681 | lhs_value = 0x41414141; 682 | lhs_type = itypes.runtime_int; 683 | endcase; 684 | 685 | case itypes.builtin_function_comptime: 686 | lhs_value = @call(lhs_value); 687 | lhs_type = itypes.comptime_int; 688 | // endcase; 689 | } 690 | 691 | tokenizer.expect_token(tokenizer.closing_paren, "Expected ')' after argument list"); 692 | tokenizer.discard(); 693 | endcase; 694 | } 695 | } 696 | 697 | eval_type[0] = lhs_type; 698 | eval_value[0] = lhs_value; 699 | 700 | if(expr_mode == primary_expr_only) { 701 | return; 702 | } else { 703 | op = peek_type(); 704 | switch(op) { 705 | @todo("eval_expr default op"); 706 | 707 | case tokenizer.closing_paren: 708 | case tokenizer.closing_curly_brace: 709 | case tokenizer.closing_square_bracket: 710 | case tokenizer.end_of_file: 711 | case tokenizer.comma: 712 | case tokenizer.semicolon: 713 | case tokenizer.colon: 714 | case tokenizer.dot_dot_dot: 715 | return; 716 | 717 | case tokenizer.assignment: 718 | case tokenizer.addition_inplace: 719 | case tokenizer.subtraction_inplace: 720 | case tokenizer.bitand_inplace: 721 | case tokenizer.bitor_inplace: 722 | case tokenizer.bitxor_inplace: 723 | case tokenizer.shift_right_inplace: 724 | case tokenizer.shift_left_inplace: 725 | case tokenizer.multiplication_inplace: 726 | case tokenizer.modulus_inplace: 727 | case tokenizer.division_inplace: 728 | if(expr_returns[0]) { 729 | } else { 730 | tokenizer.error("Left hand side of expression doesn't return!"); 731 | } 732 | 733 | discard(); 734 | 735 | if(itype_is_in_reg(lhs_type)) { 736 | rewind_save(rewind_buffer); 737 | save_eval_value(); 738 | } else { } 739 | 740 | eval_expr(any_unambigous_expr); 741 | rhs_type = eval_type[0]; 742 | rhs_value = eval_value[0]; 743 | 744 | if(lhs_not_saved(lhs_type, rhs_type)) { 745 | rewind_restore(rewind_buffer); 746 | } else { } 747 | 748 | eval_inplace_op(lhs_type, lhs_value, op, rhs_type, rhs_value); 749 | eval_type[0] = itypes.runtime_int; // TODO: Set undefined type here when available 750 | 751 | if(expr_returns[0]) { 752 | } else { 753 | tokenizer.error("Right hand side of expression doesn't return!"); 754 | } 755 | return; 756 | 757 | case tokenizer.less_than: 758 | case tokenizer.less_than_equal: 759 | case tokenizer.greater_than: 760 | case tokenizer.greater_than_equal: 761 | case tokenizer.equals: 762 | case tokenizer.not_equals: 763 | case tokenizer.bitor: 764 | case tokenizer.bitxor: 765 | case tokenizer.bitand: 766 | case tokenizer.division: 767 | case tokenizer.modulus: 768 | case tokenizer.multiplication: 769 | case tokenizer.addition: 770 | case tokenizer.subtraction: 771 | if(expr_returns[0]) { 772 | } else { 773 | tokenizer.error("Left hand side of expression doesn't return!"); 774 | } 775 | 776 | discard(); 777 | 778 | if(itype_is_in_reg(lhs_type)) { 779 | rewind_save(rewind_buffer); 780 | save_eval_value(); 781 | } else { } 782 | 783 | eval_expr(primary_expr_only); 784 | rhs_type = eval_type[0]; 785 | rhs_value = eval_value[0]; 786 | 787 | if(lhs_not_saved(lhs_type, rhs_type)) { 788 | rewind_restore(rewind_buffer); 789 | } else { } 790 | 791 | eval_binary_op(lhs_type, lhs_value, op, rhs_type, rhs_value); 792 | 793 | if(expr_returns[0]) { 794 | } else { 795 | tokenizer.error("Right hand side of expression doesn't return!"); 796 | } 797 | return; 798 | 799 | case tokenizer.question_mark: 800 | @todo("eval_expr ternary expr"); 801 | // discard(); 802 | // // Ternary operator 803 | // extra_type = lhs_type; 804 | // extra_value = lhs_value; 805 | 806 | // eval_expr(any_unambigous_expr); 807 | 808 | // lhs_type = eval_type[0]; 809 | // lhs_value = eval_value[0]; 810 | 811 | // expect_token(tokenizer.colon, "Expected `:` after expression"); 812 | // discard(); 813 | 814 | // eval_expr(any_unambigous_expr); 815 | 816 | // rhs_type = eval_type[0]; 817 | // rhs_value = eval_value[0]; 818 | 819 | // @todo("eval_expr ternary impl"); 820 | } 821 | } 822 | } 823 | 824 | fn eval_comptime_expr(mode) { 825 | expr_returns[0] = 1; 826 | eval_expr(mode); 827 | if(eval_type[0] == itypes.comptime_int) { 828 | return eval_value[0]; 829 | } else { 830 | tokenizer.error("Non-comptime expr eval!"); 831 | } 832 | } 833 | 834 | fn eval_returning_expr(mode) { 835 | expr_returns[0] = 1; 836 | eval_expr(mode); 837 | if(expr_returns[0]) { 838 | return; 839 | } else { 840 | tokenizer.error("Expression does not return!"); 841 | } 842 | } 843 | 844 | comptime max_switch_values = 0x80; 845 | comptime switch_table_entry_size = 4; 846 | comptime switch_table_size = switch_table_entry_size * max_switch_values; 847 | 848 | fn set_switch_case_here(case_value) [ptr, offset] { 849 | if(case_value >= max_switch_values) { 850 | printer.log_hex("Case value", case_value); 851 | tokenizer.error("Switch case value out of range!"); 852 | } else { 853 | offset = writer.code_addr() - switch_default_addr[0]; 854 | ptr = switch_table[0] + (case_value * switch_table_entry_size); 855 | writer.patch32(offset, writer.rodata, ptr); 856 | } 857 | } 858 | 859 | fn parse_block() [tmp, save0, save1, save2, can_be_exited] { 860 | expect_token(tokenizer.open_curly_brace, "Expected `{` at the start of block"); 861 | discard(); 862 | 863 | loop { 864 | switch(peek_type()) { 865 | // Anything else is probably an expression. 866 | expr_returns[0] = 1; 867 | eval_expr(any_unambigous_expr); 868 | 869 | expect_token(tokenizer.semicolon, "Expected `;` after expression"); 870 | discard(); 871 | 872 | switch(eval_type[0]) { 873 | // Discarding anything else is probably an error 874 | tokenizer.error("Can't discard this value!"); 875 | 876 | // These are fine to discard as they could be function return values 877 | // or in-place operations 878 | case itypes.runtime_int: 879 | 880 | } 881 | 882 | if(expr_returns[0]) { 883 | continue; 884 | } else { 885 | endcase; 886 | } 887 | 888 | case tokenizer.keyword_case: 889 | discard(); 890 | 891 | save0 = eval_comptime_expr(); 892 | if(peek_type() == tokenizer.dot_dot_dot) { 893 | discard(); 894 | save1 = eval_comptime_expr(); 895 | } else { 896 | save1 = save0; 897 | } 898 | 899 | loop { 900 | set_switch_case_here(save0); 901 | 902 | if(save0 == save1) { 903 | break; 904 | } else { 905 | save0 += 1; 906 | continue; 907 | } 908 | } 909 | 910 | expect_token(tokenizer.colon, "Expected `:` after case value"); 911 | discard(); 912 | 913 | continue; 914 | 915 | case tokenizer.keyword_break: 916 | discard(); 917 | expect_token(tokenizer.semicolon, "Expected `;` after `break`"); 918 | discard(); 919 | 920 | break_was_used[0] = 1; 921 | tmp = break_addr[0]; 922 | jmp_to(tmp); 923 | case tokenizer.keyword_unreachable: 924 | endcase; 925 | 926 | case tokenizer.keyword_loop: 927 | discard(); 928 | 929 | // Store away old values 930 | tmp = break_was_used[0]; 931 | break_was_used[0] = 0; 932 | 933 | save0 = break_addr[0]; 934 | save1 = continue_addr[0]; 935 | 936 | // Create new break fixup 937 | break_addr[0] = make_loop_break(); 938 | continue_addr[0] = writer.code_addr(); 939 | 940 | can_be_exited = parse_block(); 941 | 942 | expect_token(tokenizer.closing_curly_brace, "Expected `}` after loop body"); 943 | discard(); 944 | 945 | continue_addr[0] = save1; 946 | 947 | if(break_was_used[0]) { 948 | break_was_used[0] = tmp; // Restore old 949 | 950 | tmp = break_addr[0]; 951 | loop_break_to_here(tmp); 952 | 953 | break_addr[0] = save0; 954 | 955 | // The code after us is used! 956 | continue; 957 | } else { 958 | break_was_used[0] = tmp; // Restore old 959 | // Can we reach the code after this? 960 | 961 | break_addr[0] = save0; 962 | 963 | if(can_be_exited) { 964 | endcase; 965 | } else { 966 | continue; 967 | } 968 | } 969 | 970 | case tokenizer.keyword_if: 971 | discard(); 972 | expect_token(tokenizer.open_paren, "Expected `(` after `if`"); 973 | discard(); 974 | 975 | eval_returning_expr(any_unambigous_expr); 976 | 977 | expect_token(tokenizer.closing_paren, "Expected `)` after if conditional"); 978 | discard(); 979 | 980 | if(eval_type[0] == itypes.comptime_int) { 981 | @todo("comptime if"); 982 | } else { 983 | tmp = if_condition(); 984 | 985 | can_be_exited = parse_block(); 986 | 987 | expect_token(tokenizer.closing_curly_brace, "Expected `}` after if body"); 988 | discard(); 989 | 990 | if(peek_type() == tokenizer.keyword_else) { 991 | discard(); 992 | 993 | // Jump at the end of the taken branch to after else 994 | save0 = else_block_start(tmp, can_be_exited); 995 | 996 | save1 = parse_block(); 997 | 998 | expect_token(tokenizer.closing_curly_brace, "Expected `}` after else body"); 999 | discard(); 1000 | 1001 | // Else falls through 1002 | 1003 | // Patch end of taken jmp to here 1004 | if(can_be_exited) { 1005 | else_block_end(save0); 1006 | continue; 1007 | } else { 1008 | if(save1) { 1009 | continue; 1010 | } else { 1011 | endcase; 1012 | } 1013 | } 1014 | } else { 1015 | no_else_block(tmp); 1016 | continue; 1017 | } 1018 | } 1019 | 1020 | case tokenizer.keyword_switch: 1021 | discard(); 1022 | expect_token(tokenizer.open_paren, "Expected `(` after `switch`"); 1023 | discard(); 1024 | 1025 | eval_returning_expr(any_unambigous_expr); 1026 | 1027 | expect_token(tokenizer.closing_paren, "Expected `)` after if conditional"); 1028 | discard(); 1029 | 1030 | tmp = switch_table[0]; 1031 | save0 = endcase_was_used[0]; 1032 | save1 = endcase_addr[0]; 1033 | save2 = switch_default_addr[0]; 1034 | 1035 | endcase_was_used[0] = 0; 1036 | endcase_addr[0] = make_switch(); 1037 | switch_table[0] = writer.section_addr(writer.rodata); 1038 | switch_default_addr[0] = writer.code_addr(); 1039 | 1040 | writer.write_generic(0, writer.rodata, switch_table_size); 1041 | 1042 | can_be_exited = parse_block(); 1043 | 1044 | expect_token(tokenizer.closing_curly_brace, "Expected `}` after switch block"); 1045 | discard(); 1046 | 1047 | switch_table[0] = tmp; 1048 | switch_default_addr[0] = save2; 1049 | 1050 | tmp = endcase_addr[0]; 1051 | endcase_to_here(tmp); 1052 | endcase_addr[0] = save1; 1053 | 1054 | if(endcase_was_used[0] | can_be_exited) { 1055 | endcase_was_used[0] = save0; 1056 | continue; 1057 | } else { 1058 | endcase_was_used[0] = save0; 1059 | endcase; 1060 | } 1061 | 1062 | case tokenizer.keyword_endcase: 1063 | discard(); 1064 | expect_token(tokenizer.semicolon, "Expected `;` after `endcase`"); 1065 | discard(); 1066 | 1067 | endcase_was_used[0] = 1; 1068 | tmp = endcase_addr[0]; 1069 | jmp_to(tmp); 1070 | endcase; 1071 | 1072 | case tokenizer.keyword_continue: 1073 | discard(); 1074 | expect_token(tokenizer.semicolon, "Expected `;` after `continue`"); 1075 | discard(); 1076 | 1077 | tmp = continue_addr[0]; 1078 | jmp_to(tmp); 1079 | endcase; 1080 | 1081 | case tokenizer.keyword_return: 1082 | discard(); 1083 | if(peek_type() != tokenizer.semicolon) { 1084 | eval_returning_expr(any_unambigous_expr); 1085 | } else { 1086 | // This should make the backend not produce any code *crosses fingers* 1087 | eval_type[0] = itypes.runtime_int; 1088 | } 1089 | return_evaluated(); 1090 | expect_token(tokenizer.semicolon, "Expected `;` after `return` statment"); 1091 | discard(); 1092 | return_was_used[0] = 1; 1093 | endcase; 1094 | 1095 | case tokenizer.closing_curly_brace: 1096 | return 1; 1097 | } 1098 | 1099 | // Loop here util we have non-dead code (case comes around and enters here or we exit scope) 1100 | loop { 1101 | switch(peek_type()) { 1102 | tokenizer.error("Dead code"); 1103 | 1104 | case tokenizer.keyword_unreachable: 1105 | discard(); 1106 | expect_token(tokenizer.semicolon, "Expected `;` after `unreachable`"); 1107 | discard(); 1108 | continue; 1109 | 1110 | case tokenizer.keyword_case: 1111 | break; 1112 | 1113 | case tokenizer.closing_curly_brace: 1114 | return 0; 1115 | } 1116 | } 1117 | continue; 1118 | } 1119 | } 1120 | -------------------------------------------------------------------------------- /src/compiler.co: -------------------------------------------------------------------------------- 1 | import "src/builtins.co" builtins; 2 | import "src/codegen.co" codegen; 3 | import "src/identifier_types.co" itypes; 4 | import "src/identifiers.co" idents; 5 | import "src/printer.co" printer; 6 | import "src/source.co" source; 7 | import "src/syscalls.co" syscalls; 8 | import "src/tokenizer.co" tokenizer; 9 | import "src/writer.co" writer; 10 | 11 | zeroes import_file_root[8]; 12 | zeroes parse_top_level_ptr[8]; 13 | zeroes root_root[8]; 14 | 15 | fn root_ident(ident) [node] { 16 | node = root_root[0]; 17 | return idents.lookup(ident, node); 18 | } 19 | 20 | fn assert_fail_addr() [result] { 21 | result = root_ident("assert_fail"); 22 | @assert(idents.node_get_type(result) == itypes.function_addr); 23 | @assert(idents.node_get_attribute(result) == 0); // Shouldn't return 24 | return idents.node_get_value(result); 25 | } 26 | 27 | fn main_addr() [result] { 28 | result = root_ident("main"); 29 | @assert(idents.node_get_type(result) == itypes.function_addr); 30 | @assert(idents.node_get_attribute(result) == 0); // Shouldn't return 31 | return idents.node_get_value(result); 32 | } 33 | 34 | fn parse_comptime_decl() [value_node, value] { 35 | codegen.discard(); 36 | 37 | value_node = codegen.expect_token(tokenizer.identifier, "Expected identifier after `comptime`"); 38 | codegen.discard(); 39 | @assert(idents.node_get_type(value_node) == itypes.none); 40 | 41 | codegen.expect_token(tokenizer.assignment, "Expected `=` after identifier"); 42 | codegen.discard(); 43 | 44 | value = codegen.eval_comptime_expr(codegen.any_unambigous_expr); 45 | idents.node_set_value(value_node, value); 46 | idents.node_set_type(value_node, itypes.comptime_int); 47 | 48 | codegen.expect_token(tokenizer.semicolon, "Expected `;` after comptime declaration"); 49 | codegen.discard(); 50 | } 51 | 52 | fn parse_enum_decl() [next_value, node] { 53 | codegen.discard(); 54 | 55 | codegen.expect_token(tokenizer.open_curly_brace, "Expected `{` after `enum`"); 56 | codegen.discard(); 57 | 58 | next_value = 0; 59 | 60 | loop { 61 | if(codegen.peek_type() == tokenizer.closing_curly_brace) { 62 | codegen.discard(); 63 | codegen.expect_token(tokenizer.semicolon, "Expected `;` after enum declaration"); 64 | codegen.discard(); 65 | return; 66 | } else { 67 | node = codegen.expect_token(tokenizer.identifier, "Expected identifier or `}`"); 68 | codegen.discard(); 69 | 70 | @assert(idents.node_get_type(node) == itypes.none); 71 | 72 | if(codegen.peek_type() != tokenizer.comma) { 73 | codegen.expect_token(tokenizer.assignment, "Expected `=` or `,` after enum member identifier"); 74 | codegen.discard(); 75 | next_value = codegen.eval_comptime_expr(codegen.any_unambigous_expr); 76 | } else {} 77 | idents.node_set_type(node, itypes.comptime_int); 78 | idents.node_set_value(node, next_value); 79 | next_value += 1; 80 | 81 | codegen.expect_token(tokenizer.comma, "Expected `,` after enum member value"); 82 | codegen.discard(); 83 | continue; 84 | } 85 | } 86 | } 87 | 88 | fn set_source_file(file_path) [fd] { 89 | fd = syscalls.open(file_path, syscalls.O_RDONLY); 90 | source.switch_file(fd); 91 | } 92 | 93 | fn parse_additional_file(file_path, new_node) [fd, root_stash, file_context_stash[source.file_context_size], new_file_root, len] { 94 | // Store our current context, the imported file has no idea! 95 | root_stash = codegen.current_file_root[0]; 96 | source.stash_file_info(file_context_stash); 97 | 98 | len = strings.len(file_path) + 1; 99 | @memcpy(source.file_name, file_path, len); 100 | 101 | set_source_file(file_path); 102 | 103 | @call(parse_top_level_ptr[0]); 104 | 105 | new_file_root = codegen.current_file_root[0]; 106 | 107 | // Return to monke 108 | source.restore_file_info(file_context_stash); 109 | codegen.current_file_root[0] = root_stash; 110 | 111 | return new_file_root; 112 | } 113 | 114 | fn parse_file_if_needed(file_path) [node, new_file_root] { 115 | node = import_file_root[0]; 116 | node = idents.lookup(file_path, node); 117 | 118 | switch(idents.node_get_type(node)) { 119 | printer.print_string("Bad filename ident type!\n"); 120 | printer.exit(1); 121 | 122 | case itypes.none: // New file, parse first time 123 | idents.node_set_type(node, itypes.partially_parsed_filename); 124 | 125 | new_file_root = parse_additional_file(file_path, node); 126 | 127 | idents.node_set_type(node, itypes.fully_parsed_filename); 128 | idents.node_set_attribute(node, new_file_root); 129 | return new_file_root; 130 | 131 | case itypes.fully_parsed_filename: // Already parsed 132 | return idents.node_get_attribute(node); 133 | 134 | case itypes.partially_parsed_filename: 135 | printer.print_string("Circular import detected!\n"); 136 | printer.exit(1); 137 | } 138 | } 139 | 140 | fn parse_import() [file_root, ident_node] { 141 | codegen.discard(); 142 | 143 | codegen.expect_token(tokenizer.string_literal, "Expected a string literal after `import`."); 144 | codegen.discard(); 145 | file_root = parse_file_if_needed(tokenizer.buffer); 146 | 147 | codegen.expect_token(tokenizer.identifier, "Expected an identifier after filename."); 148 | ident_node = codegen.peek_value(); 149 | codegen.discard(); 150 | 151 | // Assert the identifier is unused 152 | @assert(idents.node_get_type(ident_node) == itypes.none); 153 | 154 | idents.node_set_type(ident_node, itypes.variable_scope); 155 | idents.node_set_attribute(ident_node, file_root); 156 | 157 | codegen.expect_token(tokenizer.semicolon, "Expected `;` after import"); 158 | codegen.discard(); 159 | } 160 | 161 | fn add_builtins_to_current_file() [node, builtin_root] { 162 | node = codegen.current_file_root[0]; 163 | builtin_root = builtins.builtin_root[0]; 164 | node = idents.node_addr(node); 165 | idents.trie_node_set_next(node, '@', builtin_root); 166 | } 167 | 168 | // Parse function argument and local variable list, creating identifiers 169 | // and storing the argument values in their stack slots 170 | fn read_stack_frame() [offset, num_args, local_var_space, ident, size] { 171 | offset = 0; 172 | num_args = 0; 173 | local_var_space = 0; 174 | 175 | if(codegen.peek_type() == tokenizer.open_paren) { 176 | codegen.discard(); 177 | // Parse argument list 178 | 179 | loop { 180 | if(codegen.peek_type() == tokenizer.closing_paren) { 181 | codegen.discard(); 182 | break; 183 | } else { 184 | ident = codegen.expect_token(tokenizer.identifier, "Expected identifier or `)`"); 185 | codegen.discard(); 186 | @assert(idents.node_get_type(ident) == itypes.none); 187 | 188 | idents.node_set_type(ident, itypes.local_variable); 189 | 190 | offset += 8; 191 | 192 | idents.node_set_value(ident, offset); 193 | 194 | num_args += 1; 195 | } 196 | 197 | if(codegen.peek_type() == tokenizer.comma) { 198 | codegen.discard(); 199 | } else {} 200 | continue; // @BUG: I need this for some reason. 201 | } 202 | } else {} 203 | 204 | if(codegen.peek_type() == tokenizer.open_square_bracket) { 205 | codegen.discard(); 206 | // Parse local var list 207 | loop { 208 | if(codegen.peek_type() == tokenizer.closing_square_bracket) { 209 | codegen.discard(); 210 | break; 211 | } else { 212 | ident = codegen.expect_token(tokenizer.identifier, "Expected identifier or `]`"); 213 | codegen.discard(); 214 | @assert(idents.node_get_type(ident) == itypes.none); 215 | 216 | if(codegen.peek_type() == tokenizer.open_square_bracket) { 217 | codegen.discard(); 218 | 219 | idents.node_set_type(ident, itypes.local_buffer); 220 | size = codegen.eval_comptime_expr(codegen.any_unambigous_expr); 221 | 222 | local_var_space += size; 223 | offset += size; 224 | idents.node_set_value(ident, offset); 225 | idents.node_set_attribute(ident, size); 226 | 227 | codegen.expect_token(tokenizer.closing_square_bracket, "Expected `]` after local buffer size"); 228 | codegen.discard(); 229 | } else { 230 | idents.node_set_type(ident, itypes.local_variable); 231 | 232 | local_var_space += 8; 233 | offset += 8; 234 | idents.node_set_value(ident, offset); 235 | } 236 | } 237 | 238 | if(codegen.peek_type() == tokenizer.comma) { 239 | codegen.discard(); 240 | } else {} 241 | continue; // @BUG: I need this for some reason. 242 | } 243 | } else {} 244 | 245 | codegen.function_prologue(num_args, local_var_space, offset); 246 | } 247 | 248 | fn clear_local_vars_in(node) [i, next] { 249 | i = 0; 250 | 251 | switch (idents.node_get_type(node)) { 252 | endcase; 253 | case itypes.local_variable: 254 | case itypes.local_buffer: 255 | idents.node_set_type(node, itypes.none); 256 | endcase; 257 | } 258 | 259 | node = idents.node_addr(node); 260 | 261 | loop { 262 | if(i == idents.max_nexts) { 263 | return; 264 | } else {} 265 | 266 | next = idents.trie_node_next(node, i); 267 | 268 | i += 1; 269 | 270 | if(next) { 271 | clear_local_vars_in(next); 272 | continue; 273 | } else { 274 | continue; 275 | } 276 | } 277 | } 278 | 279 | fn clear_local_vars() [node] { 280 | node = codegen.current_file_root[0]; 281 | clear_local_vars_in(node); 282 | } 283 | 284 | fn parse_function() [fn_ident, root, fn_addr, fn_attrs] { 285 | codegen.discard(); 286 | 287 | codegen.return_was_used[0] = 0; 288 | 289 | fn_ident = codegen.expect_token(tokenizer.identifier, "Expected identifier after `fn`"); 290 | 291 | fn_addr = writer.code_addr(); 292 | idents.node_set_type(fn_ident, itypes.function_addr); 293 | idents.node_set_value(fn_ident, fn_addr); 294 | idents.node_set_attribute(fn_ident, 1); // We assume it returns for recursion purposes 295 | 296 | codegen.add_symbol(fn_ident); 297 | codegen.discard(); 298 | 299 | read_stack_frame(); 300 | 301 | root = codegen.current_file_root[0]; 302 | // Can be exited by falling out of block 303 | fn_attrs = codegen.parse_block(root); 304 | 305 | codegen.expect_token(tokenizer.closing_curly_brace, "Expected `}` after function body"); 306 | codegen.discard(); 307 | 308 | // TODO: Only do this if the block end can be reached 309 | codegen.eval_type[0] = itypes.runtime_int; 310 | if(fn_attrs) { 311 | codegen.return_evaluated(); 312 | } else { } 313 | 314 | // Can also be exited by `return` statement 315 | fn_attrs |= codegen.return_was_used[0]; 316 | 317 | idents.node_set_attribute(fn_ident, fn_attrs); 318 | 319 | clear_local_vars(); 320 | } 321 | 322 | fn parse_zeroes() [ident, tmp] { 323 | codegen.discard(); 324 | 325 | ident = codegen.expect_token(tokenizer.identifier, "Expected identifier after `zeroes`"); 326 | 327 | tmp = writer.section_addr(writer.bss); 328 | idents.node_set_value(ident, tmp); 329 | 330 | codegen.add_symbol(ident); 331 | codegen.discard(); 332 | 333 | if(codegen.peek_type() == tokenizer.open_square_bracket) { 334 | idents.node_set_type(ident, itypes.global_buffer); 335 | 336 | codegen.discard(); 337 | 338 | tmp = codegen.eval_comptime_expr(); 339 | 340 | codegen.expect_token(tokenizer.closing_square_bracket, "Expected `]` after zeroes decl size"); 341 | codegen.discard(); 342 | 343 | writer.write_generic(0, writer.bss, tmp); 344 | 345 | idents.node_set_attribute(ident, tmp); 346 | } else { 347 | idents.node_set_type(ident, itypes.global_variable); 348 | writer.write_generic(0, writer.bss, 8); 349 | } 350 | 351 | codegen.expect_token(tokenizer.semicolon, "Expected `;` after zeroes declaration"); 352 | codegen.discard(); 353 | } 354 | 355 | fn parse_top_level() { 356 | codegen.current_file_root[0] = idents.alloc(); 357 | if(root_root[0]) { 358 | 359 | } else { 360 | root_root[0] = codegen.current_file_root[0]; 361 | } 362 | add_builtins_to_current_file(); 363 | 364 | loop { 365 | switch(codegen.peek_type()) { 366 | @todo("parse_top_level default"); 367 | 368 | case tokenizer.end_of_file: 369 | codegen.discard(); 370 | return; 371 | 372 | case tokenizer.keyword_zeroes: 373 | parse_zeroes(); 374 | continue; 375 | 376 | case tokenizer.keyword_fn: 377 | parse_function(); 378 | continue; 379 | 380 | case tokenizer.keyword_enum: 381 | parse_enum_decl(); 382 | continue; 383 | 384 | case tokenizer.keyword_comptime: 385 | parse_comptime_decl(); 386 | continue; 387 | 388 | case tokenizer.keyword_import: 389 | parse_import(); 390 | continue; 391 | } 392 | } 393 | } 394 | 395 | fn init(base_addr) { 396 | idents.init(); 397 | writer.init(base_addr); 398 | import_file_root[0] = idents.alloc(); 399 | parse_top_level_ptr[0] = parse_top_level; 400 | 401 | builtins.create_builtin_node(); 402 | } 403 | -------------------------------------------------------------------------------- /src/elf.co: -------------------------------------------------------------------------------- 1 | import "src/writer.co" writer; 2 | import "src/syscalls.co" syscalls; 3 | import "src/codegen.co" codegen; 4 | import "src/strings.co" strings; 5 | import "src/identifiers.co" idents; 6 | import "src/identifier_types.co" itypes; 7 | import "src/tokenizer.co" tokenizer; 8 | 9 | enum { 10 | sh_null, 11 | 12 | sh_text, 13 | sh_rodata, 14 | sh_data, 15 | sh_bss, 16 | 17 | sh_shstrtab, 18 | sh_symtab, 19 | sh_symstrtab, 20 | 21 | sh_end, 22 | }; 23 | 24 | enum { 25 | phdr_size = 0x38, 26 | shdr_size = 0x40, 27 | shstrtab_size = 55, 28 | }; 29 | 30 | enum { 31 | layout_header, 32 | layout_header_end = 0x40, 33 | 34 | layout_phdrs = layout_header_end, 35 | layout_phdrs_end = layout_phdrs + (phdr_size * 4), 36 | 37 | layout_shdrs = layout_phdrs_end, 38 | layout_shdrs_end = layout_shdrs + (shdr_size * sh_end), 39 | 40 | layout_shstrtab = layout_shdrs_end, 41 | layout_shstrtab_end = layout_shstrtab + shstrtab_size, 42 | }; 43 | 44 | zeroes symtab_ptr[8]; 45 | zeroes symtab_offset[8]; 46 | 47 | zeroes num_symbols[8]; 48 | 49 | zeroes symstrtab_ptr[8]; 50 | zeroes symstrtab_offset[8]; 51 | 52 | fn add_symbol(node) [addr, name, function, len, ptr] { 53 | addr = idents.node_get_value(node); 54 | name = tokenizer.buffer; 55 | 56 | ptr = symstrtab_ptr[0]; 57 | ptr += symstrtab_offset[0]; 58 | 59 | len = strings.len(name); 60 | len += 1; // Include null terminator 61 | 62 | @memcpy(ptr, name, len); 63 | 64 | ptr = symtab_ptr[0]; 65 | ptr += symtab_offset[0]; 66 | 67 | ptr[0] = symstrtab_offset[0]; 68 | if(idents.node_get_type(node) == itypes.function_addr) { 69 | ptr[4] = 0x02; 70 | ptr[6] = sh_text; 71 | } else { 72 | ptr[4] = 0x01; 73 | ptr[6] = sh_bss; 74 | } 75 | ptr[8] = addr; 76 | 77 | symtab_offset[0] += 0x18; 78 | 79 | symstrtab_offset[0] += len; 80 | num_symbols[0] += 1; 81 | } 82 | 83 | fn init() { 84 | symtab_ptr[0] = syscalls.anon_mmap(0x100000); 85 | symstrtab_ptr[0] = syscalls.anon_mmap(0x100000); 86 | 87 | symtab_offset[0] += 0x18; 88 | symstrtab_offset[0] += 1; 89 | num_symbols[0] += 1; 90 | 91 | codegen.add_symbol_ptr[0] = add_symbol; 92 | } 93 | 94 | comptime elf_header_size = 0x1000; 95 | 96 | zeroes ptr[8]; 97 | zeroes elf_header[elf_header_size]; 98 | zeroes tmp[0x100]; 99 | 100 | fn add_data(data, len) [p] { 101 | p = ptr[0]; 102 | @memcpy(p, data, len); 103 | ptr[0] += len; 104 | } 105 | 106 | fn add_int(value, size) { 107 | tmp[0] = value; 108 | add_data(tmp, size); 109 | } 110 | 111 | fn seek_align(fd) [seek] { 112 | seek = syscalls.lseek(fd, 0, syscalls.SEEK_CUR); 113 | seek += 0xFFF; 114 | seek &= ~0xFFF; 115 | syscalls.lseek(fd, seek, syscalls.SEEK_SET); 116 | } 117 | 118 | fn write(fd) [offset, i] { 119 | ptr[0] = elf_header; 120 | 121 | add_data("\x7FELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); // Header 122 | add_int(2, 2); // Type = Executable 123 | add_int(0x3E, 2); // Machine = x86_64 124 | add_int(1, 4); // Version = Current 125 | 126 | i = codegen.current_file_root[0]; 127 | i = idents.lookup("main", i); 128 | i = idents.node_get_value(i); 129 | 130 | tmp[0] = codegen.add_elf_entry_point(i); 131 | add_data(tmp, 8); 132 | 133 | add_int(layout_phdrs, 8); // phoff 134 | add_int(layout_shdrs, 8); // shoff 135 | add_int(0, 4); // flags 136 | add_int(0, 2); // ehsize 137 | add_int(phdr_size, 2); // phentsize 138 | add_int(4, 2); // phnum 139 | add_int(shdr_size, 2); // shentsize 140 | add_int(sh_end, 2); // shnum 141 | add_int(sh_shstrtab, 2); // shstrndx 142 | 143 | i = 0; 144 | offset = elf_header_size; 145 | 146 | // phdrs 147 | loop { 148 | switch(i) { 149 | break; 150 | 151 | case writer.text: 152 | add_int(1, 4); // LOAD 153 | add_int(0x5, 4); // RX 154 | endcase; 155 | 156 | case writer.rodata: 157 | add_int(1, 4); // LOAD 158 | add_int(0x4, 4); // R 159 | endcase; 160 | 161 | case writer.data: 162 | add_int(1, 4); // LOAD 163 | add_int(0x6, 4); // RW 164 | endcase; 165 | 166 | case writer.bss: 167 | add_int(1, 4); // LOAD 168 | add_int(0x6, 4); // RW 169 | endcase; 170 | } 171 | 172 | // fileoff 173 | add_int(offset, 8); // foff 174 | 175 | tmp[0] = writer.section_base(i); 176 | add_data(tmp, 8); // vaddr 177 | add_data(tmp, 8); // paddr 178 | 179 | // filesz 180 | if(i == writer.bss) { 181 | tmp[0] = 0; 182 | } else { 183 | tmp[0] = writer.section_offset(i); 184 | } 185 | add_data(tmp, 8); 186 | 187 | tmp[0] += 0xFFF; 188 | tmp[0] &= ~0xFFF; 189 | 190 | if(i != writer.bss) { 191 | offset += tmp[0]; 192 | } else { } 193 | 194 | // memsz 195 | tmp[0] = writer.section_offset(i); 196 | tmp[0] += 0xFFF + 7; 197 | tmp[0] &= ~0xFFF; 198 | 199 | add_data(tmp, 8); 200 | 201 | // Alignment 202 | add_int(0x1000, 8); 203 | 204 | i += 1; 205 | continue; 206 | } 207 | 208 | offset = elf_header_size; 209 | 210 | // null shdr 211 | add_int(0, shdr_size); 212 | 213 | // text shdr 214 | add_int(1, 4); // name 215 | add_int(1, 4); // type = PROGBITS 216 | add_int(0x06, 8); // flags = AX 217 | tmp[0] = writer.section_base(writer.text); 218 | add_data(tmp, 8); // addr 219 | add_int(offset, 8); // offset 220 | tmp[0] = writer.section_offset(writer.text); 221 | add_data(tmp, 8); // size 222 | tmp[0] += 0xFFF; 223 | tmp[0] &= ~0xFFF; 224 | offset += tmp[0]; 225 | add_int(0, 4); // link 226 | add_int(0, 4); // info 227 | add_int(0x1000, 8); // addralign 228 | add_int(0, 8); // entsize 229 | 230 | // rodata shdr 231 | add_int(7, 4); // name 232 | add_int(1, 4); // type = PROGBITS 233 | add_int(0x02, 8); // flags = A 234 | tmp[0] = writer.section_base(writer.rodata); 235 | add_data(tmp, 8); // addr 236 | add_int(offset, 8); // offset 237 | tmp[0] = writer.section_offset(writer.rodata); 238 | add_data(tmp, 8); // size 239 | tmp[0] += 0xFFF; 240 | tmp[0] &= ~0xFFF; 241 | offset += tmp[0]; 242 | add_int(0, 4); // link 243 | add_int(0, 4); // info 244 | add_int(0x1000, 8); // addralign 245 | add_int(0, 8); // entsize 246 | 247 | // data shdr 248 | add_int(0x0F, 4); // name 249 | add_int(1, 4); // type = PROGBITS 250 | add_int(0x03, 8); // flags = AW 251 | tmp[0] = writer.section_base(writer.data); 252 | add_data(tmp, 8); // addr 253 | add_int(offset, 8); // offset 254 | tmp[0] = writer.section_offset(writer.data); 255 | add_data(tmp, 8); // size 256 | tmp[0] += 0xFFF; 257 | tmp[0] &= ~0xFFF; 258 | offset += tmp[0]; 259 | add_int(0, 4); // link 260 | add_int(0, 4); // info 261 | add_int(0x1000, 8); // addralign 262 | add_int(0, 8); // entsize 263 | 264 | // bss shdr 265 | add_int(0x15, 4); // name 266 | add_int(8, 4); // type = NOBITS 267 | add_int(0x03, 8); // flags = AW 268 | tmp[0] = writer.section_base(writer.bss); 269 | add_data(tmp, 8); // addr 270 | add_int(0, 8); // offset 271 | tmp[0] = writer.section_offset(writer.bss); 272 | add_data(tmp, 8); // size 273 | add_int(0, 4); // link 274 | add_int(0, 4); // info 275 | add_int(0x1000, 8); // addralign 276 | add_int(0, 8); // entsize 277 | 278 | // shstrtab shdr 279 | add_int(0x1A, 4); // name 280 | add_int(3, 4); // type = STRTAB 281 | add_int(0, 8); // flags 282 | add_int(0, 8); // addr 283 | add_int(layout_shstrtab, 8); // offset 284 | add_int(shstrtab_size, 8); // size 285 | add_int(0, 4); // link 286 | add_int(0, 4); // info 287 | add_int(1, 8); // addralign 288 | add_int(0, 8); // entsize 289 | 290 | // symtab shdr 291 | add_int(0x24, 4); // name 292 | add_int(2, 4); // type = SYMTAB 293 | add_int(0, 8); // flags 294 | add_int(0, 8); // addr 295 | add_int(offset, 8); // offset 296 | tmp[0] = symtab_offset[0]; 297 | add_data(tmp, 8); // size 298 | offset += tmp[0]; 299 | add_int(sh_symstrtab, 4); // link 300 | add_data(num_symbols, 4); // info 301 | add_int(0, 8); // addralign 302 | add_int(0x18, 8); // entsize 303 | 304 | // symstrtab shdr 305 | add_int(0x2C, 4); // name 306 | add_int(3, 4); // type = STRTAB 307 | add_int(0, 8); // flags 308 | add_int(0, 8); // add 309 | add_int(offset, 8); // offset 310 | tmp[0] = symstrtab_offset[0]; 311 | add_data(tmp, 8); // size 312 | offset += tmp[0]; 313 | add_int(0, 4); // link 314 | add_int(0, 4); // info 315 | add_int(0, 8); // addralign 316 | add_int(0, 8); // entsize 317 | 318 | add_data("\x00.text\x00.rodata\x00.data\x00.bss\x00.shstrtab\x00.symtab\x00.symstrtab\x00", shstrtab_size); 319 | syscalls.write_all(fd, elf_header, elf_header_size); 320 | 321 | seek_align(fd); 322 | 323 | offset = writer.section_offset(writer.text); 324 | i = writer.current_section_buf[writer.text * 8]; 325 | syscalls.write_all(fd, i, offset); 326 | 327 | seek_align(fd); 328 | 329 | offset = writer.section_offset(writer.rodata); 330 | i = writer.current_section_buf[writer.rodata * 8]; 331 | syscalls.write_all(fd, i, offset); 332 | 333 | seek_align(fd); 334 | 335 | offset = writer.section_offset(writer.data); 336 | i = writer.current_section_buf[writer.data * 8]; 337 | syscalls.write_all(fd, i, offset); 338 | 339 | seek_align(fd); 340 | 341 | offset = symtab_offset[0]; 342 | i = symtab_ptr[0]; 343 | syscalls.write_all(fd, i, offset); 344 | 345 | offset = symstrtab_offset[0]; 346 | i = symstrtab_ptr[0]; 347 | syscalls.write_all(fd, i, offset); 348 | } 349 | -------------------------------------------------------------------------------- /src/identifier_types.co: -------------------------------------------------------------------------------- 1 | enum { 2 | // Not a valid identifier 3 | none, 4 | 5 | // Non-buffer local variable, value is rbp offset 6 | local_variable, 7 | 8 | // Buffer local variable, value is rbp offset and attribute is size 9 | local_buffer, 10 | 11 | // The non-dereferenced address of a local buffer 12 | local_buffer_addr, 13 | 14 | // Non-buffer global variable, value is address 15 | global_variable, 16 | 17 | // Buffer global variable, value is address and attribute is size 18 | global_buffer, 19 | 20 | // The non-dereferenced address of a global variable 21 | global_buffer_addr, 22 | 23 | // This filename has finished parsing, attribute is variable scope node id 24 | fully_parsed_filename, 25 | 26 | // Parsing of the file is incomplete, importing such a file implies a circular import. 27 | partially_parsed_filename, 28 | 29 | // A variable scope that you can access like `scope.thing`. Attribute is the scope id. 30 | variable_scope, 31 | 32 | // A compile-time known integral value, value is value 33 | comptime_int, 34 | 35 | // Address of a function, value is address of the function entry 36 | function_addr, 37 | 38 | // A builtin function that can be called from user code during code generation 39 | builtin_function_codegen, 40 | 41 | // A builtin function that can be called from user code during compile time evaluation 42 | builtin_function_comptime, 43 | 44 | // Some kind of runtime known pointer sized int 45 | runtime_int, 46 | 47 | // Somekind of runtime known dereferenced pointer 48 | runtime_reference, 49 | }; 50 | -------------------------------------------------------------------------------- /src/identifiers.co: -------------------------------------------------------------------------------- 1 | // Layout of the trie nodes 2 | 3 | // Node next index size 4 | comptime trie_node_next_size = 4; 5 | 6 | comptime max_nexts = 0x80; 7 | 8 | // Node value offset 9 | comptime trie_node_value = max_nexts * trie_node_next_size; 10 | 11 | // Size of an entire trie node, including next pointers, attribute and value 12 | comptime trie_node_size = trie_node_value + (4 + (4 + 8)); 13 | 14 | zeroes addr_value_trie_bytes[8]; 15 | zeroes last_addr_value_trie[8]; 16 | 17 | import "src/syscalls.co" syscalls; 18 | 19 | fn init() [num_bytes] { 20 | num_bytes = 0x10000 * trie_node_size; 21 | addr_value_trie_bytes[0] = syscalls.anon_mmap(num_bytes); 22 | } 23 | 24 | fn alloc() { 25 | last_addr_value_trie[0] += 1; 26 | return last_addr_value_trie[0]; 27 | } 28 | 29 | fn node_addr(node_idx) { 30 | return addr_value_trie_bytes[0] + (node_idx * trie_node_size); 31 | } 32 | 33 | fn trie_node_next(node_ptr, idx) [tmp] { 34 | @assert(idx < max_nexts); 35 | tmp = node_ptr + (idx * trie_node_next_size); 36 | return @read32(tmp); 37 | } 38 | 39 | fn trie_node_set_next(node_ptr, idx, next) [tmp] { 40 | @assert(idx < max_nexts); 41 | tmp = node_ptr + (idx * trie_node_next_size); 42 | @write32(tmp, next); 43 | } 44 | 45 | fn lookup(name, node) [addr, chr] { 46 | loop { 47 | chr = @read8(name); 48 | if(chr) { 49 | addr = node_addr(node); 50 | node = trie_node_next(addr, chr); 51 | name += 1; 52 | if(node) { 53 | continue; 54 | } else { 55 | node = alloc(); 56 | trie_node_set_next(addr, chr, node); 57 | continue; 58 | } 59 | } else { 60 | return node; 61 | } 62 | } 63 | } 64 | 65 | comptime type_offset = trie_node_value; 66 | 67 | fn node_get_type(node) { 68 | node = node_addr(node); 69 | node += type_offset; 70 | return @read32(node); 71 | } 72 | 73 | fn node_set_type(node, value) { 74 | node = node_addr(node); 75 | node += type_offset; 76 | return @write32(node, value); 77 | } 78 | 79 | comptime attribute_offset = type_offset + 4; 80 | 81 | fn node_get_attribute(node) { 82 | node = node_addr(node); 83 | node += attribute_offset; 84 | return @read32(node); 85 | } 86 | 87 | fn node_set_attribute(node, value) { 88 | node = node_addr(node); 89 | node += attribute_offset; 90 | return @write32(node, value); 91 | } 92 | 93 | comptime value_offset = attribute_offset + 4; 94 | 95 | fn node_get_value(node) { 96 | node = node_addr(node); 97 | node += value_offset; 98 | return node[0]; 99 | } 100 | 101 | fn node_set_value(node, value) { 102 | node = node_addr(node); 103 | node += value_offset; 104 | node[0] = value; 105 | return; 106 | } 107 | -------------------------------------------------------------------------------- /src/main.co: -------------------------------------------------------------------------------- 1 | // Low level code, establish what's needed for @todo() and @assert() 2 | import "src/printer.co" printer; 3 | import "src/syscalls.co" syscalls; 4 | 5 | fn assert_fail(statement, file, line) { 6 | printer.print_string("Assertion failed: "); 7 | if(statement) { 8 | printer.print_string(statement); 9 | } else { 10 | printer.print_string(""); 11 | } 12 | printer.print_string(", at "); 13 | if(file) { 14 | printer.print_string(file); 15 | } else { 16 | printer.print_string(""); 17 | } 18 | printer.print_string(": "); 19 | printer.print_decimal(line); 20 | 21 | printer.putchar('\n'); 22 | printer.exit(1); 23 | unreachable; 24 | } 25 | 26 | // Everything else 27 | import "src/compiler.co" compiler; 28 | import "src/strings.co" strings; 29 | import "src/x86_64.co" x86_64; 30 | import "src/elf.co" elf; 31 | 32 | fn identity(lmao) { 33 | return lmao; 34 | } 35 | 36 | fn main(argc, argv) [tmp, fd, out_filename, i] { 37 | if(argc < 3) { 38 | printer.print_string("Usage: "); 39 | if(argc) { 40 | tmp = argv[0]; 41 | printer.print_string(tmp); 42 | } else { 43 | printer.print_string("comp"); 44 | } 45 | printer.print_string("
\n"); 46 | printer.exit(1); 47 | unreachable; 48 | } else { 49 | out_filename = identity("out.elf"); 50 | 51 | // Parse the binary base address 52 | tmp = argv[2 * 8]; 53 | tmp = strings.parse_detect(tmp, 16); 54 | compiler.init(tmp); 55 | 56 | x86_64.init(); 57 | elf.init(); 58 | 59 | // Get the root source file 60 | tmp = argv[1 * 8]; 61 | compiler.set_source_file(tmp); 62 | 63 | i = 3; 64 | loop { 65 | if(i < argc) { 66 | tmp = argv[i * 8]; 67 | if(@read8(tmp) == '-') { 68 | tmp += 1; 69 | if(@read8(tmp) == 'o') { 70 | out_filename = argv + ((i + 1) * 8); 71 | out_filename = out_filename[0]; 72 | i += 2; 73 | continue; 74 | } else { } 75 | } else { } 76 | printer.print_string("Unknown argument!\n"); 77 | printer.exit(1); 78 | } else { 79 | break; 80 | } 81 | } 82 | 83 | fd = syscalls.open(out_filename, 0x41, syscalls.executable); 84 | 85 | compiler.parse_top_level(); 86 | 87 | elf.write(fd); 88 | printer.exit(0); 89 | unreachable; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/printer.co: -------------------------------------------------------------------------------- 1 | import "src/syscalls.co" syscalls; 2 | 3 | comptime buffer_size = 0x1000; 4 | 5 | zeroes buffer[buffer_size]; 6 | zeroes buf_size[8]; 7 | 8 | fn flush() [size] { 9 | if(buf_size[0]) { 10 | size = buf_size[0]; 11 | if(syscalls.write_all(syscalls.stderr, buffer, size)) { 12 | syscalls.exit(1); 13 | unreachable; 14 | } else { 15 | buf_size[0] = 0; 16 | return; 17 | } 18 | } else { 19 | return; 20 | } 21 | } 22 | 23 | fn putchar(ch) [ptr] { 24 | if(buf_size[0] == buffer_size) { 25 | flush(); 26 | } else {} 27 | 28 | ptr = buffer; 29 | ptr += buf_size[0]; 30 | buf_size[0] += 1; 31 | 32 | @write8(ptr, ch); 33 | 34 | // Uncomment this to flush on every '\n' 35 | // if(ch == '\n') { 36 | // flush(); 37 | // } else { } 38 | } 39 | 40 | fn print_string(str) [ch] { 41 | loop { 42 | ch = @read8(str); 43 | if(ch) { 44 | putchar(ch); 45 | str += 1; 46 | continue; 47 | } else { 48 | return; 49 | } 50 | } 51 | } 52 | 53 | comptime dec_offset = '0' - 0x00; 54 | comptime hex_offset = 'A' - 0x0A; 55 | 56 | fn print_hex(value) [tmp] { 57 | tmp = value; 58 | tmp &= 0xF; 59 | 60 | if(9 < tmp) { 61 | tmp += hex_offset; 62 | } else { 63 | tmp += dec_offset; 64 | } 65 | 66 | value >>= 4; 67 | 68 | if(value) { 69 | print_hex(value); 70 | } else {} 71 | 72 | putchar(tmp); 73 | } 74 | 75 | fn print_decimal(value) [tmp] { 76 | tmp = value; 77 | tmp %= 10; 78 | tmp += dec_offset; 79 | 80 | value /= 10; 81 | 82 | if(value) { 83 | print_decimal(value); 84 | } else {} 85 | 86 | putchar(tmp); 87 | } 88 | 89 | fn log_hex(str, val) { 90 | print_string(str); 91 | print_string(": 0x"); 92 | print_hex(val); 93 | putchar('\n'); 94 | flush(); 95 | } 96 | 97 | // Print that we're exiting, 98 | // flush the buffer and then exit 99 | fn exit(exit_code) { 100 | print_string("Exiting with error code "); 101 | print_decimal(exit_code); 102 | print_string("...\n"); 103 | flush(); 104 | syscalls.exit(exit_code); 105 | unreachable; 106 | } 107 | -------------------------------------------------------------------------------- /src/source.co: -------------------------------------------------------------------------------- 1 | import "src/printer.co" printer; 2 | import "src/syscalls.co" syscalls; 3 | 4 | // All of these better be contigous :P 5 | zeroes file_len[8]; 6 | zeroes file_buf[8]; 7 | zeroes file_descriptor[8]; 8 | zeroes file_name[0x100]; 9 | 10 | zeroes current_index[8]; 11 | zeroes current_line[8]; 12 | zeroes current_column[8]; 13 | 14 | comptime file_context_size = 0 15 | + (@size_of(file_len) 16 | + (@size_of(file_buf) 17 | + (@size_of(file_descriptor) 18 | + (@size_of(file_name) 19 | + (@size_of(current_index) 20 | + (@size_of(current_line) 21 | + @size_of(current_column))))))) 22 | ; 23 | 24 | fn stash_file_info(out_ptr) { 25 | @memcpy(out_ptr, file_len, file_context_size); 26 | } 27 | 28 | fn restore_file_info(in_ptr) { 29 | @memcpy(file_len, in_ptr, file_context_size); 30 | } 31 | 32 | fn switch_file(fd) [filesz] { 33 | filesz = syscalls.lseek(fd, 0, syscalls.SEEK_END); 34 | @assert(filesz < 0x1000000); 35 | 36 | current_line[0] = 1; 37 | current_column[0] = 1; 38 | current_index[0] = 0; 39 | 40 | // mmap the entire file 41 | file_buf[0] = syscalls.mmap_file(fd, filesz); 42 | file_len[0] = filesz; 43 | file_descriptor[0] = fd; 44 | } 45 | 46 | fn end_of_file() [addr, len, fd] { 47 | addr = file_buf[0]; 48 | len = file_len[0]; 49 | syscalls.munmap(addr, len); 50 | 51 | fd = file_descriptor[0]; 52 | syscalls.close(fd); 53 | } 54 | 55 | fn print_line(target_line) [line, ptr, chr] { 56 | line = 1; 57 | ptr = file_buf[0]; 58 | 59 | // Loop until we reach the target line 60 | if(line != target_line) { 61 | loop { 62 | if(@read8(ptr) == '\n') { 63 | ptr += 1; 64 | line += 1; 65 | if(line == target_line) { 66 | break; 67 | } else { } 68 | } else { 69 | ptr += 1; 70 | } 71 | } 72 | } else { } 73 | 74 | // We're at the first line we should print 75 | loop { 76 | chr = @read8(ptr); 77 | printer.putchar(chr); 78 | if(chr == '\n') { 79 | return; 80 | } else { } 81 | } 82 | } 83 | 84 | fn print_current_line() [ptr, chr, idx] { 85 | // Search backwards until we find the start of the file or a newline 86 | idx = current_index; 87 | ptr = file_buf + idx; 88 | loop { 89 | if(idx) { 90 | idx -= 1; 91 | ptr = file_buf + idx; 92 | chr = @read8(ptr); 93 | if(chr == '\n') { 94 | break; 95 | } else { 96 | continue; 97 | } 98 | } else { 99 | break; 100 | } 101 | } 102 | 103 | @todo("print_current_line"); 104 | } 105 | 106 | fn peek(idx) { 107 | idx += current_index[0]; 108 | 109 | if(idx < file_len[0]) { 110 | idx += file_buf[0]; 111 | return @read8(idx); 112 | } else { 113 | return 0; 114 | } 115 | } 116 | 117 | fn consume(num) [chr] { 118 | loop { 119 | if(num) { 120 | chr = peek(0); 121 | 122 | //printer.print_string("Consumed '"); 123 | //printer.putchar(chr); 124 | //printer.print_string("'\n"); 125 | 126 | current_index[0] += 1; 127 | 128 | if(chr == '\n') { 129 | current_line[0] += 1; 130 | current_column[0] = 1; 131 | } else { 132 | current_column[0] += 1; 133 | } 134 | num -= 1; 135 | continue; 136 | } else { 137 | return chr; 138 | } 139 | } 140 | } 141 | 142 | fn skip_whitespace() { 143 | loop { 144 | switch(peek(0)) { 145 | return; 146 | case '\t': 147 | case '\n': 148 | case ' ': 149 | consume(); 150 | continue; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/strings.co: -------------------------------------------------------------------------------- 1 | fn len(str) [result] { 2 | result = 0; 3 | loop { 4 | if(@read8(str)) { 5 | str += 1; 6 | result += 1; 7 | continue; 8 | } else { 9 | return result; 10 | } 11 | } 12 | } 13 | 14 | fn find(str, chr) [result] { 15 | result = 0; 16 | loop { 17 | if(@read8(str) == chr) { 18 | str += 1; 19 | result += 1; 20 | continue; 21 | } else { 22 | return result; 23 | } 24 | } 25 | } 26 | 27 | comptime dec_offset = '0' - 0x00; 28 | comptime hex_upper_offset = 'A' - 0x0A; 29 | comptime hex_lower_offset = 'a' - 0x0a; 30 | 31 | fn chr_val(ch) { 32 | switch(ch) { 33 | return ~1; 34 | case '0'...'9': 35 | return ch - dec_offset; 36 | case 'a'...'z': 37 | return ch - hex_lower_offset; 38 | case 'A'...'Z': 39 | return ch - hex_upper_offset; 40 | } 41 | } 42 | 43 | // Optimized for parsing decimal ints 44 | fn parse_dec(str) [result, val] { 45 | result = 0; 46 | loop { 47 | val = @read8(str); 48 | val = chr_val(val); 49 | if(val == ~1) { 50 | return result; 51 | } else { 52 | result *= 10; 53 | result += val; 54 | str += 1; 55 | continue; 56 | } 57 | } 58 | } 59 | 60 | // Optimized for parsing hexadecimal ints 61 | fn parse_hex(str) [result, val] { 62 | result = 0; 63 | loop { 64 | val = @read8(str); 65 | val = chr_val(val); 66 | if(val == ~1) { 67 | return result; 68 | } else { 69 | result <<= 4; 70 | result += val; 71 | str += 1; 72 | continue; 73 | } 74 | } 75 | } 76 | 77 | // Generic int parser, for any base 78 | fn parse_base(str, base) [result, val] { 79 | result = 0; 80 | loop { 81 | val = @read8(str); 82 | val = chr_val(val); 83 | if(val == ~1) { 84 | return result; 85 | } else { 86 | result *= base; 87 | result += val; 88 | str += 1; 89 | continue; 90 | } 91 | } 92 | } 93 | 94 | fn parse(str, base) { 95 | switch(base) { 96 | return parse_base(str, base); 97 | case 10: 98 | return parse_dec(str); 99 | case 16: 100 | return parse_dec(str); 101 | } 102 | } 103 | 104 | // Detects base automatically based on prefix 105 | // or uses the fallback base 106 | fn parse_detect(str, fallback_base) [tmp] { 107 | tmp = str + 1; 108 | if(@read8(str) == '0') { 109 | if(@read8(tmp) == 'x') { 110 | tmp += 1; 111 | return parse_hex(tmp); 112 | } else { 113 | return parse_base(str, fallback_base); 114 | } 115 | } else { 116 | return parse_base(str, fallback_base); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/syscalls.co: -------------------------------------------------------------------------------- 1 | enum { 2 | SYS_READ = 0, 3 | SYS_WRITE = 1, 4 | SYS_OPEN = 2, 5 | SYS_CLOSE = 3, 6 | 7 | SYS_LSEEK = 8, 8 | SYS_MMAP = 9, 9 | SYS_MUNMAP = 11, 10 | 11 | SYS_CLONE = 56, 12 | SYS_FORK = 57, 13 | SYS_EXIT = 60, 14 | 15 | SYS_EXECVEAT = 322, 16 | }; 17 | 18 | enum { 19 | stdin = 0, 20 | stdout = 1, 21 | stderr = 2, 22 | }; 23 | 24 | fn read(fd, buf, num) { 25 | return @syscall(SYS_READ, fd, buf, num); 26 | } 27 | 28 | fn write(fd, buf, num) { 29 | return @syscall(SYS_WRITE, fd, buf, num); 30 | } 31 | 32 | enum { 33 | O_RDONLY = 0x00000, 34 | O_WRONLY = 0x00001, 35 | O_RDWR = 0x00002, 36 | O_CREAT = 0x00040, 37 | O_APPEND = 0x00400, 38 | O_CLOEXEC = 0x80000, 39 | //O_TMPFILE = 40 | }; 41 | 42 | enum { 43 | AT_FDCWD = -100, 44 | 45 | AT_EMPTY_PATH = 0x1000, 46 | }; 47 | 48 | enum { 49 | executable = 0x1ED, // = 0o755 50 | normal_file = 0x1A4, // = 0o644 51 | }; 52 | 53 | fn open(filename, flags, mode) { 54 | return @syscall(SYS_OPEN, filename, flags, mode); 55 | } 56 | 57 | fn close(fd) { 58 | return @syscall(SYS_CLOSE, fd); 59 | } 60 | 61 | enum { 62 | SEEK_SET = 0, 63 | SEEK_CUR = 1, 64 | SEEK_END = 2, 65 | }; 66 | 67 | fn lseek(fd, offset, whence) { 68 | return @syscall(SYS_LSEEK, fd, offset, whence); 69 | } 70 | 71 | enum { 72 | PROT_NONE = 0x00, 73 | PROT_READ = 0x01, 74 | PROT_WRITE = 0x02, 75 | PROT_EXEC = 0x04, 76 | PROT_RW = PROT_READ | PROT_WRITE, 77 | }; 78 | 79 | enum { 80 | MAP_SHARED = 0x0001, 81 | MAP_PRIVATE = 0x0002, 82 | MAP_FIXED = 0x0010, 83 | MAP_ANONYMOUS = 0x0020, 84 | MAP_ANON = MAP_ANONYMOUS, 85 | MAP_POPULATE = 0x8000, 86 | MAP_ANONYMOUS_PRIV = MAP_ANONYMOUS | MAP_PRIVATE, 87 | }; 88 | 89 | fn mmap(addr, size, prot, flags, fd, offset) { 90 | return @syscall(SYS_MMAP, addr, size, prot, flags, fd, offset); 91 | } 92 | 93 | fn munmap(addr, size) { 94 | return @syscall(SYS_MUNMAP, addr, size); 95 | } 96 | 97 | // fn clone(fptr, stack, flags, arg, parent_tid, tls, child_tid) { 98 | // return @syscall(SYS_CLONE, fptr, stack, flags, arg, parent_tid, tls, child_tid); 99 | // } 100 | 101 | fn fork() { 102 | return @syscall(SYS_FORK); 103 | } 104 | 105 | fn exit(exit_code) { 106 | @syscall(SYS_EXIT, exit_code); 107 | unreachable; 108 | } 109 | 110 | // Wrapper around write() to call it over and over until the entire buffer is written 111 | fn write_all(fd, buf, num) [num_written] { 112 | loop { 113 | if(num) { 114 | num_written = @syscall(SYS_WRITE, fd, buf, num); 115 | if(num < num_written) { 116 | return 1; 117 | } else { 118 | num -= num_written; 119 | buf += num_written; 120 | continue; 121 | } 122 | } else { 123 | return 0; 124 | } 125 | } 126 | } 127 | 128 | // Wrapper around read() to call it over and over until the entire buffer is read 129 | fn read_all(fd, buf, num) [num_read] { 130 | loop { 131 | if(num) { 132 | num_read = @syscall(SYS_READ, fd, buf, num); 133 | if(num < num_read) { 134 | return 1; 135 | } else { 136 | num -= num_read; 137 | buf += num_read; 138 | continue; 139 | } 140 | } else { 141 | return 0; 142 | } 143 | } 144 | } 145 | 146 | // Wrapper around mmap() to allocate memory 147 | fn anon_mmap(size) { 148 | // Page align size up 149 | size += 0xFFF; 150 | size &= ~0xFFF; 151 | return @syscall(SYS_MMAP, 0, size, PROT_RW, MAP_ANONYMOUS_PRIV, 0, 0); 152 | } 153 | 154 | // Wrapper around mmap() to map a file 155 | fn mmap_file(fd, size) { 156 | // Page align size up 157 | size += 0xFFF; 158 | size &= ~0xFFF; 159 | return @syscall(SYS_MMAP, 0, size, PROT_READ, MAP_SHARED, fd, 0); 160 | } 161 | 162 | fn execve_fd(fd, argv, envp) { 163 | return @syscall(SYS_EXECVEAT, fd, "", argv, envp, AT_EMPTY_PATH); 164 | } 165 | -------------------------------------------------------------------------------- /src/tokenizer.co: -------------------------------------------------------------------------------- 1 | import "src/identifiers.co" identifiers; 2 | import "src/printer.co" printer; 3 | import "src/source.co" source; 4 | import "src/strings.co" strings; 5 | import "src/writer.co" writer; 6 | 7 | fn log_context() [tmp] { 8 | printer.print_string("while parsing "); 9 | printer.print_string(source.file_name); 10 | printer.putchar(':'); 11 | tmp = source.current_line[0]; 12 | printer.print_decimal(tmp); 13 | printer.putchar('\n'); 14 | } 15 | 16 | fn error(fail_message) [tmp] { 17 | printer.print_string(fail_message); 18 | printer.putchar('\n'); 19 | log_context(); 20 | printer.exit(1); 21 | unreachable; 22 | } 23 | 24 | zeroes token_type[8]; 25 | zeroes token_value[8]; 26 | zeroes buffer[0x1000]; // Buffer for string literals, identifers etc 27 | zeroes token_line[8]; 28 | 29 | enum { 30 | none, 31 | 32 | end_of_file, 33 | 34 | // {} 35 | open_curly_brace, 36 | closing_curly_brace, 37 | 38 | // () 39 | open_paren, 40 | closing_paren, 41 | 42 | // [] 43 | open_square_bracket, 44 | closing_square_bracket, 45 | 46 | less_than, // < 47 | less_than_equal, // <= 48 | 49 | greater_than, // > 50 | greater_than_equal, // >= 51 | 52 | shift_left, // << 53 | shift_left_inplace, // <<= 54 | 55 | shift_right, // >> 56 | shift_right_inplace, // >>= 57 | 58 | assignment, // = 59 | equals, // == 60 | not_equals, // != 61 | 62 | comma, // , 63 | colon, // : 64 | semicolon, // ; 65 | 66 | question_mark, // ? 67 | 68 | bitnot, // ~ 69 | 70 | dot, // . 71 | 72 | dot_dot_dot, // ... 73 | 74 | bitor, // | 75 | bitor_inplace, // |= 76 | 77 | bitxor, // ^ 78 | bitxor_inplace, // ^= 79 | 80 | bitand, // & 81 | bitand_inplace, // &= 82 | 83 | division, // / 84 | division_inplace, // /= 85 | 86 | modulus, // % 87 | modulus_inplace, // %= 88 | 89 | multiplication, // * 90 | multiplication_inplace, // *= 91 | 92 | addition, // + 93 | addition_inplace, // += 94 | 95 | subtraction, // - 96 | subtraction_inplace, // -= 97 | 98 | // like `420` or `0x69`, value is the value of the literal 99 | int_literal, 100 | 101 | // Like `"Hello, \x00 is a valid byte"` 102 | // Value is the length of the string 103 | string_literal, 104 | 105 | // like `my_variable`, value is pointer to node 106 | identifier, 107 | 108 | // All the keywords in the language 109 | keyword_break, 110 | keyword_case, 111 | keyword_comptime, 112 | keyword_continue, 113 | keyword_else, 114 | keyword_endcase, 115 | keyword_enum, 116 | keyword_fn, 117 | keyword_if, 118 | keyword_import, 119 | keyword_loop, 120 | keyword_return, 121 | keyword_switch, 122 | keyword_undefined, 123 | keyword_unreachable, 124 | keyword_zeroes, 125 | }; 126 | 127 | fn is_first_ident_chr(idx) { 128 | switch(source.peek(idx)) { 129 | return 0; 130 | 131 | case 'a'...'z': 132 | case 'A'...'Z': 133 | case '@': 134 | case '_': 135 | return 1; 136 | } 137 | unreachable; 138 | } 139 | 140 | fn is_ident_chr(idx) { 141 | switch(source.peek(idx)) { 142 | return 0; 143 | 144 | case 'a'...'z': 145 | case 'A'...'Z': 146 | case '0'...'9': 147 | case '@': 148 | case '_': 149 | return 1; 150 | } 151 | unreachable; 152 | } 153 | 154 | fn parse_identifier(node) [ptr] { 155 | @assert(is_first_ident_chr(0)); 156 | 157 | token_type[0] = identifier; 158 | ptr = buffer; 159 | 160 | loop { 161 | if(is_ident_chr(0)) { 162 | ptr[0] = source.consume(1); 163 | node = identifiers.lookup(ptr, node); 164 | ptr += 1; 165 | continue; 166 | } else { 167 | @write8(ptr, 0); 168 | token_value[0] = node; 169 | return; 170 | } 171 | } 172 | } 173 | 174 | fn attempt_keyword(keyword_text, keyword, node) [idx, chr] { 175 | idx = 0; 176 | loop { 177 | chr = @read8(keyword_text); 178 | if(chr) { 179 | if(source.peek(idx) == chr) { 180 | keyword_text += 1; 181 | idx += 1; 182 | continue; 183 | } else { 184 | parse_identifier(node); 185 | return; 186 | } 187 | } else { 188 | if(is_ident_chr(idx)) { 189 | parse_identifier(node); 190 | return; 191 | } else { 192 | source.consume(idx); 193 | token_type[0] = keyword; 194 | return; 195 | } 196 | } 197 | } 198 | } 199 | 200 | fn read_int_literal_token_hex() [chr] { 201 | token_type[0] = int_literal; 202 | token_value[0] = 0; 203 | 204 | loop { 205 | chr = source.peek(0); 206 | switch(chr) { 207 | return; 208 | 209 | case '0'...'9': 210 | case 'a'...'f': 211 | case 'A'...'F': 212 | token_value[0] <<= 4; 213 | token_value[0] += strings.chr_val(chr); 214 | source.consume(1); 215 | continue; 216 | } 217 | } 218 | } 219 | 220 | fn read_int_literal_token_decimal() [chr] { 221 | token_type[0] = int_literal; 222 | token_value[0] = 0; 223 | 224 | loop { 225 | chr = source.peek(0); 226 | switch(chr) { 227 | return; 228 | case '0'...'9': 229 | token_value[0] *= 10; 230 | token_value[0] += strings.chr_val(chr); 231 | source.consume(1); 232 | continue; 233 | } 234 | } 235 | } 236 | 237 | fn read_int_literal_token_zero() { 238 | // peek(0) == '0' when we enter here 239 | if(source.peek(1) == 'x') { 240 | source.consume(2); 241 | return read_int_literal_token_hex(); 242 | } else { 243 | return read_int_literal_token_decimal(); 244 | } 245 | } 246 | 247 | fn peek_val(peek) { 248 | peek = source.peek(peek); 249 | return strings.chr_val(peek); 250 | } 251 | 252 | fn read_char_literal_token() [chr] { 253 | token_type[0] = int_literal; 254 | chr = source.peek(0); 255 | 256 | if(chr == '\\') { 257 | switch(source.peek(1)) { 258 | error("Bad char literal escaped char"); 259 | 260 | case 'n': 261 | source.consume(3); 262 | return '\n'; 263 | 264 | case 'r': 265 | source.consume(3); 266 | return '\r'; 267 | 268 | case 't': 269 | source.consume(3); 270 | return '\t'; 271 | 272 | case '\\': 273 | source.consume(3); 274 | return '\\'; 275 | 276 | case '\'': 277 | source.consume(3); 278 | return '\''; 279 | 280 | case 'x': 281 | chr = peek_val(2); 282 | chr <<= 4; 283 | chr |= peek_val(3); 284 | source.consume(5); 285 | return chr; 286 | } 287 | } else { 288 | source.consume(2); 289 | return chr; 290 | } 291 | } 292 | 293 | fn read_string_literal_token() [len] { 294 | token_type[0] = string_literal; 295 | len = 0; 296 | 297 | loop { 298 | switch(source.peek(0)) { 299 | buffer[len] = source.consume(1); 300 | len += 1; 301 | continue; 302 | case '"': 303 | source.consume(1); 304 | buffer[len] = 0; 305 | token_value[0] = len; 306 | return; 307 | case '\\': 308 | switch(source.peek(1)) { 309 | error("Bad string literal escaped char"); 310 | 311 | case 'n': 312 | buffer[len] = '\n'; 313 | source.consume(2); 314 | len += 1; 315 | continue; 316 | 317 | case 'r': 318 | buffer[len] = '\r'; 319 | source.consume(2); 320 | len += 1; 321 | continue; 322 | 323 | case 't': 324 | buffer[len] = '\t'; 325 | source.consume(2); 326 | len += 1; 327 | continue; 328 | 329 | case '\\': 330 | buffer[len] = '\\'; 331 | source.consume(2); 332 | len += 1; 333 | continue; 334 | 335 | case '\'': 336 | buffer[len] = '\''; 337 | source.consume(2); 338 | len += 1; 339 | continue; 340 | 341 | case 'x': 342 | buffer[len] = peek_val(2); 343 | buffer[len] <<= 4; 344 | buffer[len] |= peek_val(3); 345 | source.consume(4); 346 | len += 1; 347 | continue; 348 | } 349 | } 350 | } 351 | } 352 | 353 | fn fill_token(node) [chr] { 354 | if(token_type[0] == none) { 355 | loop { 356 | token_line[0] = source.current_line[0]; 357 | 358 | switch(source.peek(0)) { 359 | printer.print_string("Unexpected character in fill_token: '"); 360 | chr = source.peek(0); 361 | printer.putchar(chr); 362 | printer.print_string("' (0x"); 363 | printer.print_hex(chr); 364 | printer.print_string(")\n"); 365 | printer.exit(1); 366 | 367 | // EOF 368 | case 0: 369 | token_type[0] = end_of_file; 370 | return; 371 | 372 | // No one cares about whitespace 373 | case '\n': 374 | case '\t': 375 | case ' ': 376 | source.consume(1); 377 | continue; 378 | 379 | case '0': 380 | read_int_literal_token_zero(); 381 | return; 382 | 383 | case '1'...'9': 384 | read_int_literal_token_decimal(); 385 | return; 386 | 387 | case '"': source.consume(1); 388 | read_string_literal_token(); 389 | return; 390 | 391 | case '\'': source.consume(1); 392 | token_value[0] = read_char_literal_token(); 393 | return; 394 | 395 | case '=': 396 | switch(source.peek(1)) { 397 | source.consume(1); 398 | token_type[0] = assignment; 399 | return; 400 | case '=': 401 | source.consume(2); 402 | token_type[0] = equals; 403 | return; 404 | } 405 | 406 | case '!': 407 | switch(source.peek(1)) { 408 | @todo("fill_token (!)"); 409 | case '=': 410 | source.consume(2); 411 | token_type[0] = not_equals; 412 | return; 413 | } 414 | 415 | case '?': 416 | source.consume(1); 417 | token_type[0] = question_mark; 418 | return; 419 | 420 | case ',': 421 | source.consume(1); 422 | token_type[0] = comma; 423 | return; 424 | 425 | case ':': 426 | source.consume(1); 427 | token_type[0] = colon; 428 | return; 429 | 430 | case ';': 431 | source.consume(1); 432 | token_type[0] = semicolon; 433 | return; 434 | 435 | case '.': 436 | if((source.peek(1) == '.') & (source.peek(2) == '.')) { 437 | source.consume(3); 438 | token_type[0] = dot_dot_dot; 439 | return; 440 | } else { 441 | source.consume(1); 442 | token_type[0] = dot; 443 | return; 444 | } 445 | 446 | case '{': 447 | source.consume(1); 448 | token_type[0] = open_curly_brace; 449 | return; 450 | 451 | case '}': 452 | source.consume(1); 453 | token_type[0] = closing_curly_brace; 454 | return; 455 | 456 | case '(': 457 | source.consume(1); 458 | token_type[0] = open_paren; 459 | return; 460 | 461 | case ')': 462 | source.consume(1); 463 | token_type[0] = closing_paren; 464 | return; 465 | 466 | case '[': 467 | source.consume(1); 468 | token_type[0] = open_square_bracket; 469 | return; 470 | 471 | case ']': 472 | source.consume(1); 473 | token_type[0] = closing_square_bracket; 474 | return; 475 | 476 | case '~': 477 | source.consume(1); 478 | token_type[0] = bitnot; 479 | return; 480 | 481 | case '|': 482 | if(source.peek(1) == '=') { 483 | source.consume(2); 484 | token_type[0] = bitor_inplace; 485 | return; 486 | } else { 487 | source.consume(1); 488 | token_type[0] = bitor; 489 | return; 490 | } 491 | 492 | case '&': 493 | if(source.peek(1) == '=') { 494 | source.consume(2); 495 | token_type[0] = bitand_inplace; 496 | return; 497 | } else { 498 | source.consume(1); 499 | token_type[0] = bitand; 500 | return; 501 | } 502 | 503 | case '^': 504 | if(source.peek(1) == '=') { 505 | source.consume(2); 506 | token_type[0] = bitxor_inplace; 507 | return; 508 | } else { 509 | source.consume(1); 510 | token_type[0] = bitxor; 511 | return; 512 | } 513 | 514 | case '%': 515 | if(source.peek(1) == '=') { 516 | source.consume(2); 517 | token_type[0] = modulus_inplace; 518 | return; 519 | } else { 520 | source.consume(1); 521 | token_type[0] = modulus; 522 | return; 523 | } 524 | 525 | case '*': 526 | if(source.peek(1) == '=') { 527 | source.consume(2); 528 | token_type[0] = multiplication_inplace; 529 | return; 530 | } else { 531 | source.consume(1); 532 | token_type[0] = multiplication; 533 | return; 534 | } 535 | 536 | case '+': 537 | if(source.peek(1) == '=') { 538 | source.consume(2); 539 | token_type[0] = addition_inplace; 540 | return; 541 | } else { 542 | source.consume(1); 543 | token_type[0] = addition; 544 | return; 545 | } 546 | 547 | case '-': 548 | if(source.peek(1) == '=') { 549 | source.consume(2); 550 | token_type[0] = subtraction_inplace; 551 | return; 552 | } else { 553 | source.consume(1); 554 | token_type[0] = subtraction; 555 | return; 556 | } 557 | 558 | case '/': 559 | switch(source.peek(1)) { 560 | source.consume(1); 561 | token_type[0] = division; 562 | return; 563 | 564 | case '=': source.consume(2); 565 | token_type[0] = division_inplace; 566 | return; 567 | 568 | case '/': 569 | source.consume(2); 570 | // Line comment like this one 571 | loop { 572 | if(source.consume(1) != '\n') { 573 | continue; 574 | } else { 575 | break; 576 | } 577 | } 578 | continue; 579 | } 580 | 581 | case '<': 582 | switch(source.peek(1)) { 583 | source.consume(1); 584 | token_type[0] = less_than; 585 | return; 586 | 587 | case '<': 588 | if(source.peek(2) == '=') { 589 | source.consume(3); 590 | token_type[0] = shift_left_inplace; 591 | return; 592 | } else { 593 | source.consume(2); 594 | token_type[0] = shift_left; 595 | return; 596 | } 597 | 598 | case '=': 599 | source.consume(2); 600 | token_type[0] = less_than_equal; 601 | return; 602 | } 603 | 604 | case '>': 605 | switch(source.peek(1)) { 606 | source.consume(1); 607 | token_type[0] = greater_than; 608 | return; 609 | 610 | case '>': 611 | if(source.peek(2) == '=') { 612 | source.consume(3); 613 | token_type[0] = shift_right_inplace; 614 | return; 615 | } else { 616 | source.consume(2); 617 | token_type[0] = shift_right; 618 | return; 619 | } 620 | 621 | case '=': 622 | source.consume(2); 623 | token_type[0] = greater_than_equal; 624 | return; 625 | } 626 | 627 | case 'a'...'z': 628 | case 'A'...'Z': 629 | case '@': 630 | case '_': 631 | return parse_identifier(node); 632 | 633 | case 'b': 634 | return attempt_keyword("break", keyword_break, node); 635 | 636 | case 'c': 637 | switch(source.peek(1)) { 638 | return parse_identifier(node); 639 | case 'a': 640 | return attempt_keyword("case", keyword_case, node); 641 | case 'o': 642 | switch(source.peek(2)) { 643 | return parse_identifier(node); 644 | case 'm': 645 | return attempt_keyword("comptime", keyword_comptime, node); 646 | case 'n': 647 | return attempt_keyword("continue", keyword_continue, node); 648 | } 649 | } 650 | 651 | case 'e': 652 | if(source.peek(1) == 'n') { 653 | switch(source.peek(2)) { 654 | return parse_identifier(node); 655 | case 'd': 656 | return attempt_keyword("endcase", keyword_endcase, node); 657 | case 'u': 658 | return attempt_keyword("enum", keyword_enum, node); 659 | } 660 | } else { 661 | return attempt_keyword("else", keyword_else, node); 662 | } 663 | 664 | case 'f': 665 | return attempt_keyword("fn", keyword_fn, node); 666 | 667 | case 'i': 668 | switch(source.peek(1)) { 669 | return parse_identifier(node); 670 | case 'f': 671 | return attempt_keyword("if", keyword_if, node); 672 | case 'm': 673 | return attempt_keyword("import", keyword_import, node); 674 | } 675 | 676 | case 'l': 677 | return attempt_keyword("loop", keyword_loop, node); 678 | 679 | case 'r': 680 | return attempt_keyword("return", keyword_return, node); 681 | 682 | case 's': 683 | return attempt_keyword("switch", keyword_switch, node); 684 | 685 | case 'u': 686 | if(source.peek(1) == 'n') { 687 | switch(source.peek(2)) { 688 | return parse_identifier(node); 689 | case 'd': 690 | return attempt_keyword("undefined", keyword_undefined, node); 691 | case 'r': 692 | return attempt_keyword("unreachable", keyword_unreachable, node); 693 | } 694 | } else { 695 | return parse_identifier(node); 696 | } 697 | 698 | case 'z': 699 | return attempt_keyword("zeroes", keyword_zeroes, node); 700 | } 701 | } 702 | } else { } 703 | } 704 | 705 | fn peek_type(node) { 706 | fill_token(node); 707 | return token_type[0]; 708 | } 709 | 710 | fn expect_token(expected_type, fail_message, node) { 711 | fill_token(node); 712 | if(expected_type == token_type[0]) { 713 | return token_value[0]; 714 | } else { 715 | printer.print_string("Unexpected token type: "); 716 | error(fail_message); 717 | unreachable; 718 | } 719 | } 720 | 721 | fn peek_value(node) { 722 | fill_token(node); 723 | return token_value[0]; 724 | } 725 | 726 | fn discard(node) { 727 | fill_token(node); 728 | token_type[0] = none; 729 | } 730 | -------------------------------------------------------------------------------- /src/writer.co: -------------------------------------------------------------------------------- 1 | // Doesn't affect bss, otherwise we can't compile ourselves :^) 2 | comptime max_section_bytes = 0x100000; 3 | 4 | import "src/syscalls.co" syscalls; 5 | 6 | enum { 7 | text, 8 | data, 9 | rodata, 10 | bss, 11 | 12 | end, 13 | }; 14 | 15 | zeroes section_base_addrs[4 * 8]; 16 | zeroes current_section_bytes[4 * 8]; 17 | zeroes current_section_buf[3 * 8]; 18 | 19 | fn init(binary_base_addr) { 20 | section_base_addrs[0x00] = binary_base_addr + (max_section_bytes * 0); 21 | section_base_addrs[0x08] = binary_base_addr + (max_section_bytes * 1); 22 | section_base_addrs[0x10] = binary_base_addr + (max_section_bytes * 2); 23 | section_base_addrs[0x18] = binary_base_addr + (max_section_bytes * 3); 24 | 25 | current_section_buf[0x00] = syscalls.anon_mmap(max_section_bytes); 26 | current_section_buf[0x08] = syscalls.anon_mmap(max_section_bytes); 27 | current_section_buf[0x10] = syscalls.anon_mmap(max_section_bytes); 28 | } 29 | 30 | fn section_offset(section) { 31 | return current_section_bytes[section * 8]; 32 | } 33 | 34 | fn section_addr(section) { 35 | return section_base_addrs[section * 8] + current_section_bytes[section * 8]; 36 | } 37 | 38 | fn section_base(section) { 39 | return section_base_addrs[section * 8]; 40 | } 41 | 42 | fn patch8(value, section, addr) { 43 | addr -= section_base_addrs[section * 8]; 44 | addr += current_section_buf[section * 8]; 45 | @write8(addr, value); 46 | } 47 | 48 | fn patch16(value, section, addr) { 49 | addr -= section_base_addrs[section * 8]; 50 | addr += current_section_buf[section * 8]; 51 | @write16(addr, value); 52 | } 53 | 54 | fn patch32(value, section, addr) { 55 | addr -= section_base_addrs[section * 8]; 56 | addr += current_section_buf[section * 8]; 57 | @write32(addr, value); 58 | } 59 | 60 | fn patch64(value, section, addr) { 61 | addr -= section_base_addrs[section * 8]; 62 | addr += current_section_buf[section * 8]; 63 | addr[0] = value; 64 | } 65 | 66 | fn write_generic(value, section, num_bytes) [ptr] { 67 | if(section != bss) { 68 | ptr = current_section_buf[section * 8] + section_offset(section); 69 | @memset(ptr, 0, num_bytes); 70 | ptr[0] = value; 71 | } else {} 72 | current_section_bytes[section * 8] += num_bytes; 73 | } 74 | 75 | fn write_string(ptr, len, section) [retval, tmp] { 76 | retval = section_addr(section); 77 | loop { 78 | if(len) { 79 | tmp = @read8(ptr); 80 | write_generic(tmp, section, 1); 81 | ptr += 1; 82 | len -= 1; 83 | continue; 84 | } else { 85 | return retval; 86 | } 87 | } 88 | } 89 | 90 | fn intern_string(ptr, len, section) { 91 | // TODO: Actually find an intern 92 | return write_string(ptr, len, section); 93 | } 94 | 95 | fn code_offset() { 96 | return section_offset(text); 97 | } 98 | 99 | fn code_addr() { 100 | return section_addr(text); 101 | } 102 | -------------------------------------------------------------------------------- /src/x86_64.co: -------------------------------------------------------------------------------- 1 | import "src/builtins.co" builtins; 2 | import "src/codegen.co" codegen; 3 | import "src/compiler.co" compiler; 4 | import "src/identifier_types.co" itypes; 5 | import "src/tokenizer.co" tokenizer; 6 | import "src/writer.co" writer; 7 | 8 | enum { 9 | rax = 0, 10 | rcx = 1, 11 | rdx = 2, 12 | rbx = 3, 13 | rsp = 4, 14 | rbp = 5, 15 | rsi = 6, 16 | rdi = 7, 17 | r8 = 8, 18 | r9 = 9, 19 | r10 = 10, 20 | r11 = 11, 21 | r12 = 12, 22 | r13 = 13, 23 | r14 = 14, 24 | r15 = 15, 25 | }; 26 | 27 | fn code8(value) { 28 | return writer.write_generic(value, writer.text, 1); 29 | } 30 | 31 | fn code16(value) { 32 | return writer.write_generic(value, writer.text, 2); 33 | } 34 | 35 | fn code32(value) { 36 | return writer.write_generic(value, writer.text, 4); 37 | } 38 | 39 | fn code64(value) { 40 | return writer.write_generic(value, writer.text, 8); 41 | } 42 | 43 | fn make_rex(W, R, X, B) { 44 | W <<= 1; 45 | W |= R; 46 | W <<= 1; 47 | W |= X; 48 | W <<= 1; 49 | W |= B; 50 | if(W) { 51 | W |= 0x40; 52 | code8(W); 53 | return; 54 | } else { return; } 55 | } 56 | 57 | fn rexw() { 58 | make_rex(1, 0, 0, 0); 59 | } 60 | 61 | fn rexb() { 62 | make_rex(0, 0, 0, 1); 63 | } 64 | 65 | fn modrm(mod, reg, rm) { 66 | mod <<= 3; 67 | mod |= reg; 68 | mod <<= 3; 69 | mod |= rm; 70 | code8(mod); 71 | } 72 | 73 | fn rep() { 74 | code8(0xF3); 75 | } 76 | 77 | fn op_size() { 78 | code8(0x66); 79 | } 80 | 81 | fn addr_size() { 82 | code8(0x67); 83 | } 84 | 85 | fn prefixes(opsize, regnum) { 86 | @assert(regnum < 0x10); 87 | 88 | regnum = regnum >= 8; 89 | 90 | switch(opsize) { 91 | @todo("x86_64 prefixes bad opsize"); 92 | case 16: 93 | op_size(); 94 | make_rex(0, 0, 0, regnum); 95 | return; 96 | case 32: 97 | make_rex(0, 0, 0, regnum); 98 | return; 99 | case 64: 100 | make_rex(1, 0, 0, regnum); 101 | return; 102 | } 103 | } 104 | 105 | fn prefixes_alt(opsize, regnum) { 106 | @assert(regnum < 0x10); 107 | 108 | regnum = regnum >= 8; 109 | 110 | switch(opsize) { 111 | @todo("x86_64 prefixes bad opsize"); 112 | case 16: 113 | op_size(); 114 | make_rex(0, regnum, 0, 0); 115 | return; 116 | case 32: 117 | make_rex(0, regnum, 0, 0); 118 | return; 119 | case 64: 120 | make_rex(1, regnum, 0, 0); 121 | return; 122 | } 123 | } 124 | 125 | fn movsb() { 126 | code8(0xA4); 127 | } 128 | 129 | fn stosb() { 130 | code8(0xAA); 131 | } 132 | 133 | fn lodsb() { 134 | code8(0xAC); 135 | } 136 | 137 | fn movsw() { 138 | op_size(); 139 | code8(0xA5); 140 | } 141 | 142 | fn stosw() { 143 | op_size(); 144 | code8(0xAB); 145 | } 146 | 147 | fn lodsw() { 148 | op_size(); 149 | code8(0xAD); 150 | } 151 | 152 | fn movsd() { 153 | code8(0xA5); 154 | } 155 | 156 | fn stosd() { 157 | code8(0xAB); 158 | } 159 | 160 | fn lodsd() { 161 | code8(0xAD); 162 | } 163 | 164 | fn movsq() { 165 | rexw(); 166 | code8(0xA5); 167 | } 168 | 169 | fn stosq() { 170 | rexw(); 171 | code8(0xAB); 172 | } 173 | 174 | fn lodsq() { 175 | rexw(); 176 | code8(0xAD); 177 | } 178 | 179 | fn ret() { 180 | code8(0xC3); 181 | } 182 | 183 | fn push_reg(regnum) { 184 | if(regnum >= 8) { 185 | rexb(); 186 | } else { } 187 | regnum &= 0x7; 188 | regnum |= 0x50; 189 | code8(regnum); 190 | } 191 | 192 | fn pop_reg(regnum) { 193 | if(regnum >= 8) { 194 | rexb(); 195 | } else { } 196 | regnum &= 0x7; 197 | regnum |= 0x58; 198 | code8(regnum); 199 | } 200 | 201 | fn load_reg_value(regnum, value) { 202 | prefixes(64, regnum); 203 | regnum &= 0x7; 204 | regnum |= 0xB8; 205 | code8(regnum); 206 | code64(value); 207 | 208 | return 0; 209 | } 210 | 211 | fn write_riprel32_to_here(code_addr) [rip_at_jmp, rel] { 212 | rip_at_jmp = code_addr + 4; 213 | rel = writer.code_addr() - rip_at_jmp; 214 | writer.patch32(rel, writer.text, code_addr); 215 | } 216 | 217 | fn ref_riprel32(addr, extra_offset) { 218 | addr -= writer.code_addr(); 219 | addr -= extra_offset; 220 | addr -= 4; 221 | code32(addr); 222 | } 223 | 224 | fn jmp_to(addr) { 225 | // jmp imm32 226 | code8(0xE9); 227 | ref_riprel32(addr, 0); 228 | } 229 | 230 | fn bitwise_negate() { 231 | prefixes(64, 0); 232 | code8(0xF7); 233 | code8(0xD0); 234 | } 235 | 236 | fn neg_offset(offset) { 237 | offset = -offset; 238 | code32(offset); 239 | } 240 | 241 | fn rmimm_offset_regs(offset, mem_reg, other_reg) { 242 | mem_reg &= 0x7; 243 | other_reg &= 0x7; 244 | 245 | other_reg <<= 3; 246 | 247 | mem_reg |= 0x80; 248 | mem_reg |= other_reg; 249 | 250 | code8(mem_reg); 251 | code32(offset); 252 | } 253 | 254 | fn rmimm_neg_offset_regs(offset, mem_reg, other_reg) { 255 | offset = -offset; 256 | rmimm_offset_regs(offset, mem_reg, other_reg); 257 | } 258 | 259 | fn load_reg_relative_to_reg(dest_reg, src_reg, offset) { 260 | // mov dest_reg, [src_reg + offset] 261 | prefixes(64, 0); 262 | code8(0x8B); 263 | rmimm_offset_regs(offset, src_reg, dest_reg); 264 | } 265 | 266 | fn load_local_variable(regnum, offset) { 267 | // mov reg, qword ptr [rbp - offset] 268 | prefixes_alt(64, regnum); 269 | code8(0x8B); 270 | rmimm_neg_offset_regs(offset, rbp, regnum); 271 | } 272 | 273 | fn ref_local_variable(regnum, offset) { 274 | // lea reg, [rbp - offset] 275 | rexw(); 276 | code8(0x8D); 277 | regnum <<= 3; 278 | regnum |= 0x85; 279 | code8(regnum); 280 | neg_offset(offset); 281 | } 282 | 283 | fn ref_global_variable(regnum, addr) { 284 | // lea regnum, [rel glob] 285 | prefixes(64, regnum); 286 | code8(0x8D); 287 | regnum &= 0x7; 288 | regnum <<= 3; 289 | regnum |= 0x5; 290 | code8(regnum); 291 | ref_riprel32(addr, 0); 292 | } 293 | 294 | fn load_global_variable(regnum, addr) { 295 | // mov regnum, [rel glob] 296 | prefixes(64, regnum); 297 | code8(0x8B); 298 | regnum &= 0x7; 299 | regnum += 0x5; 300 | code8(regnum); 301 | ref_riprel32(addr, 0); 302 | } 303 | 304 | fn put_value_in_reg(regnum, type, value, is_last_evaluated) { 305 | switch(type) { 306 | @todo("put_value_in_reg default type"); 307 | 308 | case itypes.local_variable: 309 | load_local_variable(regnum, value); 310 | return; 311 | 312 | case itypes.local_buffer_addr: 313 | ref_local_variable(regnum, value); 314 | return; 315 | 316 | case itypes.global_variable: 317 | load_global_variable(regnum, value); 318 | return; 319 | 320 | case itypes.global_buffer_addr: 321 | case itypes.function_addr: 322 | ref_global_variable(regnum, value); 323 | return; 324 | 325 | case itypes.comptime_int: 326 | load_reg_value(regnum, value); 327 | return; 328 | 329 | case itypes.runtime_reference: 330 | if(is_last_evaluated) { 331 | load_reg_relative_to_reg(regnum, rax, 0); 332 | return; 333 | } else { 334 | pop_reg(rcx); 335 | load_reg_relative_to_reg(regnum, rcx, 0); 336 | return; 337 | } 338 | 339 | case itypes.runtime_int: 340 | if(is_last_evaluated) { 341 | if(regnum) { 342 | // mov regnum, rax 343 | prefixes(64, regnum); 344 | code8(0x89); 345 | regnum &= 0x7; 346 | regnum += 0xC0; 347 | code8(regnum); 348 | return; 349 | } else { 350 | // Value is already in the register it needs to be 351 | return; 352 | } 353 | } else { 354 | pop_reg(regnum); 355 | return; 356 | } 357 | } 358 | } 359 | 360 | fn put_evaluated_in_reg(regnum) [value, type] { 361 | value = codegen.eval_value[0]; 362 | type = codegen.eval_type[0]; 363 | put_value_in_reg(regnum, type, value, 1); 364 | } 365 | 366 | fn put_arg_in_reg(regnum) { 367 | codegen.eval_returning_expr(codegen.any_unambigous_expr); 368 | put_evaluated_in_reg(regnum); 369 | 370 | if(codegen.peek_type() == tokenizer.comma) { 371 | codegen.discard(); 372 | } else { } 373 | } 374 | 375 | fn put_opt_arg_in_reg(regnum) { 376 | if(codegen.peek_type() == tokenizer.closing_paren) { 377 | } else { 378 | put_arg_in_reg(regnum); 379 | } 380 | } 381 | 382 | fn do_call_addr(addr) { 383 | put_opt_arg_in_reg(rdi); 384 | put_opt_arg_in_reg(rsi); 385 | put_opt_arg_in_reg(rdx); 386 | put_opt_arg_in_reg(rcx); 387 | put_opt_arg_in_reg(r8); 388 | put_opt_arg_in_reg(r9); 389 | 390 | // call imm32 391 | code8(0xE8); 392 | ref_riprel32(addr, 0); 393 | } 394 | 395 | fn return_evaluated() [value, type] { 396 | put_evaluated_in_reg(rax); 397 | 398 | // value = codegen.eval_value[0]; 399 | 400 | // switch(codegen.eval_type[0]) { 401 | // @todo("x86_64 return_evaluated default"); 402 | 403 | // case itypes.local_variable: 404 | // load_local_variable(rax, value); 405 | // endcase; 406 | 407 | // case itypes.global_variable: 408 | // load_global_variable(rax, value); 409 | // endcase; 410 | 411 | // case itypes.comptime_int: 412 | // load_reg_value(rax, value); 413 | 414 | // endcase; 415 | 416 | // case itypes.runtime_reference: 417 | // load_reg_relative_to_reg(rax, rax, 0); 418 | 419 | // case itypes.runtime_int: 420 | // // We're already done, as the value already is in rax 421 | // } 422 | 423 | // mov rsp, rbp 424 | rexw(); 425 | code8(0x89); 426 | code8(0xEC); 427 | 428 | pop_reg(rbp); 429 | 430 | ret(); 431 | } 432 | 433 | fn function_prologue(num_args, local_var_space, stack_frame_size) { 434 | push_reg(rbp); 435 | 436 | // mov rbp, rsp 437 | code8(0x48); 438 | code8(0x89); 439 | code8(0xE5); 440 | 441 | // We don't have arrays yet so this will have to do for now 442 | if(num_args >= 1) { 443 | push_reg(rdi); 444 | if(num_args >= 2) { 445 | push_reg(rsi); 446 | if(num_args >= 3) { 447 | push_reg(rdx); 448 | if(num_args >= 4) { 449 | push_reg(rcx); 450 | if(num_args >= 5) { 451 | push_reg(r8); 452 | if(num_args >= 6) { 453 | push_reg(r9); 454 | if(num_args >= 7) { 455 | @todo("x86_64 too many args"); 456 | unreachable; 457 | } else { } 458 | } else { } 459 | } else { } 460 | } else { } 461 | } else { } 462 | } else { } 463 | } else { } 464 | 465 | if(local_var_space) { 466 | // sub rsp, local_var_space 467 | rexw(); 468 | code8(0x81); 469 | code8(0xEC); 470 | code32(local_var_space); 471 | } else {} 472 | } 473 | 474 | fn builtin_memcpy() [tmp] { 475 | put_arg_in_reg(rdi); 476 | put_arg_in_reg(rsi); 477 | 478 | codegen.eval_returning_expr(codegen.any_unambigous_expr); 479 | 480 | if(codegen.eval_type[0] == itypes.comptime_int) { 481 | tmp = codegen.eval_value[0]; 482 | 483 | if(tmp <= 24) { 484 | loop { 485 | if(tmp >= 8) { 486 | movsq(); 487 | tmp -= 8; 488 | continue; 489 | } else { } 490 | if(tmp >= 4) { 491 | movsd(); 492 | tmp -= 4; 493 | } else {} 494 | if(tmp >= 2) { 495 | movsw(); 496 | tmp -= 2; 497 | } else {} 498 | if(tmp >= 1) { 499 | movsb(); 500 | tmp -= 1; 501 | } else {} 502 | return; 503 | } 504 | } else { 505 | load_reg_value(rcx, tmp); 506 | } 507 | } else { 508 | put_evaluated_in_reg(rcx); 509 | } 510 | 511 | rep(); 512 | movsb(); 513 | } 514 | 515 | fn builtin_memset() [tmp] { 516 | put_arg_in_reg(rdi); 517 | put_arg_in_reg(rax); 518 | put_arg_in_reg(rcx); 519 | 520 | rep(); 521 | stosb(); 522 | } 523 | 524 | fn jmp_riprel8(offset) { 525 | code8(0xEB); 526 | code8(offset); 527 | } 528 | 529 | fn jmp_riprel32(offset) { 530 | code8(0xE9); 531 | code32(offset); 532 | } 533 | 534 | fn builtin_syscall() { 535 | put_arg_in_reg(rax); 536 | put_opt_arg_in_reg(rdi); 537 | put_opt_arg_in_reg(rsi); 538 | put_opt_arg_in_reg(rdx); 539 | put_opt_arg_in_reg(r10); 540 | put_opt_arg_in_reg(r8); 541 | put_opt_arg_in_reg(r9); 542 | 543 | code8(0x0F); 544 | code8(0x05); 545 | } 546 | 547 | fn builtin_call() { 548 | put_arg_in_reg(rax); 549 | put_opt_arg_in_reg(rdi); 550 | put_opt_arg_in_reg(rsi); 551 | put_opt_arg_in_reg(rdx); 552 | put_opt_arg_in_reg(rcx); 553 | put_opt_arg_in_reg(r8); 554 | put_opt_arg_in_reg(r9); 555 | 556 | // call rax 557 | code8(0xFF); 558 | code8(0xD0); 559 | } 560 | 561 | fn make_switch() [switch_table_addr] { 562 | switch_table_addr = writer.section_addr(writer.rodata); 563 | 564 | put_evaluated_in_reg(rax); 565 | ref_global_variable(rdx, switch_table_addr); 566 | 567 | // mov eax, [rdx + rax * 4] 568 | code8(0x8B); 569 | code8(0x04); 570 | code8(0x82); 571 | 572 | // TODO: Skip this if not PIC AND executable base below 4G { 573 | // lea rdx, [rel l] 574 | rexw(); 575 | code8(0x8D); 576 | code8(0x15); 577 | code32(0x0A); 578 | 579 | // add rax, rdx 580 | rexw(); 581 | code8(0x01); 582 | code8(0xD0); 583 | // } 584 | 585 | // jmp rax 586 | code8(0xFF); 587 | code8(0xE0); 588 | 589 | // jmp imm32 590 | code8(0xE9); 591 | code32(0x41414141); 592 | 593 | // l: 594 | 595 | return writer.code_addr() - 5; 596 | } 597 | 598 | fn endcase_to_here(endcase_addr) { 599 | endcase_addr += 1; 600 | write_riprel32_to_here(endcase_addr); 601 | } 602 | 603 | fn make_loop_break() { 604 | jmp_riprel8(5); // Jump past next jmp 605 | 606 | jmp_riprel32(0x41414141); 607 | 608 | return writer.code_addr() - 5; 609 | } 610 | 611 | fn loop_break_to_here(loop_break) { 612 | loop_break += 1; 613 | write_riprel32_to_here(loop_break); 614 | } 615 | 616 | fn if_condition() [value] { 617 | value = codegen.eval_value[0]; 618 | 619 | switch(codegen.eval_type[0]) { 620 | @todo("x86_64 if condition default"); 621 | 622 | case itypes.local_variable: 623 | // cmp qword ptr[rbp - offset], 0 624 | value = -value; 625 | prefixes(64, rbp); 626 | code8(0x83); 627 | code8(0xBD); 628 | code32(value); 629 | code8(0); 630 | 631 | // je else_case 632 | code8(0x0F); 633 | code8(0x84); 634 | code32(0x41414141); 635 | 636 | return writer.code_addr() - 4; 637 | 638 | case itypes.global_variable: 639 | // cmp qword ptr [rel glob], 0 640 | rexw(); 641 | code8(0x83); 642 | code8(0x3D); 643 | ref_riprel32(value, 1); 644 | code8(0); 645 | 646 | // je else_case 647 | code8(0x0F); 648 | code8(0x84); 649 | code32(0x41414141); 650 | 651 | return writer.code_addr() - 4; 652 | 653 | case itypes.runtime_reference: 654 | // mov rax, [rax] 655 | load_reg_relative_to_reg(rax, rax, 0); 656 | 657 | case itypes.runtime_int: 658 | // test rax, rax 659 | rexw(); 660 | code8(0x85); 661 | code8(0xC0); 662 | 663 | // jz else_case 664 | code8(0x0F); 665 | code8(0x84); 666 | code32(0x41414141); 667 | 668 | return writer.code_addr() - 4; 669 | } 670 | } 671 | 672 | fn else_block_start(not_taken_fixup) { 673 | // Argument is the value returned from above 674 | 675 | // jmp endif 676 | code8(0xE9); 677 | code32(0x41414141); 678 | 679 | // Fixup else branch to here 680 | write_riprel32_to_here(not_taken_fixup); 681 | 682 | return writer.code_addr() - 4; 683 | } 684 | 685 | fn else_block_end(jmp_end_fixup) { 686 | write_riprel32_to_here(jmp_end_fixup); 687 | } 688 | 689 | fn store_local_variable(regnum, offset) { 690 | // mov [rbp - offset], regnum 691 | rexw(); 692 | code8(0x89); 693 | rmimm_neg_offset_regs(offset, rbp, regnum); 694 | } 695 | 696 | fn save_eval_value() { 697 | push_reg(rax); 698 | } 699 | 700 | fn add_elf_entry_point(main) [retval] { 701 | retval = writer.code_addr(); 702 | 703 | // argc = [rsp] 704 | // mov rdi, qword ptr [rsp] 705 | rexw(); 706 | code8(0x8B); 707 | code8(0x3C); 708 | code8(0x24); 709 | 710 | // argv = rsp + 8 711 | // lea rsi, [rsp + 8] 712 | rexw(); 713 | code8(0x8D); 714 | code8(0x74); 715 | code8(0x24); 716 | code8(0x08); 717 | 718 | // envp = rsp + (argc + 1) * 8 + 0x8 719 | // lea rdx, [rsp + rdi * 8 + 0x10] 720 | rexw(); 721 | code8(0x8D); 722 | code8(0x54); 723 | code8(0xFC); 724 | code8(0x10); 725 | 726 | jmp_to(main); 727 | 728 | return retval; 729 | } 730 | 731 | fn jmp_to_assert_fail() [addr] { 732 | addr = compiler.assert_fail_addr(); 733 | jmp_to(addr); 734 | } 735 | 736 | fn insert_assert_fail_offset(extra_offset) [addr] { 737 | addr = compiler.assert_fail_addr(); 738 | ref_riprel32(addr, extra_offset); 739 | } 740 | 741 | fn builtin_todo() { 742 | put_opt_arg_in_reg(rdi); 743 | load_reg_value(rsi, 0); 744 | load_reg_value(rdx, 0); 745 | 746 | codegen.expr_returns[0] = 0; 747 | 748 | jmp_to_assert_fail(); 749 | } 750 | 751 | fn builtin_panic() { 752 | put_opt_arg_in_reg(rdi); 753 | load_reg_value(rsi, 0); 754 | load_reg_value(rdx, 0); 755 | 756 | codegen.expr_returns[0] = 0; 757 | 758 | jmp_to_assert_fail(); 759 | } 760 | 761 | fn builtin_assert() { 762 | codegen.eval_expr(codegen.any_unambigous_expr); 763 | put_evaluated_in_reg(rax); 764 | put_opt_arg_in_reg(rdi); 765 | load_reg_value(rsi, 0); 766 | load_reg_value(rdx, 0); 767 | 768 | // test rax, rax 769 | rexw(); 770 | code8(0x85); 771 | code8(0xC0); 772 | 773 | // jz assert_fail 774 | code8(0x0F); 775 | code8(0x84); 776 | insert_assert_fail_offset(0); 777 | } 778 | 779 | fn builtin_write8() { 780 | put_arg_in_reg(rdi); 781 | put_arg_in_reg(rax); 782 | stosb(); 783 | } 784 | 785 | fn builtin_write16() { 786 | put_arg_in_reg(rdi); 787 | put_arg_in_reg(rax); 788 | stosw(); 789 | } 790 | 791 | fn builtin_write32() { 792 | put_arg_in_reg(rdi); 793 | put_arg_in_reg(rax); 794 | stosd(); 795 | } 796 | 797 | fn builtin_read8() { 798 | put_arg_in_reg(rsi); 799 | load_reg_value(rax, 0); // Clear upper bits 800 | lodsb(); 801 | } 802 | 803 | fn builtin_read16() { 804 | put_arg_in_reg(rsi); 805 | load_reg_value(rax, 0); // Clear upper bits 806 | lodsw(); 807 | } 808 | 809 | fn builtin_read32() { 810 | put_arg_in_reg(rsi); 811 | load_reg_value(rax, 0); // Clear upper bits 812 | lodsd(); 813 | } 814 | 815 | fn do_unary_op(op, operand_type, operand_value) { 816 | // The operand can't be of type comptime_int. If it is the value should already be known 817 | @assert(operand_type != itypes.comptime_int); 818 | 819 | put_value_in_reg(rax, operand_type, operand_value, 1); 820 | 821 | switch(op) { 822 | @todo("x86_64 do_unary_op default"); 823 | case tokenizer.subtraction: 824 | // neg rax 825 | rexw(); 826 | code8(0xF7); 827 | code8(0xD8); 828 | return; 829 | 830 | case tokenizer.bitnot: 831 | // not rax 832 | rexw(); 833 | code8(0xF7); 834 | code8(0xD0); 835 | return; 836 | } 837 | } 838 | 839 | fn do_binary_op(lhs_type, lhs_value, op, rhs_type, rhs_value) { 840 | switch(lhs_type) { 841 | endcase; 842 | 843 | case itypes.comptime_int: 844 | // Just load them into registers, can't do much about that. 845 | load_reg_value(rcx, lhs_value); 846 | lhs_type = itypes.runtime_int; 847 | endcase; 848 | 849 | case itypes.runtime_reference: 850 | if(codegen.lhs_not_saved(lhs_type, rhs_type)) { 851 | load_reg_relative_to_reg(rax, rax, 0); // deref it 852 | } else { 853 | pop_reg(rcx); 854 | load_reg_relative_to_reg(rcx, rcx, 0); // deref it 855 | } 856 | lhs_type = itypes.runtime_int; 857 | endcase; 858 | 859 | case itypes.runtime_int: 860 | if(codegen.lhs_not_saved(lhs_type, rhs_type)) { 861 | } else { 862 | pop_reg(rcx); 863 | } 864 | // endcase; 865 | } 866 | 867 | if(rhs_type == itypes.comptime_int) { 868 | switch(op) { 869 | @todo("x86_64 do_binary_op comptime default op"); 870 | 871 | case tokenizer.addition: 872 | put_value_in_reg(rax, lhs_type, lhs_value, 1); 873 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 874 | // add rax, imm32 875 | rexw(); 876 | code8(0x05); 877 | code32(rhs_value); 878 | return; 879 | } else { 880 | @todo("x86_64 do_binary_op comptime addition large"); 881 | } 882 | 883 | case tokenizer.subtraction: 884 | put_value_in_reg(rax, lhs_type, lhs_value, 1); 885 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 886 | // sub rax, imm32 887 | rexw(); 888 | code8(0x2D); 889 | code32(rhs_value); 890 | return; 891 | } else { 892 | @todo("x86_64 do_binary_op comptime subtraction large"); 893 | } 894 | 895 | case tokenizer.multiplication: 896 | put_value_in_reg(rax, lhs_type, lhs_value, 1); 897 | if(rhs_value == 8) { // Most common multiplication is by 8 (in array subscript) 898 | // shl rax, 3 899 | rexw(); 900 | code8(0xC1); 901 | code8(0xE0); 902 | code8(0x03); 903 | return; 904 | } else { 905 | load_reg_value(rcx, rhs_value); 906 | // mul rcx 907 | rexw(); 908 | code8(0xF7); 909 | code8(0xE1); 910 | return; 911 | } 912 | 913 | case tokenizer.modulus: 914 | @todo("x86_64 do_binary_op comptime modulus"); 915 | 916 | case tokenizer.bitor: 917 | @todo("x86_64 do_binary_op comptime bitor"); 918 | 919 | case tokenizer.bitand: 920 | @todo("x86_64 do_binary_op comptime bitand"); 921 | 922 | case tokenizer.bitxor: 923 | @todo("x86_64 do_binary_op comptime bitxor"); 924 | 925 | case tokenizer.less_than: 926 | case tokenizer.less_than_equal: 927 | case tokenizer.greater_than: 928 | case tokenizer.greater_than_equal: 929 | case tokenizer.equals: 930 | case tokenizer.not_equals: 931 | switch(lhs_type) { 932 | @todo("x86_64 do_binary_op comptime compare default lhs"); 933 | 934 | case itypes.runtime_reference: 935 | load_reg_relative_to_reg(rax, rax, 0); 936 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 937 | // cmp rax, imm32 938 | rexw(); 939 | code8(0x3D); 940 | code32(rhs_value); 941 | endcase; 942 | } else { 943 | @todo("x86_64 do_binary_op comptime compare runtime_reference large"); 944 | } 945 | 946 | 947 | case itypes.runtime_int: 948 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 949 | // cmp rax, imm32 950 | rexw(); 951 | code8(0x3D); 952 | code32(rhs_value); 953 | endcase; 954 | } else { 955 | @todo("x86_64 do_binary_op comptime compare runtime_int large"); 956 | } 957 | 958 | case itypes.local_variable: 959 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 960 | // cmp [rbp - offset], imm32 961 | rexw(); 962 | code8(0x81); 963 | code8(0xBD); 964 | neg_offset(lhs_value); 965 | code32(rhs_value); 966 | endcase; 967 | } else { 968 | @todo("x86_64 do_binary_op comptime compare local_variable large"); 969 | } 970 | 971 | case itypes.global_variable: 972 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 973 | // cmp [rel glob], imm32 974 | rexw(); 975 | code8(0x81); 976 | code8(0x3D); 977 | ref_riprel32(lhs_value, 4); 978 | code32(rhs_value); 979 | endcase; 980 | } else { 981 | @todo("x86_64 do_binary_op comptime compare global_variable large"); 982 | } 983 | } 984 | // endcase; 985 | } 986 | } else { 987 | switch(op) { 988 | @todo("x86_64 do_binary_op runtime default op"); 989 | 990 | case tokenizer.modulus: 991 | case tokenizer.division: 992 | load_reg_value(rdx, 0); // Upper half 993 | case tokenizer.addition: 994 | case tokenizer.subtraction: 995 | case tokenizer.multiplication: 996 | case tokenizer.bitand: 997 | case tokenizer.bitor: 998 | case tokenizer.bitxor: 999 | case tokenizer.shift_left: 1000 | case tokenizer.shift_right: 1001 | put_value_in_reg(rcx, rhs_type, rhs_value, 1); 1002 | put_value_in_reg(rax, lhs_type, lhs_value, 0); 1003 | endcase; 1004 | 1005 | case tokenizer.less_than: 1006 | case tokenizer.less_than_equal: 1007 | case tokenizer.greater_than: 1008 | case tokenizer.greater_than_equal: 1009 | case tokenizer.equals: 1010 | case tokenizer.not_equals: 1011 | switch(lhs_type) { 1012 | @todo("x86_64 do_inplace_op runtime compare default lhs"); 1013 | 1014 | case itypes.runtime_int: 1015 | put_value_in_reg(rcx, lhs_type, lhs_value, 1); 1016 | put_value_in_reg(rax, rhs_type, rhs_value, 1); 1017 | 1018 | // cmp rcx, rax 1019 | rexw(); 1020 | code8(0x39); 1021 | code8(0xC1); 1022 | endcase; 1023 | 1024 | case itypes.local_variable: 1025 | put_value_in_reg(rax, rhs_type, rhs_value, 1); 1026 | // cmp [rbp - offset], rax 1027 | rexw(); 1028 | code8(0x39); 1029 | rmimm_neg_offset_regs(lhs_value, rbp, rax); 1030 | // endcase; 1031 | } 1032 | // endcase; 1033 | } 1034 | 1035 | } 1036 | 1037 | switch(op) { 1038 | case tokenizer.addition: 1039 | // add rax, rcx 1040 | rexw(); 1041 | code8(0x01); 1042 | code8(0xC8); 1043 | return; 1044 | 1045 | case tokenizer.subtraction: 1046 | // sub rax, rcx 1047 | rexw(); 1048 | code8(0x29); 1049 | code8(0xC8); 1050 | return; 1051 | 1052 | case tokenizer.multiplication: 1053 | // mul rcx 1054 | rexw(); 1055 | code8(0xF7); 1056 | code8(0xE1); 1057 | return; 1058 | 1059 | case tokenizer.modulus: 1060 | // div rcx 1061 | rexw(); 1062 | code8(0xF7); 1063 | code8(0xF1); 1064 | 1065 | // mov rax, rdx 1066 | rexw(); 1067 | code8(0x89); 1068 | code8(0xD0); 1069 | return; 1070 | 1071 | case tokenizer.division: 1072 | // div rcx 1073 | rexw(); 1074 | code8(0xF7); 1075 | code8(0xF1); 1076 | return; 1077 | 1078 | case tokenizer.bitand: 1079 | // and rax, rcx 1080 | rexw(); 1081 | code8(0x21); 1082 | code8(0xC8); 1083 | return; 1084 | 1085 | case tokenizer.bitor: 1086 | // or rax, rcx 1087 | rexw(); 1088 | code8(0x09); 1089 | code8(0xC8); 1090 | return; 1091 | 1092 | case tokenizer.bitxor: 1093 | // xor rax, rcx 1094 | rexw(); 1095 | code8(0x31); 1096 | code8(0xC8); 1097 | return; 1098 | 1099 | case tokenizer.shift_left: 1100 | @todo("shl rax, cl"); 1101 | 1102 | case tokenizer.shift_right: 1103 | @todo("shr rax, cl"); 1104 | 1105 | case tokenizer.less_than: 1106 | // setb al 1107 | code8(0x0F); 1108 | code8(0x92); 1109 | code8(0xC0); 1110 | endcase; 1111 | 1112 | case tokenizer.greater_than_equal: 1113 | // setae al 1114 | code8(0x0F); 1115 | code8(0x93); 1116 | code8(0xC0); 1117 | endcase; 1118 | 1119 | case tokenizer.equals: 1120 | // sete al 1121 | code8(0x0F); 1122 | code8(0x94); 1123 | code8(0xC0); 1124 | endcase; 1125 | 1126 | case tokenizer.not_equals: 1127 | // setne al 1128 | code8(0x0F); 1129 | code8(0x95); 1130 | code8(0xC0); 1131 | endcase; 1132 | 1133 | case tokenizer.less_than_equal: 1134 | // setbe al 1135 | code8(0x0F); 1136 | code8(0x96); 1137 | code8(0xC0); 1138 | endcase; 1139 | 1140 | case tokenizer.greater_than: 1141 | // seta al 1142 | code8(0x0F); 1143 | code8(0x97); 1144 | code8(0xC0); 1145 | // endcase; 1146 | } 1147 | 1148 | // movzx rax, al 1149 | rexw(); 1150 | code8(0x0F); 1151 | code8(0xB6); 1152 | code8(0xC0); 1153 | return; 1154 | } 1155 | 1156 | fn do_inplace_op(lhs_type, lhs_value, op, rhs_type, rhs_value) { 1157 | if(rhs_type == itypes.comptime_int) { 1158 | switch(op) { 1159 | @todo("x86_64 do_inplace_op comptime default op"); 1160 | 1161 | case tokenizer.assignment: 1162 | switch(lhs_type) { 1163 | case itypes.global_variable: 1164 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1165 | // mov qword ptr [rbp - offset], imm32 1166 | rexw(); 1167 | code8(0xC7); 1168 | code8(0x05); 1169 | ref_riprel32(lhs_value, 4); 1170 | code32(rhs_value); 1171 | return; 1172 | } else { 1173 | @todo("x86_64 do_inplace_op comptime assignment global_variable large"); 1174 | } 1175 | 1176 | case itypes.local_variable: 1177 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1178 | // mov qword ptr [rbp - offset], imm32 1179 | rexw(); 1180 | code8(0xC7); 1181 | code8(0x85); 1182 | neg_offset(lhs_value); 1183 | code32(rhs_value); 1184 | return; 1185 | } else { 1186 | @todo("x86_64 do_inplace_op comptime assignment local_variable large"); 1187 | } 1188 | 1189 | case itypes.runtime_reference: 1190 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1191 | @assert(codegen.lhs_not_saved(lhs_type, rhs_type)); 1192 | 1193 | // mov qword[rax], imm32 1194 | rexw(); 1195 | code8(0xC7); 1196 | code8(0x00); 1197 | code32(rhs_value); 1198 | return; 1199 | } else { 1200 | @todo("x86_64 do_inplace_op comptime assignment runtime_reference large"); 1201 | } 1202 | } 1203 | 1204 | case tokenizer.addition_inplace: 1205 | switch(lhs_type) { 1206 | case itypes.global_variable: 1207 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1208 | // add qword ptr [rel glob], imm32 1209 | rexw(); 1210 | code8(0x81); 1211 | code8(0x05); 1212 | ref_riprel32(lhs_value, 4); 1213 | code32(rhs_value); 1214 | return; 1215 | } else { 1216 | @todo("x86_64 do_inplace_op comptime addition global_variable large"); 1217 | } 1218 | 1219 | case itypes.local_variable: 1220 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1221 | // add qword ptr [rbp - offset], imm32 1222 | rexw(); 1223 | code8(0x81); 1224 | code8(0x85); 1225 | neg_offset(lhs_value); 1226 | code32(rhs_value); 1227 | return; 1228 | } else { 1229 | @todo("x86_64 do_inplace_op comptime addition local_variable large"); 1230 | } 1231 | 1232 | case itypes.runtime_reference: 1233 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1234 | @assert(codegen.lhs_not_saved(lhs_type, rhs_type)); 1235 | 1236 | // add qword[rax], imm32 1237 | rexw(); 1238 | code8(0x81); 1239 | code8(0x00); 1240 | code32(rhs_value); 1241 | return; 1242 | } else { 1243 | @todo("x86_64 do_inplace_op comptime addition runtime_reference large"); 1244 | } 1245 | } 1246 | 1247 | case tokenizer.subtraction_inplace: 1248 | switch(lhs_type) { 1249 | case itypes.global_variable: 1250 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1251 | // sub qword ptr [rel glob], imm32 1252 | rexw(); 1253 | code8(0x81); 1254 | code8(0x2D); 1255 | ref_riprel32(lhs_value, 4); 1256 | code32(rhs_value); 1257 | return; 1258 | } else { 1259 | @todo("x86_64 do_inplace_op comptime subtraction global_variable large"); 1260 | } 1261 | 1262 | case itypes.local_variable: 1263 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1264 | // sub qword ptr [rbp - offset], imm32 1265 | rexw(); 1266 | code8(0x81); 1267 | code8(0xAD); 1268 | neg_offset(lhs_value); 1269 | code32(rhs_value); 1270 | return; 1271 | } else { 1272 | @todo("x86_64 do_inplace_op comptime subtraction local_variable large"); 1273 | } 1274 | 1275 | case itypes.runtime_reference: 1276 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1277 | @assert(codegen.lhs_not_saved(lhs_type, rhs_type)); 1278 | 1279 | // sub qword[rax], imm32 1280 | rexw(); 1281 | code8(0x81); 1282 | code8(0x28); 1283 | code32(rhs_value); 1284 | return; 1285 | } else { 1286 | @todo("x86_64 do_inplace_op comptime subtraction runtime_reference large"); 1287 | } 1288 | } 1289 | 1290 | case tokenizer.bitand_inplace: 1291 | switch(lhs_type) { 1292 | case itypes.global_variable: 1293 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1294 | // and qword ptr [rel glob], imm32 1295 | rexw(); 1296 | code8(0x81); 1297 | code8(0x25); 1298 | ref_riprel32(lhs_value, 4); 1299 | code32(rhs_value); 1300 | return; 1301 | } else { 1302 | @todo("x86_64 do_inplace_op comptime bitand local_variable large"); 1303 | } 1304 | 1305 | case itypes.local_variable: 1306 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1307 | // and qword ptr [rbp - offset], imm32 1308 | rexw(); 1309 | code8(0x81); 1310 | code8(0xA5); 1311 | neg_offset(lhs_value); 1312 | code32(rhs_value); 1313 | return; 1314 | } else { 1315 | @todo("x86_64 do_inplace_op comptime bitand local_variable large"); 1316 | } 1317 | 1318 | case itypes.runtime_reference: 1319 | @todo("x86_64 do_inplace_op comptime bitand runtime_reference"); 1320 | } 1321 | 1322 | case tokenizer.bitor_inplace: 1323 | switch(lhs_type) { 1324 | case itypes.global_variable: 1325 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1326 | // or qword ptr [rel glob], imm32 1327 | rexw(); 1328 | code8(0x81); 1329 | code8(0x0D); 1330 | ref_riprel32(lhs_value, 4); 1331 | code32(rhs_value); 1332 | return; 1333 | } else { 1334 | @todo("x86_64 do_inplace_op comptime bitor global_variable large"); 1335 | } 1336 | 1337 | case itypes.local_variable: 1338 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1339 | // or qword ptr [rbp - offset], imm32 1340 | rexw(); 1341 | code8(0x81); 1342 | code8(0x8D); 1343 | neg_offset(lhs_value); 1344 | code32(rhs_value); 1345 | return; 1346 | } else { 1347 | @todo("x86_64 do_inplace_op comptime bitor local_variable large"); 1348 | } 1349 | 1350 | case itypes.runtime_reference: 1351 | if((rhs_value <= 0x7FFFFFFF) | (rhs_value >= 0xFFFFFFFF80000000)) { 1352 | @assert(codegen.lhs_not_saved(lhs_type, rhs_type)); 1353 | 1354 | // or qword[rax], imm32 1355 | rexw(); 1356 | code8(0x81); 1357 | code8(0x08); 1358 | code32(rhs_value); 1359 | return; 1360 | } else { 1361 | @todo("x86_64 do_inplace_op comptime bitor runtime_reference large"); 1362 | } 1363 | } 1364 | 1365 | 1366 | case tokenizer.shift_right_inplace: 1367 | switch(lhs_type) { 1368 | case itypes.global_variable: 1369 | @todo("x86_64 do_inplace_op comptime shift_right global_variable"); 1370 | 1371 | case itypes.local_variable: 1372 | // shr qword[rbp - offset], rhs_value 1373 | rexw(); 1374 | code8(0xC1); 1375 | code8(0xAD); 1376 | neg_offset(lhs_value, 1); 1377 | code8(rhs_value); 1378 | return; 1379 | 1380 | case itypes.runtime_reference: 1381 | @todo("x86_64 do_inplace_op comptime shift_right runtime_reference"); 1382 | } 1383 | 1384 | case tokenizer.shift_left_inplace: 1385 | switch(lhs_type) { 1386 | case itypes.global_variable: 1387 | // shl qword[rel glob], rhs_value 1388 | rexw(); 1389 | code8(0xC1); 1390 | code8(0x25); 1391 | ref_riprel32(lhs_value, 1); 1392 | code8(rhs_value); 1393 | return; 1394 | 1395 | case itypes.local_variable: 1396 | // shl qword[rbp - offset], rhs_value 1397 | rexw(); 1398 | code8(0xC1); 1399 | code8(0xA5); 1400 | neg_offset(lhs_value); 1401 | code8(rhs_value); 1402 | return; 1403 | 1404 | case itypes.runtime_reference: 1405 | @assert(codegen.lhs_not_saved(lhs_type, rhs_type)); 1406 | 1407 | // shl [rax], rhs_value 1408 | rexw(); 1409 | code8(0xC1); 1410 | code8(0x20); 1411 | code8(rhs_value); 1412 | return; 1413 | } 1414 | 1415 | case tokenizer.modulus_inplace: 1416 | switch(lhs_type) { 1417 | case itypes.global_variable: 1418 | @todo("x86_64 do_inplace_op comptime modulus global_variable"); 1419 | 1420 | case itypes.local_variable: 1421 | load_reg_value(rdx, 0); // Upper half 1422 | load_local_variable(rax, lhs_value); // Lower half 1423 | load_reg_value(rcx, rhs_value); 1424 | 1425 | // div rcx 1426 | rexw(); 1427 | code8(0xF7); 1428 | code8(0xF1); 1429 | 1430 | store_local_variable(rdx, lhs_value); 1431 | return; 1432 | 1433 | case itypes.runtime_reference: 1434 | @todo("x86_64 do_inplace_op comptime modulus runtime_reference"); 1435 | } 1436 | 1437 | case tokenizer.division_inplace: 1438 | switch(lhs_type) { 1439 | case itypes.global_variable: 1440 | @todo("x86_64 do_inplace_op comptime division global_variable"); 1441 | 1442 | case itypes.local_variable: 1443 | load_reg_value(rdx, 0); // Upper half 1444 | load_local_variable(rax, lhs_value); // Lower half 1445 | load_reg_value(rcx, rhs_value); 1446 | 1447 | // div rcx 1448 | rexw(); 1449 | code8(0xF7); 1450 | code8(0xF1); 1451 | 1452 | store_local_variable(rax, lhs_value); 1453 | return; 1454 | 1455 | case itypes.runtime_reference: 1456 | @todo("x86_64 do_inplace_op comptime division runtime_reference"); 1457 | } 1458 | 1459 | case tokenizer.multiplication_inplace: 1460 | switch(lhs_type) { 1461 | case itypes.global_variable: 1462 | load_global_variable(rax, lhs_value); 1463 | load_reg_value(rcx, rhs_value); 1464 | 1465 | // mul rcx 1466 | rexw(); 1467 | code8(0xF7); 1468 | code8(0xE1); 1469 | 1470 | do_inplace_op(itypes.global_variable, lhs_value, tokenizer.assignment, itypes.runtime_int); 1471 | return; 1472 | 1473 | case itypes.local_variable: 1474 | load_local_variable(rax, lhs_value); 1475 | load_reg_value(rcx, rhs_value); 1476 | 1477 | // mul rcx 1478 | rexw(); 1479 | code8(0xF7); 1480 | code8(0xE1); 1481 | 1482 | store_local_variable(rax, lhs_value); 1483 | return; 1484 | 1485 | case itypes.runtime_reference: 1486 | @todo("x86_64 do_inplace_op comptime multiplication runtime_reference"); 1487 | } 1488 | } 1489 | } else { 1490 | put_evaluated_in_reg(rax); 1491 | switch(op) { 1492 | @todo("x86_64 do_inplace_op runtime default op"); 1493 | 1494 | case tokenizer.assignment: 1495 | switch(lhs_type) { 1496 | case itypes.global_variable: 1497 | // mov [rel glob], rax 1498 | rexw(); 1499 | code8(0x89); 1500 | code8(0x05); 1501 | ref_riprel32(lhs_value, 0); 1502 | return; 1503 | 1504 | case itypes.local_variable: 1505 | store_local_variable(rax, lhs_value); 1506 | return; 1507 | 1508 | case itypes.runtime_reference: 1509 | pop_reg(rcx); 1510 | 1511 | // mov [rcx], rax 1512 | rexw(); 1513 | code8(0x89); 1514 | code8(0x01); 1515 | return; 1516 | } 1517 | 1518 | case tokenizer.addition_inplace: 1519 | switch(lhs_type) { 1520 | case itypes.global_variable: 1521 | // add [rel glob], rax 1522 | rexw(); 1523 | code8(0x01); 1524 | code8(0x05); 1525 | ref_riprel32(lhs_value, 0); 1526 | return; 1527 | 1528 | case itypes.local_variable: 1529 | // add [rbp - offset], rax 1530 | rexw(); 1531 | code8(0x01); 1532 | rmimm_neg_offset_regs(lhs_value, rbp, rax); 1533 | return; 1534 | 1535 | case itypes.runtime_reference: 1536 | pop_reg(rcx); 1537 | 1538 | // add [rcx], rax 1539 | rexw(); 1540 | code8(0x01); 1541 | code8(0x01); 1542 | return; 1543 | } 1544 | 1545 | case tokenizer.subtraction_inplace: 1546 | switch(lhs_type) { 1547 | case itypes.global_variable: 1548 | // sub [rel glob], rax 1549 | rexw(); 1550 | code8(0x29); 1551 | code8(0x05); 1552 | ref_riprel32(lhs_value, 0); 1553 | return; 1554 | 1555 | case itypes.local_variable: 1556 | // sub [rbp - offset], rax 1557 | rexw(); 1558 | code8(0x29); 1559 | rmimm_neg_offset_regs(lhs_value, rbp, rax); 1560 | return; 1561 | 1562 | case itypes.runtime_reference: 1563 | pop_reg(rcx); 1564 | 1565 | // sub [rcx], rax 1566 | rexw(); 1567 | code8(0x29); 1568 | code8(0x01); 1569 | return; 1570 | } 1571 | 1572 | case tokenizer.bitor_inplace: 1573 | switch(lhs_type) { 1574 | case itypes.global_variable: 1575 | // or [rel glob], rax 1576 | rexw(); 1577 | code8(0x09); 1578 | code8(0x05); 1579 | ref_riprel32(lhs_value, 0); 1580 | return; 1581 | 1582 | case itypes.local_variable: 1583 | // or [rbp - offset], rax 1584 | rexw(); 1585 | code8(0x09); 1586 | rmimm_neg_offset_regs(lhs_value, rbp, rax); 1587 | return; 1588 | 1589 | case itypes.runtime_reference: 1590 | pop_reg(rcx); 1591 | 1592 | // or [rcx], rax 1593 | rexw(); 1594 | code8(0x09); 1595 | code8(0x01); 1596 | return; 1597 | } 1598 | 1599 | case tokenizer.bitand_inplace: 1600 | switch(lhs_type) { 1601 | case itypes.global_variable: 1602 | // and [rel glob], rax 1603 | rexw(); 1604 | code8(0x21); 1605 | code8(0x05); 1606 | ref_riprel32(lhs_value, 0); 1607 | return; 1608 | 1609 | case itypes.local_variable: 1610 | // and [rbp - offset], rax 1611 | rexw(); 1612 | code8(0x21); 1613 | rmimm_neg_offset_regs(lhs_value, rbp, rax); 1614 | return; 1615 | 1616 | case itypes.runtime_reference: 1617 | pop_reg(rcx); 1618 | 1619 | // and [rcx], rax 1620 | rexw(); 1621 | code8(0x21); 1622 | code8(0x01); 1623 | return; 1624 | } 1625 | 1626 | case tokenizer.bitxor_inplace: 1627 | switch(lhs_type) { 1628 | case itypes.global_variable: 1629 | // xor [rel glob], rax 1630 | rexw(); 1631 | code8(0x31); 1632 | code8(0x05); 1633 | ref_riprel32(lhs_value, 0); 1634 | return; 1635 | 1636 | case itypes.local_variable: 1637 | // xor [rbp - offset], rax 1638 | rexw(); 1639 | code8(0x31); 1640 | rmimm_neg_offset_regs(lhs_value, rbp, rax); 1641 | return; 1642 | 1643 | case itypes.runtime_reference: 1644 | pop_reg(rcx); 1645 | 1646 | // xor [rcx], rax 1647 | rexw(); 1648 | code8(0x31); 1649 | code8(0x01); 1650 | return; 1651 | } 1652 | 1653 | case tokenizer.multiplication_inplace: 1654 | switch(lhs_type) { 1655 | case itypes.global_variable: 1656 | @todo("x86_64 do_inplace_op runtime multiplication global_variable"); 1657 | 1658 | case itypes.local_variable: 1659 | load_local_variable(rcx, lhs_value); 1660 | 1661 | // mul rcx 1662 | rexw(); 1663 | code8(0xF7); 1664 | code8(0xE1); 1665 | 1666 | store_local_variable(rax, lhs_value); 1667 | return; 1668 | 1669 | case itypes.runtime_reference: 1670 | @todo("x86_64 do_inplace_op runtime multiplication runtime_reference"); 1671 | } 1672 | } 1673 | } 1674 | } 1675 | 1676 | fn init() { 1677 | // Function prologue and epilogue 1678 | codegen.function_prologue_ptr[0] = function_prologue; 1679 | codegen.return_evaluated_ptr[0] = return_evaluated; 1680 | codegen.add_elf_entry_point_ptr[0] = add_elf_entry_point; 1681 | 1682 | // Control flow 1683 | codegen.jmp_to_ptr[0] = jmp_to; 1684 | codegen.do_call_addr_ptr[0] = do_call_addr; 1685 | codegen.make_switch_ptr[0] = make_switch; 1686 | codegen.endcase_to_here_ptr[0] = endcase_to_here; 1687 | codegen.make_loop_break_ptr[0] = make_loop_break; 1688 | codegen.loop_break_to_here_ptr[0] = loop_break_to_here; 1689 | codegen.if_condition_ptr[0] = if_condition; 1690 | codegen.else_block_start_ptr[0] = else_block_start; 1691 | codegen.else_block_end_ptr[0] = else_block_end; 1692 | 1693 | // Expr eval 1694 | codegen.save_eval_value_ptr[0] = save_eval_value; 1695 | codegen.do_unary_op_ptr[0] = do_unary_op; 1696 | codegen.do_binary_op_ptr[0] = do_binary_op; 1697 | codegen.do_inplace_op_ptr[0] = do_inplace_op; 1698 | 1699 | builtins.add_builtin("memcpy", builtin_memcpy, itypes.builtin_function_codegen); 1700 | builtins.add_builtin("memset", builtin_memset, itypes.builtin_function_codegen); 1701 | builtins.add_builtin("syscall", builtin_syscall, itypes.builtin_function_codegen); 1702 | builtins.add_builtin("call", builtin_call, itypes.builtin_function_codegen); 1703 | 1704 | builtins.add_builtin("write8", builtin_write8, itypes.builtin_function_codegen); 1705 | builtins.add_builtin("write16", builtin_write16, itypes.builtin_function_codegen); 1706 | builtins.add_builtin("write32", builtin_write32, itypes.builtin_function_codegen); 1707 | 1708 | builtins.add_builtin("read8", builtin_read8, itypes.builtin_function_codegen); 1709 | builtins.add_builtin("read16", builtin_read16, itypes.builtin_function_codegen); 1710 | builtins.add_builtin("read32", builtin_read32, itypes.builtin_function_codegen); 1711 | 1712 | builtins.add_builtin("assert", builtin_assert, itypes.builtin_function_codegen); 1713 | builtins.add_builtin("todo", builtin_todo, itypes.builtin_function_codegen); 1714 | builtins.add_builtin("panic", builtin_panic, itypes.builtin_function_codegen); 1715 | } 1716 | --------------------------------------------------------------------------------