diff --git a/lang/src/codegen.asm b/lang/src/codegen.asm index 58c40fb..5cf1f8d 100644 --- a/lang/src/codegen.asm +++ b/lang/src/codegen.asm @@ -1,6 +1,7 @@ default rel %include "src/ast.inc" +%include "src/tokeniser.inc" extern panic extern vec_extend @@ -10,6 +11,7 @@ extern vec_insert_sorted extern vec_insert_many extern vec_init_with extern int_to_str2 +extern strlen global codegen_function global get_register_name @@ -961,10 +963,10 @@ codegen_expr: push r15 push r14 - ; scratch [16..48] + ; scratch [16..80] ; function_ctx: [8..16] ; ctx [0..8] - sub rsp, 48 + sub rsp, 80 mov [rsp], rdi ; ctx mov [rsp + 8], rsi ; &function_ctx @@ -1090,9 +1092,201 @@ codegen_expr: mov rdx, qword [rsp + 24] jmp .done +.binary_op: + mov rax, [rax + 8] ; AstNode.data + mov [rsp + 16], rax ; scratch = *AstBinaryOp + + mov rdi, [rsp] ; ctx + mov rsi, [rsp + 8] ; &function_ctx + mov rdx, [rax + 0] ; left operand index + call codegen_expr + mov [rsp + 32], rax ; left operand + mov [rsp + 40], rdx + + mov rdi, [rsp] ; ctx + mov rsi, [rsp + 8] ; &function_ctx + mov rdx, [rsp + 16] ; *AstBinaryOp + mov rdx, [rdx + 16] ; right operand index + call codegen_expr + mov [rsp + 48], rax ; right operand + mov [rsp + 56], rdx + + mov rax, [rsp + 16] ; *AstBinaryOp + mov al, byte [rax + 8] ; operator + mov rbx, -1 + cmp al, TOKEN_PLUS + cmove rbx, [rel ADD_] + cmp al, TOKEN_MINUS + cmove rbx, [rel SUB_] + cmp rbx, -1 + jne .gen_op + cmp al, TOKEN_STAR + cmove rbx, [rel MUL_] + cmp al, TOKEN_SLASH + cmove rbx, [rel DIV_] + cmp al, TOKEN_PERCENT + cmove rbx, [rel DIV_] + cmp rbx, -1 + je .panic ; unknown operator +.mul_div: + ; mul/div need to clobber rax:rdx + + ; TODO only check for div + mov rax, [rsp + 8] ; &function_ctx + mov ax, word [rax + 48] ; register_bitset + bt ax, 3 ; is rdx used? + jnc .after_spill_rdx + + ; allocate scratch value for rdx + mov rdi, [rsp + 8] ; &function_ctx + mov rsi, 8 ; width + call codegen_allocate_place + mov [rsp + 64], rax + mov [rsp + 72], rdx + + ; mov scratch, rdx + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rsp + 64] ; scratch value + lea rdx, [rel OPERAND_RDX] ; rax + call codegen_move_dst_src + + ; check if rhs is rdx + mov rax, [rsp + 48] ; right operand + and rax, 0xFFF + mov rdx, [rel OPERAND_RDX] + and rdx, 0xFFF + cmp rax, rdx + jne .after_spill_rdx + + ; free rhs + mov rdi, [rsp + 8] ; &function_ctx + lea rsi, [rsp + 48] ; right operand + call codegen_free_operand + + mov rdx, [rsp + 48] ; right operand + and rdx, 0xF000 ; Operand.width + mov rax, [rsp + 64] ; scratch value + or rax, rdx ; preserve width + mov rdx, [rsp + 72] + mov [rsp + 48], rax ; right operand + mov [rsp + 56], rdx + +.after_spill_rdx: + mov rax, [rsp + 16] ; *AstBinaryOp + mov al, byte [rax + 8] ; operator + cmp al, TOKEN_STAR + je .after_clear_rdx + + ; clear rdx for div + ; xor rdx, rdx + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel XOR_RDX_RDX] ; rdx + mov rdx, XOR_RDX_RDX_LEN + call vec_extend + +.after_clear_rdx: + ; mov rax, lhs + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel OPERAND_RAX] ; rax + lea rdx, [rsp + 32] ; left operand + call codegen_move_dst_src + + ; op rhs + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + push rbx + lea rsi, [rsp] ; op + mov rdx, 4 + call vec_extend + pop rbx + + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rsp + 48] ; left operand + call codegen_write_operand + + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + mov rsi, 10 + push rsi + lea rsi, [rsp] ; newline + call vec_push + pop rsi + + mov rax, [rsp + 16] ; *AstBinaryOp + mov al, byte [rax + 8] ; operator + cmp al, TOKEN_PERCENT + jne .after_rem + + ; mov rax, rdx // only for rem + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel OPERAND_RAX] ; rax + lea rdx, [rel OPERAND_RDX] ; rdx + call codegen_move_dst_src + +.after_rem: + mov rax, [rsp + 8] ; &function_ctx + mov ax, word [rax + 48] ; register_bitset + bt ax, 3 ; is rdx used? + jnc .after_unspill_rdx + + ; mov rdx, scratch + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel OPERAND_RDX] ; rdx + lea rdx, [rsp + 64] ; scratch value + call codegen_move_dst_src + +.after_unspill_rdx: + ; free [scratch, rhs, lhs] + mov rdi, [rsp + 8] ; &function_ctx + lea rsi, [rsp + 64] ; scratch value + call codegen_free_operand + + mov rdi, [rsp + 8] ; &function_ctx + lea rsi, [rsp + 48] ; right operand + call codegen_free_operand + + mov rdi, [rsp + 8] ; &function_ctx + lea rsi, [rsp + 32] ; left operand + call codegen_free_operand + + ; alloca dst + mov rdi, [rsp + 8] ; &function_ctx + mov rsi, 8 ; width + call codegen_allocate_value + mov [rsp + 32], rax + mov [rsp + 40], rdx + + ; mov dst, rax + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rsp + 32] ; dst + lea rdx, [rel OPERAND_RAX] ; rax + call codegen_move_dst_src + + ; return dst + mov rax, [rsp + 32] + mov rdx, [rsp + 40] + jmp .done + +.gen_op: + + mov rdi, [rsp + 8] ; &function_ctx + mov rsi, [rsp] ; ctx + lea rsi, [rsi + 8] ; &ctx.text + lea rdx, [rsp + 32] ; left operand + lea rcx, [rsp + 48] ; right operand + mov r8, rbx ; operation + call codegen_binary_op_rm64_rm64 + jmp .done + .var_decl: .var_ref: -.binary_op: .assignment: .place_to_value: .value_to_place: @@ -1101,7 +1295,7 @@ codegen_expr: ; TODO .done: - add rsp, 48 + add rsp, 80 pop r14 pop r15 pop rbx @@ -1362,6 +1556,218 @@ codegen_move_rbp_slot_src: pop rbp ret +;; rdi: *function_ctx +;; rsi: *text +;; rdx: lhs: *Operand +;; rcx: rhs: *Operand +;; r8: op: [u8; 8] +;; Generates: {op} {lhs}, {rhs} for a binary operation that has the encodings rN, rmN and rmN, rN +codegen_binary_op_rm64_rm64: + push rbp + mov rbp, rsp + push rbx + + ; dst [32..48] + sub rsp, 48 + mov [rsp], rdi ; *function_ctx + mov [rsp + 8], rsi ; *text + mov [rsp + 16], rdx ; lhs + mov [rsp + 24], rcx ; rhs + mov [rsp + 32], r8 ; op + + ; if lhs.kind == REGISTER || lhs.kind < ADDRESS && rhs.kind == REGISTER { + cmp byte [rdx + 0], OPERAND_REGISTER + je .simple + cmp byte [rdx + 0], OPERAND_ADDRESS + setb al + cmp byte [rcx + 0], OPERAND_REGISTER + sete bl + test al, bl + jne .simple + jmp .complex +.simple: + ; op lhs, rhs + lea rdi, [rsp + 32] ; op + call strlen + mov rdi, [rsp + 8] ; *text + lea rsi, [rsp + 32] ; op + mov rdx, rax ; op length + call vec_extend + + mov rdi, [rsp + 8] ; *text + mov rsi, [rsp + 16] ; lhs + call codegen_write_operand + + mov rdi, [rsp + 8] ; *text + lea rsi, [rel COMMA_RAX] + mov rdx, 2 + call vec_extend + + mov rdi, [rsp + 8] ; *text + mov rsi, [rsp + 24] ; rhs + call codegen_write_operand + + mov byte [rsp + 32], 10 ; newline + mov rdi, [rsp + 8] ; *text + lea rsi, [rsp + 32] + call vec_push + + ; free rhs + mov rdi, [rsp] ; *function_ctx + mov rsi, [rsp + 24] ; rhs + call codegen_free_operand + + ; ret lhs + mov rbx, [rsp + 16] ; lhs + mov rax, [rbx] + mov rdx, [rbx + 8] + jmp .epilogue + ; } else { +.complex: + ; if lhs.kind < ADDRESS { + cmp byte [rdx + 0], OPERAND_ADDRESS + jae .check_rhs + + ; mov rax, rhs + mov rdi, [rsp + 8] ; *text + lea rsi, [rel OPERAND_RAX] + mov rdx, [rsp + 24] ; rhs + call codegen_move_dst_src + + ; op lhs, rax + mov rdi, [rsp + 8] ; *text + mov rsi, [rsp + 16] ; lhs + lea rdx, [rel OPERAND_RAX] ; rax + mov rcx, [rsp + 32] ; op + call codegen_binary_op_unchecked + + ; free rhs + mov rdi, [rsp] ; *function_ctx + mov rsi, [rsp + 24] ; rhs + call codegen_free_operand + + ; ret lhs + mov rbx, [rsp + 16] ; lhs + mov rax, [rbx] + mov rdx, [rbx + 8] + jmp .epilogue + +.check_rhs: + ; } else if rhs.kind < ADDRESS { + cmp byte [rcx + 0], OPERAND_ADDRESS + jae .allocate_dst + ; mov rax, lhs + mov rdi, [rsp + 8] ; *text + lea rsi, [rel OPERAND_RAX] + mov rdx, [rsp + 16] ; lhs + call codegen_move_dst_src + + ; op rax, rhs + mov rdi, [rsp + 8] ; *text + lea rsi, [rel OPERAND_RAX] ; rax + mov rdx, [rsp + 24] ; rhs + mov rcx, [rsp + 32] ; op + call codegen_binary_op_unchecked + + ; mov rhs, rax + mov rdi, [rsp + 8] ; *text + mov rsi, [rsp + 24] ; rhs + lea rdx, [rel OPERAND_RAX] + call codegen_move_dst_src + + ; free lhs + mov rdi, [rsp] ; *function_ctx + mov rsi, [rsp + 16] ; rhs + call codegen_free_operand + + ; ret rhs + mov rbx, [rsp + 24] ; rhs + mov rax, [rbx] + mov rdx, [rbx + 8] + jmp .epilogue + ; } else { +.allocate_dst: + ; dst = allocate_value + mov rdi, [rsp] ; *function_ctx + mov rsi, 8 ; width = 8 + call codegen_allocate_value + mov [rsp + 32], rax ; dst + mov [rsp + 40], rdx + + ; mov dst, lhs + mov rdi, [rsp + 8] ; *text + lea rsi, [rsp + 32] ; dst + mov rdx, [rsp + 16] ; lhs + call codegen_move_dst_src + + ; mov rax, rhs + mov rdi, [rsp + 8] ; *text + lea rsi, [rel OPERAND_RAX] ; rax + mov rdx, [rsp + 24] ; rhs + call codegen_move_dst_src + + ; op dst, rax + mov rdi, [rsp + 8] ; *text + lea rsi, [rsp + 32] ; dst + lea rdx, [rel OPERAND_RAX] ; rax + mov rcx, [rsp + 32] ; op + call codegen_binary_op_unchecked + + ; ret dst + mov rax, [rsp + 32] ; dst + mov rdx, [rsp + 40] + ; } + +.epilogue: + add rsp, 48 + pop rbx + pop rbp + ret + +;; rdi: *text +;; rsi: lhs: *Operand +;; rdx: rhs: *Operand +;; rcx: op: [u8; 8] +codegen_binary_op_unchecked: + push rbp + mov rbp, rsp + + sub rsp, 32 + mov [rsp], rdi ; *text + mov [rsp + 8], rsi ; lhs + mov [rsp + 16], rdx ; rhs + mov [rsp + 24], rcx ; op + + ; op lhs, rax + lea rdi, [rsp + 24] ; op + call strlen + mov rdi, [rsp] ; *text + lea rsi, [rsp + 24] ; op + mov rdx, rax ; op length + call vec_extend + + mov rdi, [rsp] ; *text + mov rsi, [rsp + 8] ; lhs + call codegen_write_operand + + mov rdi, [rsp] ; *text + lea rsi, [rel COMMA_RAX] + mov rdx, 2 + call vec_extend + + mov rdi, [rsp] ; *text + mov rsi, [rsp + 16] ; rhs + call codegen_write_operand + + mov byte [rsp + 31], 10 ; newline + mov rdi, [rsp + 8] ; *text + lea rsi, [rsp + 31] + call vec_push + + add rsp, 32 + pop rbp + ret + ;; rdi: *text ;; rsi: dst: *Operand ;; rdx: src: *Operand @@ -1458,7 +1864,15 @@ section .rdata MOV_RAX_COMMA_LEN equ $ - MOV_RAX_COMMA COMMA_RAX db ", rax" COMMA_RAX_LEN equ $ - COMMA_RAX + XOR_RDX_RDX db "xor rdx, rdx", 10 + XOR_RDX_RDX_LEN equ $ - XOR_RDX_RDX + ADD_ dq "add " + SUB_ dq "sub " + MUL_ dq "mul " + DIV_ dq "div " + ; Operand { kind: REGISTER, register: 0, width: 8, len: 0, padding: 0, value: 0 } align 8 OPERAND_RAX dq 0x0000_8001, 0 + OPERAND_RDX dq 0x0000_8301, 0 diff --git a/lang/tests/codegen.rs b/lang/tests/codegen.rs index cd9c1e7..ff9c270 100644 --- a/lang/tests/codegen.rs +++ b/lang/tests/codegen.rs @@ -93,7 +93,7 @@ fn main() { print_ast( b"fn main(a: u32) -> void { - return 4; + return 2 * 3 + 4 * 5; }", |ast| unsafe { parse_func(ast) }, );