diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-28 16:11:22 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-28 16:11:22 +0200 |
commit | 74d361639148feceb67f8a3db22856c80b048b63 (patch) | |
tree | 13b844702a071e88075ee15311610c8274d799f8 | |
parent | a723c431f7674de916997df5ed4ce4026a244e07 (diff) | |
download | bdl-74d361639148feceb67f8a3db22856c80b048b63.tar.gz bdl-74d361639148feceb67f8a3db22856c80b048b63.zip |
Add compilation of binary logic expressions
-rw-r--r-- | src/compiler.c | 55 | ||||
-rw-r--r-- | src/main.c | 13 | ||||
-rw-r--r-- | src/vm.c | 47 | ||||
-rw-r--r-- | tests/compilation.bad | 7 |
4 files changed, 106 insertions, 16 deletions
diff --git a/src/compiler.c b/src/compiler.c index bc875fd..51d7cd6 100644 --- a/src/compiler.c +++ b/src/compiler.c | |||
@@ -20,6 +20,7 @@ typedef union Constant { | |||
20 | } Constant; | 20 | } Constant; |
21 | 21 | ||
22 | typedef struct Chunk { | 22 | typedef struct Chunk { |
23 | sz id; | ||
23 | Instruction *code; | 24 | Instruction *code; |
24 | 25 | ||
25 | // Constant values that fit in 64 bits. | 26 | // Constant values that fit in 64 bits. |
@@ -94,6 +95,23 @@ typedef enum OpCode { | |||
94 | OP_MOV16, // mov16 rx, ra -> rx = ra & 0xFFFF | 95 | OP_MOV16, // mov16 rx, ra -> rx = ra & 0xFFFF |
95 | OP_MOV32, // mov32 rx, ra -> rx = ra & 0xFFFFFFFF | 96 | OP_MOV32, // mov32 rx, ra -> rx = ra & 0xFFFFFFFF |
96 | OP_MOV64, // mov64 rx, ra -> rx = ra & 0xFFFFFFFFFFFFFFFF | 97 | OP_MOV64, // mov64 rx, ra -> rx = ra & 0xFFFFFFFFFFFFFFFF |
98 | // Logic operations (only 64 bits for now). | ||
99 | OP_EQI, // eqk rx, ra, cb | ||
100 | OP_NEQI, // neqk rx, ra, cb | ||
101 | OP_LTI, // ltk rx, ra, cb | ||
102 | OP_GTI, // gtk rx, ra, cb | ||
103 | OP_LEI, // lek rx, ra, cb | ||
104 | OP_GEI, // gek rx, ra, cb | ||
105 | OP_ANDI, // andk rx, ra, cb | ||
106 | OP_ORI, // ork rx, ra, cb | ||
107 | OP_EQ, // eq rx, ra, rb | ||
108 | OP_NEQ, // neq rx, ra, rb | ||
109 | OP_LT, // lt rx, ra, rb | ||
110 | OP_GT, // gt rx, ra, rb | ||
111 | OP_LE, // le rx, ra, rb | ||
112 | OP_GE, // ge rx, ra, rb | ||
113 | OP_AND, // and rx, ra, rb | ||
114 | OP_OR, // or rx, ra, rb | ||
97 | } OpCode; | 115 | } OpCode; |
98 | 116 | ||
99 | Str op_str[] = { | 117 | Str op_str[] = { |
@@ -146,6 +164,19 @@ Str op_str[] = { | |||
146 | [OP_MOV16] = cstr("MOV16 "), | 164 | [OP_MOV16] = cstr("MOV16 "), |
147 | [OP_MOV32] = cstr("MOV32 "), | 165 | [OP_MOV32] = cstr("MOV32 "), |
148 | [OP_MOV64] = cstr("MOV64 "), | 166 | [OP_MOV64] = cstr("MOV64 "), |
167 | // Logic operations. | ||
168 | [OP_EQI] = cstr("EQI "), | ||
169 | [OP_NEQI] = cstr("NEQI "), | ||
170 | [OP_LTI] = cstr("LTI "), | ||
171 | [OP_GTI] = cstr("GTI "), | ||
172 | [OP_LEI] = cstr("LEI "), | ||
173 | [OP_GEI] = cstr("GEI "), | ||
174 | [OP_EQ] = cstr("EQ "), | ||
175 | [OP_NEQ] = cstr("NEQ "), | ||
176 | [OP_LT] = cstr("LT "), | ||
177 | [OP_GT] = cstr("GT "), | ||
178 | [OP_LE] = cstr("LE "), | ||
179 | [OP_GE] = cstr("GE "), | ||
149 | }; | 180 | }; |
150 | 181 | ||
151 | typedef enum { | 182 | typedef enum { |
@@ -213,6 +244,14 @@ compile_binary(Chunk *chunk, Node *node) { | |||
213 | op = OP_MODF; | 244 | op = OP_MODF; |
214 | } | 245 | } |
215 | } break; | 246 | } break; |
247 | case NODE_EQ: op = OP_EQ; break; | ||
248 | case NODE_NEQ: op = OP_NEQ; break; | ||
249 | case NODE_LT: op = OP_LT; break; | ||
250 | case NODE_GT: op = OP_GT; break; | ||
251 | case NODE_LE: op = OP_LE; break; | ||
252 | case NODE_GE: op = OP_GE; break; | ||
253 | case NODE_AND: op = OP_EQ; break; | ||
254 | case NODE_OR: op = OP_NEQ; break; | ||
216 | default: break; | 255 | default: break; |
217 | } | 256 | } |
218 | CompResult comp_a = compile_expr(chunk, node->left); | 257 | CompResult comp_a = compile_expr(chunk, node->left); |
@@ -243,8 +282,8 @@ compile_binary(Chunk *chunk, Node *node) { | |||
243 | return (CompResult){.type = COMP_ERR}; | 282 | return (CompResult){.type = COMP_ERR}; |
244 | } break; | 283 | } break; |
245 | } | 284 | } |
246 | sz reg_dst = comp_a.idx; // Less registers | 285 | // sz reg_dst = comp_a.idx; // Less registers |
247 | // sz reg_dst = chunk->reg_idx++; // Better for optimization | 286 | sz reg_dst = chunk->reg_idx++; // Better for optimization |
248 | EMIT_OP(op, reg_dst, reg_a, reg_b, node, chunk); | 287 | EMIT_OP(op, reg_dst, reg_a, reg_b, node, chunk); |
249 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 288 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
250 | } | 289 | } |
@@ -252,6 +291,18 @@ compile_binary(Chunk *chunk, Node *node) { | |||
252 | CompResult | 291 | CompResult |
253 | compile_expr(Chunk *chunk, Node *node) { | 292 | compile_expr(Chunk *chunk, Node *node) { |
254 | switch (node->kind) { | 293 | switch (node->kind) { |
294 | // Logic. | ||
295 | // case NODE_NOT: | ||
296 | // case NODE_XOR: | ||
297 | case NODE_AND: | ||
298 | case NODE_OR: | ||
299 | case NODE_EQ: | ||
300 | case NODE_NEQ: | ||
301 | case NODE_LT: | ||
302 | case NODE_GT: | ||
303 | case NODE_LE: | ||
304 | // Arithmetic. | ||
305 | case NODE_GE: | ||
255 | case NODE_ADD: | 306 | case NODE_ADD: |
256 | case NODE_SUB: | 307 | case NODE_SUB: |
257 | case NODE_MUL: | 308 | case NODE_MUL: |
@@ -194,6 +194,19 @@ process_file(Str path) { | |||
194 | Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg}; | 194 | Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg}; |
195 | array_push(chunk.code, halt, &bytecode_arena); | 195 | array_push(chunk.code, halt, &bytecode_arena); |
196 | 196 | ||
197 | if (chunk.const_idx >= 256) { | ||
198 | eprintln("too many constants on chunk %s", chunk.id); | ||
199 | exit(EXIT_FAILURE); | ||
200 | } | ||
201 | if (chunk.str_idx >= 256) { | ||
202 | eprintln("too many strings on chunk %s", chunk.id); | ||
203 | exit(EXIT_FAILURE); | ||
204 | } | ||
205 | if (chunk.reg_idx >= 256) { | ||
206 | eprintln("too many registers used on chunk %s", chunk.id); | ||
207 | exit(EXIT_FAILURE); | ||
208 | } | ||
209 | |||
197 | disassemble_chunk(chunk); | 210 | disassemble_chunk(chunk); |
198 | 211 | ||
199 | // Run bytecode on VM. | 212 | // Run bytecode on VM. |
@@ -39,6 +39,14 @@ disassemble_instruction(Instruction instruction) { | |||
39 | case OP_MULFI: | 39 | case OP_MULFI: |
40 | case OP_DIVFI: | 40 | case OP_DIVFI: |
41 | case OP_MODFI: | 41 | case OP_MODFI: |
42 | case OP_EQI: | ||
43 | case OP_NEQI: | ||
44 | case OP_LTI: | ||
45 | case OP_GTI: | ||
46 | case OP_LEI: | ||
47 | case OP_GEI: | ||
48 | case OP_ANDI: | ||
49 | case OP_ORI: | ||
42 | println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst, | 50 | println("%s r%d, r%d, c%d", op_str[instruction.op], instruction.dst, |
43 | instruction.a, instruction.b); | 51 | instruction.a, instruction.b); |
44 | break; | 52 | break; |
@@ -60,6 +68,14 @@ disassemble_instruction(Instruction instruction) { | |||
60 | case OP_MULF: | 68 | case OP_MULF: |
61 | case OP_DIVF: | 69 | case OP_DIVF: |
62 | case OP_MODF: | 70 | case OP_MODF: |
71 | case OP_EQ: | ||
72 | case OP_NEQ: | ||
73 | case OP_LT: | ||
74 | case OP_GT: | ||
75 | case OP_LE: | ||
76 | case OP_GE: | ||
77 | case OP_AND: | ||
78 | case OP_OR: | ||
63 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, | 79 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, |
64 | instruction.a, instruction.b); | 80 | instruction.a, instruction.b); |
65 | break; | 81 | break; |
@@ -106,7 +122,7 @@ vm_init(VM *vm, Chunk *chunk) { | |||
106 | vm->ip = vm->chunk->code; | 122 | vm->ip = vm->chunk->code; |
107 | } | 123 | } |
108 | 124 | ||
109 | #define OP_ARITHMETIC(OP, TYPE) \ | 125 | #define OP_BINARY(OP, TYPE) \ |
110 | do { \ | 126 | do { \ |
111 | u8 dst = instruction.dst; \ | 127 | u8 dst = instruction.dst; \ |
112 | u8 src_a = instruction.a; \ | 128 | u8 src_a = instruction.a; \ |
@@ -114,7 +130,7 @@ vm_init(VM *vm, Chunk *chunk) { | |||
114 | vm->regs[dst].TYPE = vm->regs[src_a].TYPE OP vm->regs[src_b].TYPE; \ | 130 | vm->regs[dst].TYPE = vm->regs[src_a].TYPE OP vm->regs[src_b].TYPE; \ |
115 | } while (0); | 131 | } while (0); |
116 | 132 | ||
117 | #define OP_ARITHMETIC_CONST(OP, TYPE) \ | 133 | #define OP_BINARY_CONST(OP, TYPE) \ |
118 | do { \ | 134 | do { \ |
119 | u8 dst = instruction.dst; \ | 135 | u8 dst = instruction.dst; \ |
120 | u8 src_a = instruction.a; \ | 136 | u8 src_a = instruction.a; \ |
@@ -144,15 +160,23 @@ vm_run(VM *vm) { | |||
144 | u8 src_a = instruction.a; | 160 | u8 src_a = instruction.a; |
145 | vm->regs[dst].i = vm->chunk->constants[src_a].i; | 161 | vm->regs[dst].i = vm->chunk->constants[src_a].i; |
146 | } break; | 162 | } break; |
147 | case OP_ADD: OP_ARITHMETIC(+, i) break; | 163 | case OP_EQ: OP_BINARY(==, i) break; |
148 | case OP_SUB: OP_ARITHMETIC(-, i) break; | 164 | case OP_NEQ: OP_BINARY(!=, i) break; |
149 | case OP_MUL: OP_ARITHMETIC(*, i) break; | 165 | case OP_LT: OP_BINARY(<, i) break; |
150 | case OP_DIV: OP_ARITHMETIC(/, i) break; | 166 | case OP_GT: OP_BINARY(>, i) break; |
151 | case OP_MOD: OP_ARITHMETIC(%, i) break; | 167 | case OP_LE: OP_BINARY(<=, i) break; |
152 | case OP_ADDF: OP_ARITHMETIC(+, f) break; | 168 | case OP_GE: OP_BINARY(>=, i) break; |
153 | case OP_SUBF: OP_ARITHMETIC(-, f) break; | 169 | case OP_AND: OP_BINARY(&&, i) break; |
154 | case OP_MULF: OP_ARITHMETIC(*, f) break; | 170 | case OP_OR: OP_BINARY(||, i) break; |
155 | case OP_DIVF: OP_ARITHMETIC(/, f) break; | 171 | case OP_ADD: OP_BINARY(+, i) break; |
172 | case OP_SUB: OP_BINARY(-, i) break; | ||
173 | case OP_MUL: OP_BINARY(*, i) break; | ||
174 | case OP_DIV: OP_BINARY(/, i) break; | ||
175 | case OP_MOD: OP_BINARY(%, i) break; | ||
176 | case OP_ADDF: OP_BINARY(+, f) break; | ||
177 | case OP_SUBF: OP_BINARY(-, f) break; | ||
178 | case OP_MULF: OP_BINARY(*, f) break; | ||
179 | case OP_DIVF: OP_BINARY(/, f) break; | ||
156 | case OP_MODF: { | 180 | case OP_MODF: { |
157 | u8 dst = instruction.dst; | 181 | u8 dst = instruction.dst; |
158 | u8 src_a = instruction.a; | 182 | u8 src_a = instruction.a; |
@@ -165,7 +189,6 @@ vm_run(VM *vm) { | |||
165 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); | 189 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); |
166 | return; | 190 | return; |
167 | } | 191 | } |
168 | |||
169 | default: { | 192 | default: { |
170 | eprintln("unimplemented OP code: %d", instruction.op); | 193 | eprintln("unimplemented OP code: %d", instruction.op); |
171 | return; | 194 | return; |
diff --git a/tests/compilation.bad b/tests/compilation.bad index 6fc5662..da6a6e3 100644 --- a/tests/compilation.bad +++ b/tests/compilation.bad | |||
@@ -1,5 +1,8 @@ | |||
1 | 1 + 2 * 3 | 1 | ; 1 < 2 |
2 | 1.0 + 2.0 * 3.0 | 2 | ; 1 == 1 && 1 <= 1 |
3 | 1 * 3 >= 3 && 4 != 3 + 2 | ||
4 | ; 1 + 2 * 3 | ||
5 | ; 1.0 + 2.0 * 3.0 | ||
3 | ; true | 6 | ; true |
4 | ; false | 7 | ; false |
5 | ; 0 1 | 8 | ; 0 1 |