aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-10-02 01:00:59 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commitb124548bcee1ab6d034d4499fe695073566ae37d (patch)
treee4014070ac69a2b821e12cc9264ba54aaa8089f4
parent7eb8642c3ace697b03c4fc6edc90ea0ada715689 (diff)
Add !=,<,<=,>,>=; both signed and not
-rw-r--r--README.md2
-rw-r--r--executor/executor.h12
-rw-r--r--executor/x86_64/asm.c89
-rw-r--r--executor/x86_64/asm.h19
-rw-r--r--executor/x86_64/executor.c187
-rw-r--r--include/binop_type.h7
-rw-r--r--include/bytecode/bytecode.h11
-rw-r--r--include/ssa/ssa.h9
-rw-r--r--include/tokenizer.h1
-rw-r--r--src/bytecode/bytecode.c47
-rw-r--r--src/program.c51
-rw-r--r--src/ssa/ssa.c36
-rw-r--r--src/tokenizer.c27
-rw-r--r--tests/bytecode.amal15
14 files changed, 487 insertions, 26 deletions
diff --git a/README.md b/README.md
index 8d9745d..36997eb 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,8 @@ should be in the bytecode before the files that they are used from, to reduce de
* Document all limits.
* To remove some overhead that external variables have on LhsExpr, make a ExternLhsExpr type.
* Make function calls work for functions that return no value or returns multiple values.
+* Some bytecode instructions take 3 operands. These should be optimized to operate directly on
+the destination if the second or third operand is also the destination.
# Documents
Documents are located under doc. The file doc/Documentation.md is generated from source files by running doc/doc_extract.py
but there is no need to run this script unless you are modifying documentation in the source.
diff --git a/executor/executor.h b/executor/executor.h
index 245e64c..70d9637 100644
--- a/executor/executor.h
+++ b/executor/executor.h
@@ -45,7 +45,17 @@ CHECK_RESULT int amal_exec_call(amal_executor *self, u32 code_offset, AmalReg ds
void amal_exec_call_overwrite(amal_executor *self, u32 call_code_offset, i32 new_target_rel32);
/*CHECK_RESULT int amal_exec_callr(AmalReg dst_reg, BufferView data);*/
CHECK_RESULT int amal_exec_calle(amal_executor *self, void *func, AmalReg dst_reg);
-CHECK_RESULT int amal_exec_cmp(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_eq(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_neq(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_ilt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_ile(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_igt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_ige(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_lt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_le(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_gt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_ge(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
+CHECK_RESULT int amal_exec_and(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2);
CHECK_RESULT int amal_exec_jz(amal_executor *self, AmalReg reg, u16 target_label);
CHECK_RESULT int amal_exec_jmp(amal_executor *self, u16 target_label);
CHECK_RESULT int amal_exec_ret(amal_executor *self, AmalReg reg);
diff --git a/executor/x86_64/asm.c b/executor/x86_64/asm.c
index f7bc19c..a400656 100644
--- a/executor/x86_64/asm.c
+++ b/executor/x86_64/asm.c
@@ -410,6 +410,14 @@ void asm_mov_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_end(self, "mov %s, %s", reg64_to_str(dst), reg64_to_str(src));
}
+void asm_and_mr(Asm *self, AsmPtr *dst, Reg64 src) {
+ ins_start(self);
+ *self->code_it++ = rex_rm(dst, src);
+ *self->code_it++ = 0x21;
+ asm_rm(self, dst, src);
+ ins_end(self, "and %s, %s", asm_ptr_to_string(dst), reg64_to_str(src));
+}
+
void asm_add_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
*self->code_it++ = rex_rr(dst, src);
@@ -515,6 +523,87 @@ void asm_sete_r(Asm *self, Reg64 dst) {
ins_end(self, "sete %s", reg64_to_str(dst));
}
+void asm_setne_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x95;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setne %s", reg64_to_str(dst));
+}
+
+void asm_setb_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x92;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setb %s", reg64_to_str(dst));
+}
+
+void asm_setbe_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x96;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setbe %s", reg64_to_str(dst));
+}
+
+void asm_seta_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x97;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "seta %s", reg64_to_str(dst));
+}
+
+void asm_setae_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x93;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setae %s", reg64_to_str(dst));
+}
+
+void asm_setl_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x9C;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setl %s", reg64_to_str(dst));
+}
+
+void asm_setle_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x9E;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setle %s", reg64_to_str(dst));
+}
+
+void asm_setg_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x9F;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setg %s", reg64_to_str(dst));
+}
+
+void asm_setge_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x9D;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "setge %s", reg64_to_str(dst));
+}
+
/*
Note: This is sometimes called with @relative INT32_MAX-(2 or 6) (will print jz 0x7ffffff9), in which case it's most likely a dummy
jump until the relative position is later changed with @asm_overwrite_jcc_rel32.
diff --git a/executor/x86_64/asm.h b/executor/x86_64/asm.h
index dacc248..7d68bc0 100644
--- a/executor/x86_64/asm.h
+++ b/executor/x86_64/asm.h
@@ -80,6 +80,8 @@ void asm_mov_rm(Asm *self, Reg64 dst, AsmPtr *src);
void asm_mov_ri(Asm *self, Reg64 dst, i64 immediate);
void asm_mov_rr(Asm *self, Reg64 dst, Reg64 src);
+void asm_and_mr(Asm *self, AsmPtr *dst, Reg64 src);
+
void asm_add_rr(Asm *self, Reg64 dst, Reg64 src);
void asm_sub_rr(Asm *self, Reg64 dst, Reg64 src);
void asm_imul_rr(Asm *self, Reg64 dst, Reg64 src);
@@ -110,6 +112,23 @@ void asm_cmp_rm(Asm *self, Reg64 reg1, AsmPtr *reg2);
*/
void asm_sete_m(Asm *self, AsmPtr *dst);
void asm_sete_r(Asm *self, Reg64 dst);
+void asm_setne_r(Asm *self, Reg64 dst);
+/* Unsigned */
+void asm_setb_r(Asm *self, Reg64 dst);
+/* Unsigned */
+void asm_setbe_r(Asm *self, Reg64 dst);
+/* Unsigned */
+void asm_seta_r(Asm *self, Reg64 dst);
+/* Unsigned */
+void asm_setae_r(Asm *self, Reg64 dst);
+/* Signed */
+void asm_setl_r(Asm *self, Reg64 dst);
+/* Signed */
+void asm_setle_r(Asm *self, Reg64 dst);
+/* Signed */
+void asm_setg_r(Asm *self, Reg64 dst);
+/* Signed */
+void asm_setge_r(Asm *self, Reg64 dst);
/*
In x86 assembly, the @relative position starts from the next instruction.
This offset shouldn't be calculated by the caller and is instead managed
diff --git a/executor/x86_64/executor.c b/executor/x86_64/executor.c
index 6f3c1de..7591dcf 100644
--- a/executor/x86_64/executor.c
+++ b/executor/x86_64/executor.c
@@ -389,10 +389,15 @@ int amal_exec_call(amal_executor *self, u32 code_offset, AmalReg dst_reg) {
/* TODO: This assumes all arguments are isize */
/* Do the function call */
isize asm_offset = asm_get_size(&impl->asm);
- int num_pushed_stack = impl->num_pushed_values + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS;
+ /* TODO: Do not push */
+ int num_pushed_stack = impl->num_pushed_values;/* + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS;*/
ASM_ENSURE_CAPACITY
- assert((num_pushed_stack <= 0 || num_pushed_stack % 2 == 0) && "TODO: Align stack to 16-bytes before calling functions");
+ /*assert((num_pushed_stack <= 0 || num_pushed_stack % 2 == 0) && "TODO: Align stack to 16-bytes before calling functions");*/
+ if(num_pushed_stack & 1) {
+ ++num_pushed_stack;
+ asm_sub_rm64_imm(&impl->asm, RSP, sizeof(isize));
+ }
assert(code_offset < asm_offset);
asm_call_rel32(&impl->asm, (isize)code_offset - asm_offset);
@@ -427,10 +432,15 @@ int amal_exec_calle(amal_executor *self, void *func, AmalReg dst_reg) {
AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
AsmOperand rax_op = asm_reg_to_operand(RAX);
amal_executor_impl *impl = (amal_executor_impl*)self;
- int num_pushed_stack = impl->num_pushed_values + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS;
+ /* TODO: Do not push */
+ int num_pushed_stack = impl->num_pushed_values;/* + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS;*/
ASM_ENSURE_CAPACITY
- assert((num_pushed_stack <= 0 || num_pushed_stack % 2 == 0) && "TODO: Align stack to 16-bytes before calling functions");
+ /*assert((num_pushed_stack <= 0 || num_pushed_stack % 2 == 0) && "TODO: Align stack to 16-bytes before calling functions");*/
+ if(num_pushed_stack & 1) {
+ ++num_pushed_stack;
+ asm_sub_rm64_imm(&impl->asm, RSP, sizeof(isize));
+ }
/* TODO: Preserve necessary registers before call? */
/* TODO: This assumes all arguments are isize */
@@ -449,7 +459,7 @@ int amal_exec_callr(AmalReg dst_reg, BufferView data) {
}
*/
-int amal_exec_cmp(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+int amal_exec_eq(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
@@ -458,9 +468,7 @@ int amal_exec_cmp(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalRe
amal_executor_impl *impl = (amal_executor_impl*)self;
ASM_ENSURE_CAPACITY
- asm_mov(&impl->asm, &rcx_op, &dst_op);
asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
-
asm_mov(&impl->asm, &rax_op, &src_op1);
asm_cmp(&impl->asm, &rax_op, &src_op2);
asm_sete_r(&impl->asm, rcx_op.value.reg);
@@ -468,6 +476,171 @@ int amal_exec_cmp(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalRe
return 0;
}
+int amal_exec_neq(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setne_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_ilt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setb_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_ile(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setbe_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_igt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_seta_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_ige(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setae_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_lt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setl_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_le(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setle_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_gt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setg_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_ge(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ AsmOperand rax_op = asm_reg_to_operand(RAX);
+ AsmOperand rcx_op = asm_reg_to_operand(RCX);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg);
+ asm_mov(&impl->asm, &rax_op, &src_op1);
+ asm_cmp(&impl->asm, &rax_op, &src_op2);
+ asm_setge_r(&impl->asm, rcx_op.value.reg);
+ asm_mov(&impl->asm, &dst_op, &rcx_op);
+ return 0;
+}
+
+int amal_exec_and(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) {
+ AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg);
+ AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1);
+ AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2);
+ amal_executor_impl *impl = (amal_executor_impl*)self;
+ ASM_ENSURE_CAPACITY
+
+ asm_and_rm64(&impl->asm, src_op1.value.reg, src_op2.value.reg);
+ asm_mov(&impl->asm, &dst_op, &src_op1);
+ return 0;
+}
+
int amal_exec_jz(amal_executor *self, AmalReg reg, u16 target_label) {
AsmOperand op = amal_reg_to_asm_operand(reg);
AsmOperand rax_op = asm_reg_to_operand(RAX);
diff --git a/include/binop_type.h b/include/binop_type.h
index 94d2b7a..9264cf7 100644
--- a/include/binop_type.h
+++ b/include/binop_type.h
@@ -8,7 +8,12 @@ typedef enum {
BINOP_DIV,
BINOP_DOT,
BINOP_EQUALS,
- BINOP_AND
+ BINOP_NOT_EQUAL,
+ BINOP_AND,
+ BINOP_LESS,
+ BINOP_LESS_EQUAL,
+ BINOP_GREATER,
+ BINOP_GREATER_EQUAL
} BinopType;
#endif
diff --git a/include/bytecode/bytecode.h b/include/bytecode/bytecode.h
index 30f3bb5..f649368 100644
--- a/include/bytecode/bytecode.h
+++ b/include/bytecode/bytecode.h
@@ -59,7 +59,16 @@ typedef enum {
AMAL_OP_CALL, /* call ii, fi - Call a function in imported file (ii, import index) using function index (fi). The number of arguments is the number of values pushed to stack. ii is u8, fi is u16 */
AMAL_OP_CALLR, /* callr reg - Call a function using a register. Used for function pointers. The number of arguments is the number of values pushed to stack */
AMAL_OP_CALLE, /* calle ii, efi - Call an extern function in imported file (ii, import index) using extern function index (efi). The number of arguments is the number of values pushed to stack. ii is u8, efi is u16 */
- AMAL_OP_CMP, /* cmp dst, reg1, reg2 - Set dst to 1 if reg1 equals reg2, otherwise set it to 0 */
+ AMAL_OP_EQ, /* eq dst, reg1, reg2 - Set dst to 1 if reg1 equals reg2, otherwise set it to 0 */
+ AMAL_OP_NEQ, /* neq dst, reg1, reg2 - Set dst to 1 if reg1 is not equal to reg2, otherwise set it to 0 */
+ AMAL_OP_ILT, /* ilt dst, reg1, reg2 - Set dst to 1 if reg1 is less than reg2 (signed comparison), otherwise set it to 0 */
+ AMAL_OP_ILE, /* ile dst, reg1, reg2 - Set dst to 1 if reg1 is less or equal to reg2 (signed comparison), otherwise set it to 0 */
+ AMAL_OP_IGT, /* igt dst, reg1, reg2 - Set dst to 1 if reg1 is greater than reg2 (signed comparison), otherwise set it to 0 */
+ AMAL_OP_IGE, /* ige dst, reg1, reg2 - Set dst to 1 if reg1 is greater or equal to reg2 (signed comparison), otherwise set it to 0 */
+ AMAL_OP_LT, /* lt dst, reg1, reg2 - Set dst to 1 if reg1 is less than reg2 (unsigned comparison), otherwise set it to 0 */
+ AMAL_OP_LE, /* le dst, reg1, reg2 - Set dst to 1 if reg1 is less or equal to reg2 (unsigned comparison), otherwise set it to 0 */
+ AMAL_OP_GT, /* gt dst, reg1, reg2 - Set dst to 1 if reg1 is greater than reg2 (unsigned comparison), otherwise set it to 0 */
+ AMAL_OP_GE, /* ge dst, reg1, reg2 - Set dst to 1 if reg1 is greater or equal to reg2 (unsigned comparison), otherwise set it to 0 */
AMAL_OP_BIT_AND, /* and dst, reg1, reg2 - Perform bit and on reg1 and reg2, store the result in dst */
AMAL_OP_JZ, /* jz reg, label - Jump to label in the current function if reg is zero. label is u16 */
AMAL_OP_JMP, /* jmp label - Unconditional jump to label in the current function. label is u16 */
diff --git a/include/ssa/ssa.h b/include/ssa/ssa.h
index c6d58f6..e8e68c3 100644
--- a/include/ssa/ssa.h
+++ b/include/ssa/ssa.h
@@ -22,7 +22,16 @@ typedef enum {
SSA_IDIV,
SSA_DIV,
SSA_EQUALS,
+ SSA_NOT_EQUAL,
SSA_AND,
+ SSA_ILT,
+ SSA_ILE,
+ SSA_IGT,
+ SSA_IGE,
+ SSA_LT,
+ SSA_LE,
+ SSA_GT,
+ SSA_GE,
SSA_FUNC_START,
SSA_FUNC_END,
SSA_PUSH,
diff --git a/include/tokenizer.h b/include/tokenizer.h
index 4735d6f..f716ab2 100644
--- a/include/tokenizer.h
+++ b/include/tokenizer.h
@@ -24,6 +24,7 @@ typedef enum {
TOK_FN,
TOK_STRUCT,
TOK_EQUALS,
+ TOK_NOT,
TOK_OPEN_PAREN,
TOK_CLOSING_PAREN,
TOK_COMMA,
diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c
index b947985..98698bc 100644
--- a/src/bytecode/bytecode.c
+++ b/src/bytecode/bytecode.c
@@ -522,7 +522,12 @@ static void add_instructions(BytecodeCompilerContext *self) {
}
case SSA_EQUALS: {
instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
- add_ins5(self, AMAL_OP_CMP, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "cmp r%d, r%d, r%d");
+ add_ins5(self, AMAL_OP_EQ, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "eq r%d, r%d, r%d");
+ break;
+ }
+ case SSA_NOT_EQUAL: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_NEQ, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "neq r%d, r%d, r%d");
break;
}
case SSA_AND: {
@@ -530,6 +535,46 @@ static void add_instructions(BytecodeCompilerContext *self) {
add_ins5(self, AMAL_OP_BIT_AND, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "and r%d, r%d, r%d");
break;
}
+ case SSA_ILT: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_ILT, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "ilt r%d, r%d, r%d");
+ break;
+ }
+ case SSA_ILE: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_ILE, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "ile r%d, r%d, r%d");
+ break;
+ }
+ case SSA_IGT: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_IGT, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "igt r%d, r%d, r%d");
+ break;
+ }
+ case SSA_IGE: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_IGE, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "ige r%d, r%d, r%d");
+ break;
+ }
+ case SSA_LT: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_LT, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "lt r%d, r%d, r%d");
+ break;
+ }
+ case SSA_LE: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_LE, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "le r%d, r%d, r%d");
+ break;
+ }
+ case SSA_GT: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_GT, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "gt r%d, r%d, r%d");
+ break;
+ }
+ case SSA_GE: {
+ instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2));
+ add_ins5(self, AMAL_OP_GE, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "ge r%d, r%d, r%d");
+ break;
+ }
case SSA_FUNC_START: {
instruction += ssa_extract_data(instruction, &ssa_ins_func_start, sizeof(ssa_ins_func_start));
add_ins6(self, AMAL_OP_FUNC_START, ssa_ins_func_start.flags, ssa_ins_func_start.num_local_vars_regs, "func_start 0x%02x, %u");
diff --git a/src/program.c b/src/program.c
index ac3c923..169d270 100644
--- a/src/program.c
+++ b/src/program.c
@@ -656,13 +656,58 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_
self->read_index += 3;
break;
}
- case AMAL_OP_CMP: {
- return_if_error(amal_exec_cmp(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ case AMAL_OP_EQ: {
+ return_if_error(amal_exec_eq(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_NEQ: {
+ return_if_error(amal_exec_neq(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_ILT: {
+ return_if_error(amal_exec_ilt(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_ILE: {
+ return_if_error(amal_exec_ile(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_IGT: {
+ return_if_error(amal_exec_igt(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_IGE: {
+ return_if_error(amal_exec_ige(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_LT: {
+ return_if_error(amal_exec_lt(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_LE: {
+ return_if_error(amal_exec_le(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_GT: {
+ return_if_error(amal_exec_gt(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_GE: {
+ return_if_error(amal_exec_ge(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_BIT_AND: {
- assert(bool_false && "TODO: Implement!");
+ return_if_error(amal_exec_and(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c
index d875ea4..34ecada 100644
--- a/src/ssa/ssa.c
+++ b/src/ssa/ssa.c
@@ -244,15 +244,23 @@ static CHECK_RESULT int ssa_add_ins_form1(Ssa *self, SsaInstruction ins_type, Ss
}
static const char* binop_type_to_string(SsaInstruction binop_type) {
- assert(binop_type >= SSA_ADD && binop_type <= SSA_EQUALS);
+ assert(binop_type >= SSA_ADD && binop_type <= SSA_GE);
switch(binop_type) {
- case SSA_ADD: return "+";
- case SSA_SUB: return "-";
- case SSA_MUL: return "*";
- case SSA_DIV: return "/";
- case SSA_EQUALS: return "==";
- case SSA_AND: return "&&";
- default: return "";
+ case SSA_ADD: return "+";
+ case SSA_SUB: return "-";
+ case SSA_MUL: return "*";
+ case SSA_DIV: return "/";
+ case SSA_EQUALS: return "==";
+ case SSA_AND: return "&&";
+ case SSA_ILT: return "<";
+ case SSA_LT: return "<";
+ case SSA_ILE: return "<=";
+ case SSA_LE: return "<=";
+ case SSA_IGT: return ">";
+ case SSA_GT: return ">";
+ case SSA_IGE: return ">=";
+ case SSA_GE: return ">=";
+ default: return "";
}
}
@@ -287,7 +295,7 @@ static CHECK_RESULT int ssa_ins_assign_reg(Ssa *self, SsaRegister dest, SsaRegis
}
static CHECK_RESULT int ssa_ins_binop(Ssa *self, SsaInstruction binop_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result) {
- assert(binop_type >= SSA_ADD && binop_type <= SSA_AND);
+ assert(binop_type >= SSA_ADD && binop_type <= SSA_GE);
return ssa_add_ins_form2(self, binop_type, lhs, rhs, result);
}
@@ -759,8 +767,18 @@ static SsaInstruction binop_type_to_ssa_type(BinopType binop_type, amal_default_
return 0;
case BINOP_EQUALS:
return SSA_EQUALS;
+ case BINOP_NOT_EQUAL:
+ return SSA_NOT_EQUAL;
case BINOP_AND:
return SSA_AND;
+ case BINOP_LESS:
+ return type->is_signed ? SSA_ILT : SSA_LT;
+ case BINOP_LESS_EQUAL:
+ return type->is_signed ? SSA_ILE : SSA_LE;
+ case BINOP_GREATER:
+ return type->is_signed ? SSA_IGT : SSA_GT;
+ case BINOP_GREATER_EQUAL:
+ return type->is_signed ? SSA_IGE : SSA_GE;
}
return 0;
}
diff --git a/src/tokenizer.c b/src/tokenizer.c
index da6ad53..2c27809 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -339,6 +339,14 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) {
} else {
*token = TOK_EQUALS;
}
+ } else if(c == '!') {
+ ++self->index;
+ if(self->index < (int)self->code.size && tokenizer_get_char(self) == '=') {
+ ++self->index;
+ SET_BINOP(BINOP_NOT_EQUAL);
+ } else {
+ *token = TOK_NOT;
+ }
} else if(c == '&') {
++self->index;
if(self->index < (int)self->code.size && tokenizer_get_char(self) == '&') {
@@ -347,6 +355,22 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) {
} else {
*token = TOK_AMPERSAND;
}
+ } else if(c == '<') {
+ ++self->index;
+ if(self->index < (int)self->code.size && tokenizer_get_char(self) == '=') {
+ ++self->index;
+ SET_BINOP(BINOP_LESS_EQUAL);
+ } else {
+ SET_BINOP(BINOP_LESS);
+ }
+ } else if(c == '>') {
+ ++self->index;
+ if(self->index < (int)self->code.size && tokenizer_get_char(self) == '=') {
+ ++self->index;
+ SET_BINOP(BINOP_GREATER_EQUAL);
+ } else {
+ SET_BINOP(BINOP_GREATER);
+ }
} else if(c == '(') {
++self->index;
*token = TOK_OPEN_PAREN;
@@ -470,6 +494,9 @@ static BufferView tokenizer_expected_token_as_string(Token token) {
case TOK_EQUALS:
str = "=";
break;
+ case TOK_NOT:
+ str = "!";
+ break;
case TOK_OPEN_PAREN:
str = "(";
break;
diff --git a/tests/bytecode.amal b/tests/bytecode.amal
index 35dc3da..5ba26ed 100644
--- a/tests/bytecode.amal
+++ b/tests/bytecode.amal
@@ -1,5 +1,14 @@
-const io = @import("../std/io.amal");
+extern const printf: fn(fmt: ?&c_char, ...) c_int;
const main = fn {
- io.print("hello %s", "world!");
-}
+ var value = 23 + 50;
+ if value < 23
+ printf("less!\n");
+ else
+ printf("more!\n");
+
+ while value > 0 {
+ printf("value: %ld\n", value);
+ value = value - 1;
+ }
+} \ No newline at end of file