; CALLING CONVENTION: ; Here is the process for calling a function: ; - the caller pushes the arguments on to the stack, from right to left ; - the caller subtracts sizeof(return type) from rsp, rounded up to the nearest 8 bytes ; - the caller calls the function ; - the caller stores away the return value ; - the caller adds (sizeof(return type) + sizeof arg0 + ... + sizeof argn) to rsp - where each sizeof is rounded up to the nearest 8 bytes ; STACK LAYOUT: ; arg n ; ... ; arg 0 ; return value [rbp+16] ; return address [rbp+8] ; old rbp [rbp] ; local variables global code_output global codegen_second_pass ; = 0 on first global pass, 1 on second global pass global functions_addresses ; ident list of addresses global functions_labels ; ident list of ident lists of label addresses global curr_function_labels ; ident list of labels for current function (written to in 1st pass, read from in 2nd pass) global curr_function_return_type #define REG_RAX 0 #define REG_RBX 3 #define REG_RCX 1 #define REG_RDX 2 #define REG_RSP 4 #define REG_RBP 5 #define REG_RSI 6 #define REG_RDI 7 function emit_byte argument byte *1code_output = byte code_output += 1 return function emit_bytes argument bytes argument count memcpy(code_output, bytes, count) code_output += count return function emit_word argument word *2code_output = word code_output += 2 return function emit_dword argument word *4code_output = word code_output += 4 return function emit_qword argument word *8code_output = word code_output += 8 return ; e.g. emit_mov_reg(REG_RAX, REG_RBX) emits mov rax, rbx function emit_mov_reg argument dest argument src local n if src ] 7 goto bad_reg if dest ] 7 goto bad_reg ;48 89 (DEST|SRC<<3|0xc0) *2code_output = 0x8948 code_output += 2 n = 0xc0 | dest n |= src < 3 *1code_output = n code_output += 1 return :bad_reg die(.str_bad_reg) :str_bad_reg string Internal compiler error: bad register passed to emit_mov_reg. byte 0 function emit_mov_rax_imm64 argument imm64 if imm64 == 0 goto rax_imm64_0 ; 48 b8 IMM64 *2code_output = 0xb848 code_output += 2 *8code_output = imm64 code_output += 8 return :rax_imm64_0 emit_zero_rax() return function emit_mov_rbx_imm64 ; 48 bb IMM64 argument imm64 *2code_output = 0xbb48 code_output += 2 *8code_output = imm64 code_output += 8 return function emit_add_rax_imm32 ; 48 05 IMM32 argument imm32 *2code_output = 0x0548 code_output += 2 *4code_output = imm32 code_output += 4 return function emit_zero_rax ; 31 c0 *2code_output = 0xc031 code_output += 2 return function emit_zero_rdx ; 31 d2 *2code_output = 0xd231 code_output += 2 return function emit_movsx_rax_al ; 48 0f be c0 *4code_output = 0xc0be0f48 code_output += 4 return function emit_movsx_rax_ax ; 48 0f bf c0 *4code_output = 0xc0bf0f48 code_output += 4 return function emit_movsx_rax_eax ; 48 63 c0 *2code_output = 0x6348 code_output += 2 *1code_output = 0xc0 code_output += 1 return function emit_movzx_rax_al ; 48 0f b6 c0 *4code_output = 0xc0b60f48 code_output += 4 return function emit_movzx_rax_ax ; 48 0f b7 c0 *4code_output = 0xc0b70f48 code_output += 4 return function emit_mov_eax_eax ; 89 c0 *2code_output = 0xc089 code_output += 2 return function emit_mov_al_byte_rbx ; 8a 03 *2code_output = 0x038a code_output += 2 return function emit_mov_byte_rbx_al ; 88 03 *2code_output = 0x0388 code_output += 2 return function emit_mov_ax_word_rbx ; 66 8b 03 *2code_output = 0x8b66 code_output += 2 *1code_output = 0x03 code_output += 1 return function emit_mov_word_rbx_ax ; 66 89 03 *2code_output = 0x8966 code_output += 2 *1code_output = 0x03 code_output += 1 return function emit_mov_eax_dword_rbx ; 8b 03 *2code_output = 0x038b code_output += 2 return function emit_mov_dword_rbx_eax ; 89 03 *2code_output = 0x0389 code_output += 2 return function emit_mov_rax_qword_rbx ; 48 8b 03 *2code_output = 0x8b48 code_output += 2 *1code_output = 0x03 code_output += 1 return function emit_mov_qword_rbx_rax ; 48 89 03 *2code_output = 0x8948 code_output += 2 *1code_output = 0x03 code_output += 1 return function emit_mov_qword_rsp_plus_imm32_rax argument imm32 ; 48 89 84 24 IMM32 *4code_output = 0x24848948 code_output += 4 *4code_output = imm32 code_output += 4 return function emit_mov_rax_qword_rsp_plus_imm32 argument imm32 ; 48 8b 84 24 IMM32 *4code_output = 0x24848b48 code_output += 4 *4code_output = imm32 code_output += 4 return function emit_mov_rax_qword_rsp emit_mov_rax_qword_rsp_plus_imm32(0) return function emit_mov_qword_rsp_rax emit_mov_qword_rsp_plus_imm32_rax(0) return function emit_sub_rsp_imm32 argument imm32 ;48 81 ec IMM32 *2code_output = 0x8148 code_output += 2 *1code_output = 0xec code_output += 1 *4code_output = imm32 code_output += 4 return function emit_mov_qword_rsp_rbp ; 48 89 2c 24 *4code_output = 0x242c8948 code_output += 4 return function emit_mov_rbp_qword_rsp ; 48 8b 2c 24 *4code_output = 0x242c8b48 code_output += 4 return function emit_add_rsp_imm32 argument imm32 ;48 81 c4 IMM32 *2code_output = 0x8148 code_output += 2 *1code_output = 0xc4 code_output += 1 *4code_output = imm32 code_output += 4 return function emit_ret *1code_output = 0xc3 code_output += 1 return function emit_call_rax ; ff d0 *2code_output = 0xd0ff code_output += 2 return function emit_push_rax ; 50 *1code_output = 0x50 code_output += 1 return function emit_pop_rax ; 58 *1code_output = 0x58 code_output += 1 return function emit_syscall ; 0f 05 *2code_output = 0x050f code_output += 2 return function emit_lea_rax_rbp_plus_imm32 ; 48 8d 85 IMM32 argument imm32 *2code_output = 0x8d48 code_output += 2 *1code_output = 0x85 code_output += 1 *4code_output = imm32 code_output += 4 return function emit_lea_rsp_rbp_plus_imm32 ; 48 8d a5 IMM32 argument imm32 *2code_output = 0x8d48 code_output += 2 *1code_output = 0xa5 code_output += 1 *4code_output = imm32 code_output += 4 return function emit_mov_rax_qword_rbp_plus_imm32 ; 48 8b 85 IMM32 argument imm32 *2code_output = 0x8b48 code_output += 2 *1code_output = 0x85 code_output += 1 *4code_output = imm32 code_output += 4 return function emit_rep_movsb ; f3 a4 *2code_output = 0xa4f3 code_output += 2 return function emit_movsq ; 48 a5 *2code_output = 0xa548 code_output += 2 return function emit_movq_rax_xmm0 ; 66 48 0f 7e c0 *4code_output = 0x7e0f4866 code_output += 4 *1code_output = 0xc0 code_output += 1 return function emit_movq_xmm0_rax ; 66 48 0f 6e c0 *4code_output = 0x6e0f4866 code_output += 4 *1code_output = 0xc0 code_output += 1 return function emit_movq_xmm1_rax ; 66 48 0f 6e c8 *4code_output = 0x6e0f4866 code_output += 4 *1code_output = 0xc8 code_output += 1 return function emit_movq_xmm1_xmm0 ; f3 0f 7e c8 *4code_output = 0xc87e0ff3 code_output += 4 return function emit_cvtss2sd_xmm0_xmm0 ; f3 0f 5a c0 *4code_output = 0xc05a0ff3 code_output += 4 return function emit_cvtsd2ss_xmm0_xmm0 ; f2 0f 5a c0 *4code_output = 0xc05a0ff2 code_output += 4 return function emit_cvttsd2si_rax_xmm0 ; f2 48 0f 2c c0 *4code_output = 0x2c0f48f2 code_output += 4 *1code_output = 0xc0 code_output += 1 return function emit_cvtsi2sd_xmm0_rax ; f2 48 0f 2a c0 *4code_output = 0x2a0f48f2 code_output += 4 *1code_output = 0xc0 code_output += 1 return function emit_addsd_xmm0_xmm1 *4code_output = 0xc1580ff2 code_output += 4 return function emit_subsd_xmm0_xmm1 *4code_output = 0xc15c0ff2 code_output += 4 return function emit_mulsd_xmm0_xmm1 *4code_output = 0xc1590ff2 code_output += 4 return function emit_divsd_xmm0_xmm1 *4code_output = 0xc15e0ff2 code_output += 4 return function emit_neg_rax ; 48 f7 d8 *2code_output = 0xf748 code_output += 2 *1code_output = 0xd8 code_output += 1 return function emit_not_rax ; 48 f7 d0 *2code_output = 0xf748 code_output += 2 *1code_output = 0xd0 code_output += 1 return function emit_add_rax_rbx ; 48 01 d8 *2code_output = 0x0148 code_output += 2 *1code_output = 0xd8 code_output += 1 return function emit_sub_rax_rbx ; 48 29 d8 *2code_output = 0x2948 code_output += 2 *1code_output = 0xd8 code_output += 1 return function emit_imul_rbx ; 48 f7 eb *2code_output = 0xf748 code_output += 2 *1code_output = 0xeb code_output += 1 return function emit_idiv_rbx ; 48 f7 fb *2code_output = 0xf748 code_output += 2 *1code_output = 0xfb code_output += 1 return function emit_mul_rbx ; 48 f7 e3 *2code_output = 0xf748 code_output += 2 *1code_output = 0xe3 code_output += 1 return function emit_div_rbx ; 48 f7 f3 *2code_output = 0xf748 code_output += 2 *1code_output = 0xf3 code_output += 1 return function emit_and_rax_rbx ; 48 21 d8 *2code_output = 0x2148 code_output += 2 *1code_output = 0xd8 code_output += 1 return function emit_or_rax_rbx ; 48 09 d8 *2code_output = 0x0948 code_output += 2 *1code_output = 0xd8 code_output += 1 return function emit_xor_rax_rbx ; 48 31 d8 *2code_output = 0x3148 code_output += 2 *1code_output = 0xd8 code_output += 1 return function emit_shl_rax_cl ; 48 d3 e0 *2code_output = 0xd348 code_output += 2 *1code_output = 0xe0 code_output += 1 return function emit_shr_rax_cl ; 48 d3 e8 *2code_output = 0xd348 code_output += 2 *1code_output = 0xe8 code_output += 1 return function emit_sar_rax_cl ; 48 d3 f8 *2code_output = 0xd348 code_output += 2 *1code_output = 0xf8 code_output += 1 return function emit_cqo ; 48 99 *2code_output = 0x9948 code_output += 2 return function emit_test_rax_rax ; 48 85 c0 *2code_output = 0x8548 code_output += 2 *1code_output = 0xc0 code_output += 1 return function emit_jmp_rel32 ; e9 REL32 argument rel32 *1code_output = 0xe9 code_output += 1 *4code_output = rel32 code_output += 4 return function emit_je_rel32 ; 0f 84 REL32 argument rel32 *2code_output = 0x840f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jne_rel32 ; 0f 85 REL32 argument rel32 *2code_output = 0x850f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jl_rel32 ; 0f 8c REL32 argument rel32 *2code_output = 0x8c0f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jg_rel32 ; 0f 8f REL32 argument rel32 *2code_output = 0x8f0f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jle_rel32 ; 0f 8e REL32 argument rel32 *2code_output = 0x8e0f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jge_rel32 ; 0f 8d REL32 argument rel32 *2code_output = 0x8d0f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jb_rel32 ; 0f 82 REL32 argument rel32 *2code_output = 0x820f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_ja_rel32 ; 0f 87 REL32 argument rel32 *2code_output = 0x870f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jbe_rel32 ; 0f 86 REL32 argument rel32 *2code_output = 0x860f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_jae_rel32 ; 0f 83 REL32 argument rel32 *2code_output = 0x830f code_output += 2 *4code_output = rel32 code_output += 4 return function emit_comisd_xmm0_xmm1 ; 66 0f 2f c1 *4code_output = 0xc12f0f66 code_output += 4 return ; make sure you put the return value in the proper place before calling this function generate_return emit_mov_reg(REG_RSP, REG_RBP) emit_mov_rbp_qword_rsp() emit_add_rsp_imm32(8) emit_ret() return ; copy sizeof(type) bytes, rounded up to the nearest 8, from rsi to rdi function generate_copy_rsi_to_rdi_qwords argument type local n n = type_sizeof(type) n = round_up_to_8(n) if n == 8 goto rsi2rdi_qwords_simple ; this is a struct or something, use rep movsb emit_mov_rax_imm64(n) emit_mov_reg(REG_RCX, REG_RAX) emit_rep_movsb() return :rsi2rdi_qwords_simple ; copy 8 bytes from rsi to rdi ; this is a little "optimization" over rep movsb with rcx = 8, mainly it just makes debugging easier (otherwise you'd need 8 `stepi`s in gdb to skip over the instruction) emit_movsq() return ; cast whatever was just pushed onto the stack from from_type to to_type ; `statement` is used for errors function generate_cast_top_of_stack argument statement argument from_type argument to_type local from local to local c local d from = types + from_type to = types + to_type if *1to == TYPE_VOID goto gen_cast_to_void if *1from == TYPE_VOID goto bad_gen_cast ; cast from void to something - that's bad if *1from == TYPE_ARRAY goto bad_gen_cast ; cast array (this probably won't ever happen because of decaying) if *1to == TYPE_ARRAY goto bad_gen_cast ; cast to array if *1from == TYPE_FUNCTION goto bad_gen_cast ; shouldn't happen if *1to == TYPE_FUNCTION goto bad_gen_cast ; shouldn't happen if *1to == TYPE_STRUCT goto gen_cast_to_struct if *1from == TYPE_STRUCT goto bad_gen_cast ; cast from struct to something else if *1to < TYPE_FLOAT goto gen_cast_to_integer if *1to == TYPE_POINTER goto gen_cast_to_integer ; pointers are basically integers ; cast to float/double if *1from == TYPE_POINTER goto bad_gen_cast ; pointer to float/double if *1to == *1from goto return_0 if *1from == TYPE_DOUBLE goto gen_cast_double_to_float if *1from == TYPE_FLOAT goto gen_cast_float_to_double ; int to float/double if *1to == TYPE_FLOAT goto gen_cast_int_to_float if *1to == TYPE_DOUBLE goto gen_cast_int_to_double goto bad_gen_cast ; in theory we shouldn't get here :gen_cast_to_void ; we need to handle rsp properly for stuff like: ; SomeLargeStruct s; ; (void)s; c = type_sizeof(from_type) c = round_up_to_8(c) c -= 8 if c == 0 goto return_0 ; int to void cast or something; we don't care emit_add_rsp_imm32(c) return :gen_cast_to_integer if *1from == *1to goto return_0 ; casting from type to same type if *1from == TYPE_POINTER goto return_0 ; no need to do anything ; cast float/double to integer if *1from == TYPE_FLOAT goto gen_cast_float_to_int if *1from == TYPE_DOUBLE goto gen_cast_double_to_int c = type_sizeof(*1from) d = type_sizeof(*1to) if d > c goto return_0 ; casting to bigger type, so we're good if d == 8 goto return_0 ; casting from unsigned/signed long to unsigned/signed long/pointer, we're good ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; now sign/zero extend the lower part of rax to the whole of rax if *1to == TYPE_CHAR goto gen_cast_integer_to_signed_char if *1to == TYPE_UNSIGNED_CHAR goto gen_cast_integer_to_unsigned_char if *1to == TYPE_SHORT goto gen_cast_integer_to_signed_short if *1to == TYPE_UNSIGNED_SHORT goto gen_cast_integer_to_unsigned_short if *1to == TYPE_INT goto gen_cast_integer_to_signed_int if *1to == TYPE_UNSIGNED_INT goto gen_cast_integer_to_unsigned_int goto bad_gen_cast ; in theory we shouldn't get here :int2int_cast_cont ; mov [rsp], rax emit_mov_qword_rsp_rax() return :gen_cast_integer_to_signed_char emit_movsx_rax_al() goto int2int_cast_cont :gen_cast_integer_to_unsigned_char emit_movzx_rax_al() goto int2int_cast_cont :gen_cast_integer_to_signed_short emit_movsx_rax_ax() goto int2int_cast_cont :gen_cast_integer_to_unsigned_short emit_movzx_rax_ax() goto int2int_cast_cont :gen_cast_integer_to_signed_int emit_movsx_rax_eax() goto int2int_cast_cont :gen_cast_integer_to_unsigned_int emit_mov_eax_eax() goto int2int_cast_cont :gen_cast_to_struct ; this is necessary because we add an implicit cast for return values ; so if we didn't have this, we wouldn't be able to return structs. if *1from != TYPE_STRUCT goto bad_gen_cast from += 1 to += 1 if *8from != *8to goto bad_gen_cast return ; no casting needed; these are the same type :gen_cast_double_to_float ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; movq xmm0, rax emit_movq_xmm0_rax() ; cvtsd2ss xmm0, xmm0 emit_cvtsd2ss_xmm0_xmm0() ; movq rax, xmm0 emit_movq_rax_xmm0() ; mov [rsp], rax emit_mov_qword_rsp_rax() return :gen_cast_float_to_double ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; movq xmm0, rax emit_movq_xmm0_rax() ; cvtss2sd xmm0, xmm0 emit_cvtss2sd_xmm0_xmm0() ; movq rax, xmm0 emit_movq_rax_xmm0() ; mov [rsp], rax emit_mov_qword_rsp_rax() return :gen_cast_int_to_float ; to reduce # of instructions, we first convert int to double, then double to float ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; cvtsi2sd xmm0, rax emit_cvtsi2sd_xmm0_rax() ; cvtsd2ss xmm0, xmm0 emit_cvtsd2ss_xmm0_xmm0() ; movq rax, xmm0 emit_movq_rax_xmm0() ; mov [rsp], rax emit_mov_qword_rsp_rax() ; it shouldn't matter that there's junk at [rsp+4] return :gen_cast_int_to_double ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; cvtsi2sd xmm0, rax emit_cvtsi2sd_xmm0_rax() ; movq rax, xmm0 emit_movq_rax_xmm0() ; mov [rsp], rax emit_mov_qword_rsp_rax() return :gen_cast_float_to_int ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; movq xmm0, rax emit_movq_xmm0_rax() ; convert float to double, then double to int ; cvtss2sd xmm0, xmm0 emit_cvtss2sd_xmm0_xmm0() ; cvttsd2si rax, xmm0 emit_cvttsd2si_rax_xmm0() ; mov [rsp], rax emit_mov_qword_rsp_rax() return :gen_cast_double_to_int ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; movq xmm0, rax emit_movq_xmm0_rax() ; cvttsd2si rax, xmm0 emit_cvttsd2si_rax_xmm0() ; mov [rsp], rax emit_mov_qword_rsp_rax() return :bad_gen_cast print_statement_location(statement) puts(.str_bad_gen_cast1) print_type(from_type) puts(.str_bad_gen_cast2) print_type(to_type) putc(10) exit(1) :str_bad_gen_cast1 string : Error: Cannot convert type byte 32 byte 0 :str_bad_gen_cast2 string to type byte 32 byte 0 ; push expr, casted to to_type, onto the stack ; returns pointer to end of expr function generate_push_expression_casted argument statement argument expr argument to_type local from_type from_type = expr + 4 from_type = *4from_type expr = generate_push_expression(statement, expr) generate_cast_top_of_stack(statement, from_type, to_type) return expr ; if type is a pointer type, returns the size of the underlying type ; otherwise, returns 1 ; this is so that (int *)p + 5 adds 20 to p, instead of 5 function scale_rax_for_addition_with argument type local p p = types + type if *1p != TYPE_POINTER goto return_0 local n p = type + 1 n = type_sizeof(p) ; now scale rax by n emit_mov_rbx_imm64(n) emit_mul_rbx() return ; pop the top two things off of the stack, and push their sum ; the things should both have type `out_type` on the stack, but their original types are given by type1,2 function generate_stack_add argument statement ; for errors (currently unused) argument type1 ; type of 1st operand argument type2 ; type of 2nd operand argument out_type local p p = types + out_type if *1p == TYPE_FLOAT goto generate_add_floats if *1p == TYPE_DOUBLE goto generate_add_doubles emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) scale_rax_for_addition_with(type1) ; in case this is a pointer addition emit_mov_reg(REG_RSI, REG_RAX) ; mov rsi, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) scale_rax_for_addition_with(type2) ; in case this is a pointer addition emit_mov_reg(REG_RBX, REG_RSI) ; mov rbx, rsi emit_add_rax_rbx() ; add rax, rbx emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, out_type) return :generate_add_floats emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_movq_xmm1_xmm0() ; movq xmm1, xmm0 emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_addsd_xmm0_xmm1() ; addsd xmm0, xmm1 emit_cvtsd2ss_xmm0_xmm0() ; cvtsd2ss xmm0, xmm0 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return :generate_add_doubles emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm1_rax() ; movq xmm1, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_addsd_xmm0_xmm1() ; addsd xmm0, xmm1 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return ; pop the top two things off of the stack, and push their difference ; the things should both have type `out_type` on the stack, but their original types are given by type1,2 function generate_stack_sub argument statement ; for errors argument type1 ; type of 1st operand argument type2 ; type of 2nd operand argument out_type local p p = types + out_type if *1p == TYPE_FLOAT goto generate_sub_floats if *1p == TYPE_DOUBLE goto generate_sub_doubles p = types + type2 if *1p == TYPE_POINTER goto generate_sub_pointers emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) scale_rax_for_addition_with(type1) ; in case this is a pointer - integer subtraction emit_mov_reg(REG_RSI, REG_RAX) ; mov rsi, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_mov_reg(REG_RBX, REG_RSI) ; mov rbx, rsi emit_sub_rax_rbx() ; sub rax, rbx emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, out_type) return :generate_sub_pointers ; pointer difference - need to divide by object size local sz1 local sz2 p = types + type1 if *1p != TYPE_POINTER goto bad_pointer_diff p = type1 + 1 sz1 = type_sizeof(p) p = type2 + 1 sz2 = type_sizeof(p) if sz1 != sz2 goto bad_pointer_diff emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RSI, REG_RAX) ; mov rsi, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_mov_reg(REG_RBX, REG_RSI) ; mov rbx, rsi emit_sub_rax_rbx() ; sub rax, rbx emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_rbx_imm64(sz1) ; mov rbx, (object size) emit_zero_rdx() ; xor edx, edx emit_div_rbx() ; div rbx emit_mov_qword_rsp_rax() ; mov [rsp], rax return :bad_pointer_diff statement_error(statement, .str_bad_pointer_diff) :str_bad_pointer_diff string Subtraction of incompatible pointer types. byte 0 :generate_sub_floats emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_movq_xmm1_xmm0() ; movq xmm1, xmm0 emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_subsd_xmm0_xmm1() ; subsd xmm0, xmm1 emit_cvtsd2ss_xmm0_xmm0() ; cvtsd2ss xmm0, xmm0 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return :generate_sub_doubles emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm1_rax() ; movq xmm1, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_subsd_xmm0_xmm1() ; subsd xmm0, xmm1 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return ; pop the top two things off of the stack, and push their product function generate_stack_mul argument statement ; for errors argument type local p p = types + type if *1p == TYPE_FLOAT goto generate_mul_floats if *1p == TYPE_DOUBLE goto generate_mul_doubles emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_mul_rbx() ; mul rbx emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return :generate_mul_floats emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_movq_xmm1_xmm0() ; movq xmm1, xmm0 emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_mulsd_xmm0_xmm1() ; mulsd xmm0, xmm1 emit_cvtsd2ss_xmm0_xmm0() ; cvtsd2ss xmm0, xmm0 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return :generate_mul_doubles emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm1_rax() ; movq xmm1, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_mulsd_xmm0_xmm1() ; mulsd xmm0, xmm1 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return ; pop the top two things off of the stack, and push their quotient function generate_stack_div argument statement ; for errors argument type local p local c p = types + type if *1p == TYPE_FLOAT goto generate_div_floats if *1p == TYPE_DOUBLE goto generate_div_doubles emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) c = *1p & 1 if c == 1 goto generate_div_signed emit_zero_rdx() ; xor edx, edx emit_div_rbx() ; div rbx goto generate_div_cont :generate_div_signed emit_cqo() ; cqo emit_idiv_rbx() ; idiv rbx :generate_div_cont emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return :generate_div_floats emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_movq_xmm1_xmm0() ; movq xmm1, xmm0 emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_cvtss2sd_xmm0_xmm0() ; cvtss2sd xmm0, xmm0 emit_divsd_xmm0_xmm1() ; divsd xmm0, xmm1 emit_cvtsd2ss_xmm0_xmm0() ; cvtsd2ss xmm0, xmm0 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return :generate_div_doubles emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_movq_xmm1_rax() ; movq xmm1, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_movq_xmm0_rax() ; movq xmm0, rax emit_divsd_xmm0_xmm1() ; divsd xmm0, xmm1 emit_movq_rax_xmm0() ; movq rax, xmm0 emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax return ; pop the top two things off of the stack, and push their remainder function generate_stack_remainder argument statement ; for errors argument type local p local c p = types + type emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) c = *1p & 1 if c == 1 goto generate_remainder_signed emit_zero_rdx() ; xor edx, edx emit_div_rbx() ; div rbx emit_mov_reg(REG_RAX, REG_RDX) ; mov rax, rdx goto generate_remainder_cont :generate_remainder_signed emit_cqo() ; cqo emit_idiv_rbx() ; idiv rbx emit_mov_reg(REG_RAX, REG_RDX) ; mov rax, rdx :generate_remainder_cont emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return ; pop the top two things off of the stack, and push their bitwise and function generate_stack_bitwise_and argument statement ; for errors argument type emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_and_rax_rbx() ; and rax, rbx emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return ; pop the top two things off of the stack, and push their bitwise or function generate_stack_bitwise_or argument statement ; for errors argument type emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_or_rax_rbx() ; or rax, rbx emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return ; pop the top two things off of the stack, and push their bitwise xor function generate_stack_bitwise_xor argument statement ; for errors argument type emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_xor_rax_rbx() ; xor rax, rbx emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return function generate_stack_lshift argument statement ; for errors argument type emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RCX, REG_RAX) ; mov rcx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) emit_shl_rax_cl() ; shl rax, cl emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return function generate_stack_rshift argument statement ; for errors argument type local p local c p = types + type c = *1p & 1 emit_mov_rax_qword_rsp_plus_imm32(0) ; mov rax, [rsp] (second operand) emit_mov_reg(REG_RCX, REG_RAX) ; mov rcx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] (first operand) if c == 1 goto gen_rshift_signed emit_shr_rax_cl() ; shr rax, cl goto gen_rshift_cont :gen_rshift_signed emit_sar_rax_cl() ; sar rax, cl :gen_rshift_cont emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return ; pop a pointer off of the stack, then push the dereferenced value according to `type` function generate_stack_dereference argument statement ; for errors argument type local p local size local c size = type_sizeof(type) if size == 1 goto gen_deref1 if size == 2 goto gen_deref2 if size == 4 goto gen_deref4 if size == 8 goto gen_deref8 emit_pop_rax() ; pop rax emit_mov_reg(REG_RSI, REG_RAX) ; mov rsi, rax c = round_up_to_8(size) emit_sub_rsp_imm32(c) ; sub rsp, (size) emit_mov_reg(REG_RDI, REG_RSP) ; mov rdi, rsp emit_mov_rax_imm64(size) ; mov rax, (size) emit_mov_reg(REG_RCX, REG_RAX) ; mov rcx, rax emit_rep_movsb() ; rep movsb return :gen_deref_cast emit_mov_qword_rsp_rax() p = types + type if *1p >= TYPE_LONG goto return_0 generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return :gen_deref1 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_al_byte_rbx() ; mov al, [rbx] goto gen_deref_cast :gen_deref2 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_ax_word_rbx() ; mov ax, [rbx] goto gen_deref_cast :gen_deref4 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_eax_dword_rbx() ; mov eax, [rbx] goto gen_deref_cast :gen_deref8 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rbx() ; mov rax, [rbx] goto gen_deref_cast ; pop address off of stack, pop value, set *address to value, then push value function generate_stack_assign argument statement argument type local size size = type_sizeof(type) if size == 1 goto gen_assign1 if size == 2 goto gen_assign2 if size == 4 goto gen_assign4 if size == 8 goto gen_assign8 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RDI, REG_RAX) ; mov rdi, rax emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_reg(REG_RSI, REG_RSP) ; mov rsi, rsp emit_mov_rax_imm64(size) ; mov rax, (size) emit_mov_reg(REG_RCX, REG_RAX) ; mov rcx, rax emit_rep_movsb() ; rep movsb return :gen_assign_ret emit_add_rsp_imm32(8) ; pop address return :gen_assign1 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] emit_mov_byte_rbx_al() ; mov [rbx], al goto gen_assign_ret :gen_assign2 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] emit_mov_word_rbx_ax() ; mov [rbx], ax goto gen_assign_ret :gen_assign4 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] emit_mov_dword_rbx_eax() ; mov [rbx], eax goto gen_assign_ret :gen_assign8 emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_qword_rsp_plus_imm32(8) ; mov rax, [rsp+8] emit_mov_qword_rbx_rax() ; mov [rbx], rax goto gen_assign_ret ; pop from the stack and compare with 0 (setting eflags appropriately) function generate_stack_compare_against_zero argument statement argument type local p p = types + type if *1p == TYPE_FLOAT goto cmp_zero_float if *1p == TYPE_DOUBLE goto cmp_zero_double emit_add_rsp_imm32(8) ; add rsp, 8 emit_mov_rax_qword_rsp_plus_imm32(-8) ; mov rax, [rsp-8] emit_test_rax_rax() ; test rax, rax return :cmp_zero_float generate_cast_top_of_stack(statement, TYPE_FLOAT, TYPE_DOUBLE) ; cast to double for comparison :cmp_zero_double emit_add_rsp_imm32(8) ; add rsp, 8 emit_zero_rax() ; xor eax, eax emit_movq_xmm1_rax() ; movq xmm1, rax emit_mov_rax_qword_rsp_plus_imm32(-8) ; mov rax, [rsp-8] emit_movq_xmm0_rax() ; movq xmm0, rax emit_comisd_xmm0_xmm1() ; comisd xmm0, xmm1 return ; returns pointer to end of expr function generate_push_address_of_expression argument statement ; for errors argument expr local c local d c = *1expr if c == EXPRESSION_GLOBAL_VARIABLE goto addrof_global_var if c == EXPRESSION_LOCAL_VARIABLE goto addrof_local_var if c == EXPRESSION_DEREFERENCE goto addrof_dereference if c == EXPRESSION_SUBSCRIPT goto addrof_subscript if c == EXPRESSION_DOT goto addrof_dot if c == EXPRESSION_ARROW goto addrof_arrow statement_error(statement, .str_bad_lvalue) :str_bad_lvalue string Bad l-value. byte 0 :addrof_global_var expr += 8 emit_mov_rax_imm64(*4expr) emit_push_rax() expr += 8 return expr :addrof_local_var expr += 8 emit_lea_rax_rbp_plus_imm32(*4expr) emit_push_rax() expr += 8 return expr :addrof_dereference expr += 8 return generate_push_expression(statement, expr) :addrof_subscript expr += 8 c = expr + 4 ; type 1 c = *4c expr = generate_push_expression(statement, expr) d = expr + 4 ; type 2 d = *4d expr = generate_push_expression(statement, expr) generate_stack_add(statement, c, d, c) return expr :addrof_dot expr += 8 expr = generate_push_address_of_expression(statement, expr) goto addrof_dot_cont :addrof_arrow expr += 8 expr = generate_push_expression(statement, expr) :addrof_dot_cont emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_imm64(*8expr) ; mov rax, (offset to member) emit_add_rax_rbx() ; add rax, rbx emit_mov_qword_rsp_rax() ; mov [rsp], rax expr += 8 return expr ; `statement` is used for errors ; returns pointer to end of expression function generate_push_expression argument statement argument expr local b local c local d local p local addr1 local addr2 local type type = expr + 4 type = *4type c = *1expr if c == EXPRESSION_CONSTANT_INT goto generate_int if c == EXPRESSION_CONSTANT_FLOAT goto generate_float if c == EXPRESSION_GLOBAL_VARIABLE goto generate_global_variable if c == EXPRESSION_LOCAL_VARIABLE goto generate_local_variable if c == EXPRESSION_FUNCTION goto generate_function_addr if c == EXPRESSION_CAST goto generate_cast if c == EXPRESSION_UNARY_PLUS goto generate_cast ; the unary plus operator just casts to the promoted type if c == EXPRESSION_UNARY_MINUS goto generate_unary_minus if c == EXPRESSION_BITWISE_NOT goto generate_unary_bitwise_not if c == EXPRESSION_LOGICAL_NOT goto generate_unary_logical_not if c == EXPRESSION_ADD goto generate_add if c == EXPRESSION_SUB goto generate_sub if c == EXPRESSION_MUL goto generate_mul if c == EXPRESSION_DIV goto generate_div if c == EXPRESSION_REMAINDER goto generate_remainder if c == EXPRESSION_BITWISE_AND goto generate_bitwise_and if c == EXPRESSION_BITWISE_OR goto generate_bitwise_or if c == EXPRESSION_BITWISE_XOR goto generate_bitwise_xor if c == EXPRESSION_LSHIFT goto generate_lshift if c == EXPRESSION_RSHIFT goto generate_rshift if c == EXPRESSION_ASSIGN goto generate_assign if c == EXPRESSION_DEREFERENCE goto generate_dereference if c == EXPRESSION_SUBSCRIPT goto generate_subscript if c == EXPRESSION_ADDRESS_OF goto generate_address_of if c == EXPRESSION_DOT goto generate_dot_or_arrow if c == EXPRESSION_ARROW goto generate_dot_or_arrow if c == EXPRESSION_COMMA goto generate_comma if c == EXPRESSION_CALL goto generate_call if c == EXPRESSION_LOGICAL_AND goto generate_logical_and if c == EXPRESSION_LOGICAL_OR goto generate_logical_or if c == EXPRESSION_CONDITIONAL goto generate_conditional putnln(c) die(.str_genpushexprNI) :str_genpushexprNI string generate_push_expression not implemented. byte 0 :generate_cast expr += 8 expr = generate_push_expression_casted(statement, expr, type) return expr :generate_function_addr d = 1 ; default to address of 1, not 0 because that will be optimized to xor eax, eax expr += 8 if codegen_second_pass == 0 goto function_noaddr d = ident_list_lookup(functions_addresses, *8expr) if d == 0 goto no_such_function :function_noaddr expr += 8 emit_mov_rax_imm64(d) emit_push_rax() return expr :no_such_function print_statement_location(statement) puts(.str_no_such_function) putsln(*8expr) exit(1) :str_no_such_function string : Error: No such function: byte 32 byte 0 :generate_unary_minus expr += 8 expr = generate_push_expression_casted(statement, expr, type) p = types + type if *1p == TYPE_FLOAT goto generate_unary_minus_float if *1p == TYPE_DOUBLE goto generate_unary_minus_double ; it's just an integer emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_neg_rax() ; neg rax emit_mov_qword_rsp_rax() ; mov [rsp], rax return expr ; "negate(x) copies a floating-point operand x to a destination in the same format, reversing the sign bit." IEEE 754 ยง 5.5.1 :generate_unary_minus_float c = 1 < 31 ; sign bit for floats goto generate_unary_minus_floating :generate_unary_minus_double c = 1 < 63 ; sign bit for doubles :generate_unary_minus_floating emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_mov_reg(REG_RBX, REG_RAX) ; mov rbx, rax emit_mov_rax_imm64(c) ; mov rax, (sign bit) emit_xor_rax_rbx() ; xor rax, rbx emit_mov_qword_rsp_rax() ; mov [rsp], rax return expr :generate_unary_bitwise_not expr += 8 expr = generate_push_expression(statement, expr) ; we'll cast after we take the bitwise not. emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_not_rax() ; not rax emit_mov_qword_rsp_rax() ; mov [rsp], rax generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return expr :generate_address_of expr += 8 expr = generate_push_address_of_expression(statement, expr) return expr :generate_assign expr += 8 b = type_is_array(type) if b != 0 goto assign_array p = expression_get_end(expr) c = p ; it makes things a lot easier if we push the rhs of the assignment first -- also ; *f() = g() ; it might be required to call g first? (something something sequence points) p = generate_push_expression_casted(statement, p, type) d = generate_push_address_of_expression(statement, expr) if c != d goto exprend_wrong expr = p generate_stack_assign(statement, type) return expr :assign_array statement_error(statement, .str_assign_array) :str_assign_array string Assigning to array. byte 0 :exprend_wrong die(.str_exprend_wrong) :str_exprend_wrong string Internal compiler error: expression_get_end disagrees with generate_push_expression. byte 0 :generate_add expr += 8 c = expr + 4 ; type of 1st operand expr = generate_push_expression_casted(statement, expr, type) d = expr + 4 ; type of 2nd operand expr = generate_push_expression_casted(statement, expr, type) generate_stack_add(statement, *4c, *4d, type) return expr :generate_sub expr += 8 c = expr + 4 ; type of 1st operand expr = generate_push_expression_casted(statement, expr, type) d = expr + 4 ; type of 2nd operand expr = generate_push_expression_casted(statement, expr, type) generate_stack_sub(statement, *4c, *4d, type) return expr :generate_mul expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_mul(statement, type) return expr :generate_div expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_div(statement, type) return expr :generate_remainder expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_remainder(statement, type) return expr :generate_bitwise_and expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_bitwise_and(statement, type) return expr :generate_bitwise_or expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_bitwise_or(statement, type) return expr :generate_bitwise_xor expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_bitwise_xor(statement, type) return expr :generate_lshift expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_lshift(statement, type) return expr :generate_rshift expr += 8 expr = generate_push_expression_casted(statement, expr, type) expr = generate_push_expression_casted(statement, expr, type) generate_stack_rshift(statement, type) return expr :generate_unary_logical_not expr += 8 p = expr + 4 expr = generate_push_expression(statement, expr) generate_stack_compare_against_zero(statement, *4p) emit_je_rel32(7) ; je +7 (2 bytes for xor eax, eax; 5 bytes for jmp +10) emit_zero_rax() ; xor eax, eax emit_jmp_rel32(10) ; jmp +10 (10 bytes for mov rax, 1) emit_mov_rax_imm64(1) ; mov rax, 1 emit_push_rax() ; push rax return expr :generate_conditional expr += 8 p = expr + 4 expr = generate_push_expression(statement, expr) generate_stack_compare_against_zero(statement, *4p) emit_je_rel32(0) ; temporary je +0 (correct offset will be filled in) addr1 = code_output expr = generate_push_expression_casted(statement, expr, type) emit_jmp_rel32(0) ; temporary jmp +0 (correct offset will be filled in) addr2 = code_output ; fill in jump offset d = addr2 - addr1 addr1 -= 4 *4addr1 = d addr1 = code_output expr = generate_push_expression_casted(statement, expr, type) addr2 = code_output ; fill in jump offset d = addr2 - addr1 addr1 -= 4 *4addr1 = d return expr :generate_logical_and expr += 8 p = expr + 4 expr = generate_push_expression(statement, expr) generate_stack_compare_against_zero(statement, *4p) emit_je_rel32(0) ; temporary je +0; offset will be filled in later addr1 = code_output p = expr + 4 expr = generate_push_expression(statement, expr) generate_stack_compare_against_zero(statement, *4p) emit_je_rel32(15) ; je +15 (10 bytes for mov rax, 1; 5 bytes for jmp +2) emit_mov_rax_imm64(1) ; mov rax, 1 emit_jmp_rel32(2) ; jmp +2 (skip xor rax, rax) addr2 = code_output emit_zero_rax() ; xor rax, rax emit_push_rax() ; push rax ; fill in jump offset d = addr2 - addr1 addr1 -= 4 *4addr1 = d return expr :generate_logical_or expr += 8 p = expr + 4 expr = generate_push_expression(statement, expr) generate_stack_compare_against_zero(statement, *4p) emit_jne_rel32(0) ; temporary jne +0; offset will be filled in later addr1 = code_output p = expr + 4 expr = generate_push_expression(statement, expr) generate_stack_compare_against_zero(statement, *4p) emit_jne_rel32(7) ; jne +7 (2 bytes for xor eax, eax; 5 bytes for jmp +10) emit_zero_rax() ; xor eax, eax emit_jmp_rel32(10) ; jmp +10 (skip mov rax, 1) addr2 = code_output emit_mov_rax_imm64(1) ; mov rax, 1 emit_push_rax() ; push rax ; fill in jump offset d = addr2 - addr1 addr1 -= 4 *4addr1 = d return expr :generate_global_variable expr += 8 d = *4expr ; address expr += 4 b = *4expr ; is array? expr += 4 if b != 0 goto global_var_array c = type_sizeof(type) if c > 8 goto global_var_large emit_mov_rbx_imm64(d) ; mov rbx, (address) emit_mov_rax_qword_rbx() ; mov rax, [rbx] emit_push_rax() ; push rax p = types + type if *1p < TYPE_LONG goto global_var_needs_cast return expr :global_var_needs_cast ; we need to sign extend 8/16/32-bit signed global variables to 64 bits generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return expr :global_var_large c = round_up_to_8(c) emit_sub_rsp_imm32(c) ; sub rsp, (size) emit_mov_reg(REG_RDI, REG_RSP) ; mov rdi, rsp emit_mov_rax_imm64(d) ; mov rax, (address) emit_mov_reg(REG_RSI, REG_RAX) ; mov rsi, rax emit_mov_rax_imm64(c) ; mov rax, (size) emit_mov_reg(REG_RCX, REG_RAX) ; mov rcx, rax emit_rep_movsb() ; rep movsb return expr :global_var_array ; just push the address of the array emit_mov_rax_imm64(d) ; mov rax, (address) emit_push_rax() ; push rax return expr :generate_dereference expr += 8 p = expr + 4 expr = generate_push_expression(statement, expr) p = types + *4p if *2p == TYPE2_FUNCTION_POINTER goto deref_function_pointer generate_stack_dereference(statement, type) :deref_function_pointer ; dereferencing a function pointer does NOTHING return expr :generate_subscript expr += 8 c = expr + 4 ; type 1 c = *4c expr = generate_push_expression(statement, expr) d = expr + 4 ; type 2 d = *4d expr = generate_push_expression(statement, expr) generate_stack_add(statement, c, d, c) generate_stack_dereference(statement, type) return expr :generate_dot_or_arrow ; @NONSTANDARD: we require that the 1st operand to . be an lvalue ; e.g. int thing = function_which_returns_struct().x; ; is not allowed expr = generate_push_address_of_expression(statement, expr) generate_stack_dereference(statement, type) return expr :generate_local_variable expr += 8 d = sign_extend_32_to_64(*4expr) ; rbp offset expr += 4 b = *4expr ; is array? expr += 4 if b != 0 goto local_var_array c = type_sizeof(type) if c > 8 goto local_var_large emit_mov_rax_qword_rbp_plus_imm32(d) ; mov rax, [rbp+X] emit_push_rax() ; push rax p = types + type if *1p < TYPE_LONG goto local_var_needs_cast return expr :local_var_needs_cast generate_cast_top_of_stack(statement, TYPE_UNSIGNED_LONG, type) return expr :local_var_large c = round_up_to_8(c) emit_sub_rsp_imm32(c) ; sub rsp, (size) emit_mov_reg(REG_RDI, REG_RSP) ; mov rdi, rsp emit_lea_rax_rbp_plus_imm32(d) ; lea rax, [rbp+X] emit_mov_reg(REG_RSI, REG_RAX) ; mov rsi, rax emit_mov_rax_imm64(c) ; mov rax, (size) emit_mov_reg(REG_RCX, REG_RAX) ; mov rcx, rax emit_rep_movsb() ; rep movsb return expr :local_var_array ; push address of array instead of array emit_lea_rax_rbp_plus_imm32(d) ; lea rax, [rbp+X] emit_push_rax() ; push rax return expr :generate_float expr += 8 emit_mov_rax_imm64(*8expr) emit_push_rax() generate_cast_top_of_stack(statement, TYPE_DOUBLE, type) expr += 8 return expr :generate_int expr += 8 emit_mov_rax_imm64(*8expr) emit_push_rax() expr += 8 return expr :generate_comma expr += 8 c = expr + 4 ; type of 1st expression c = *4c expr = generate_push_expression(statement, expr) c = type_sizeof(c) c = round_up_to_8(c) emit_add_rsp_imm32(c) ; add rsp, (size of expression value on stack) expr = generate_push_expression(statement, expr) return expr :generate_call expr += 8 global 4000 expr_arg_ptrs_dat local expr_arg_ptrs expr_arg_ptrs = &expr_arg_ptrs_dat local arg_idx local call_function local return_val_size local args_size return_val_size = type_sizeof(type) return_val_size = round_up_to_8(return_val_size) call_function = expr expr = expression_get_end(expr) args_size = 0 ; we have to do a bit of work because the arguments are stored ; left to right, but pushed right to left arg_idx = 0 p = expr_arg_ptrs :find_call_args_loop if *1expr == 0 goto find_call_args_loop_end *8p = expr c = expr + 4 c = type_sizeof(*4c) c = round_up_to_8(c) args_size += c expr = expression_get_end(expr) p += 8 arg_idx += 1 goto find_call_args_loop :find_call_args_loop_end expr += 8 :push_args_loop if arg_idx == 0 goto push_args_loop_end p -= 8 generate_push_expression(statement, *8p) arg_idx -= 1 goto push_args_loop :push_args_loop_end ; create space on stack for return value emit_sub_rsp_imm32(return_val_size) ; put function in rax generate_push_expression(statement, call_function) emit_mov_rax_qword_rsp() emit_add_rsp_imm32(8) ; call emit_call_rax() if return_val_size > 8 goto generate_big_return emit_mov_rax_qword_rsp() ; mov rax, [rsp] emit_add_rsp_imm32(args_size) ; add rsp, (size of arguments on stack) emit_mov_qword_rsp_rax() ; mov [rsp], rax return expr :generate_big_return ; now we need to copy the return value to the proper place in the stack ; this is kind of annoying because that might overlap with the current position. ; so, we'll push a copy of the return value, then copy that over to the proper place. local copy_offset ; first copy copy_offset = 0 - return_val_size emit_mov_reg(REG_RSI, REG_RSP) emit_mov_reg(REG_RAX, REG_RSP) emit_add_rax_imm32(copy_offset) emit_mov_reg(REG_RDI, REG_RAX) generate_copy_rsi_to_rdi_qwords(type) ; second copy emit_mov_reg(REG_RAX, REG_RSP) emit_add_rax_imm32(copy_offset) emit_mov_reg(REG_RSI, REG_RAX) emit_mov_reg(REG_RAX, REG_RSP) emit_add_rax_imm32(args_size) emit_mov_reg(REG_RDI, REG_RAX) generate_copy_rsi_to_rdi_qwords(type) emit_add_rsp_imm32(args_size) return function generate_statement argument statement local dat1 local dat2 local dat3 local dat4 local n local p local c dat1 = statement + 8 dat1 = *8dat1 dat2 = statement + 16 dat2 = *8dat2 dat3 = statement + 24 dat3 = *8dat3 dat4 = statement + 32 dat4 = *8dat4 c = *1statement if c == STATEMENT_BLOCK goto gen_block if c == STATEMENT_RETURN goto gen_return if c == STATEMENT_LOCAL_DECLARATION goto gen_local_decl if c == STATEMENT_EXPRESSION goto gen_stmt_expr ; @TODO die(.str_genstmtNI) :str_genstmtNI string generate_statement not implemented. byte 0 :gen_block :gen_block_loop if *1dat1 == 0 goto gen_block_loop_end generate_statement(dat1) dat1 += 40 goto gen_block_loop :gen_block_loop_end return :gen_return if dat1 == 0 goto gen_return_noexpr generate_push_expression_casted(statement, dat1, curr_function_return_type) ; copy sizeof(return expression) rounded up to 8 bytes from [rsp] to [rbp+16] emit_mov_reg(REG_RSI, REG_RSP) emit_lea_rax_rbp_plus_imm32(16) emit_mov_reg(REG_RDI, REG_RAX) generate_copy_rsi_to_rdi_qwords(curr_function_return_type) :gen_return_noexpr generate_return() return :gen_local_decl c = type_sizeof(dat2) c = round_up_to_8(c) if dat3 != 0 goto gen_local_decl_initializer ; move the stack pointer to the start of the variable dat1 += c dat1 = 0 - dat1 emit_lea_rsp_rbp_plus_imm32(dat1) if dat4 != 0 goto gen_local_decl_data_initializer return :gen_local_decl_initializer dat1 = 0 - dat1 ; move the stack pointer to the end of the variable emit_lea_rsp_rbp_plus_imm32(dat1) ; push the expression generate_push_expression_casted(statement, dat3, dat2) return :gen_local_decl_data_initializer emit_mov_rax_imm64(dat4) ; mov rax, (data address) emit_mov_reg(REG_RSI, REG_RAX) ; mov rsi, rax emit_mov_reg(REG_RDI, REG_RSP) ; mov rdi, rsp emit_mov_rax_imm64(c) ; mov rax, (size) emit_mov_reg(REG_RCX, REG_RAX) ; mov rcx, rax emit_rep_movsb() ; rep movsb return :gen_stmt_expr generate_push_expression_casted(statement, dat1, TYPE_VOID) ; since we casted to void, it'll always be 8 bytes on the stack emit_add_rsp_imm32(8) return function generate_function argument function_name argument function_statement local function_type local out0 function_type = ident_list_lookup(function_types, function_name) curr_function_return_type = functype_return_type(function_type) if codegen_second_pass != 0 goto genf_second_pass curr_function_labels = ident_list_create(4000) ; ~ 200 labels per function should be plenty ident_list_add(functions_labels, function_name, curr_function_labels) goto genf_cont :genf_second_pass curr_function_labels = ident_list_lookup(functions_labels, function_name) :genf_cont ; prologue emit_sub_rsp_imm32(8) emit_mov_qword_rsp_rbp() emit_mov_reg(REG_RBP, REG_RSP) generate_statement(function_statement) ; implicit return at end of function generate_return() return function generate_functions local addr local c local p local function_name function_name = function_statements :genfunctions_loop if *1function_name == 0 goto genfunctions_loop_end addr = code_output - output_file_data ; address of this function if codegen_second_pass != 0 goto genfs_check_addr ; first pass; record address of function ident_list_add(functions_addresses, function_name, addr) goto genfs_cont :genfs_check_addr c = ident_list_lookup(functions_addresses, function_name) if c != addr goto function_addr_mismatch goto genfs_cont :genfs_cont p = memchr(function_name, 0) p += 1 generate_function(function_name, *8p) function_name = p + 8 goto genfunctions_loop :genfunctions_loop_end return :function_addr_mismatch ; address of function on 2nd pass doesn't line up with 1st pass puts(.str_function_addr_mismatch) putsln(function_name) exit(1) :str_function_addr_mismatch string Function address on first pass doesn't match 2nd pass: byte 32 byte 0 ; emit ELF header and code. function generate_code code_output = output_file_data emit_qword(0x00010102464c457f) ; elf identifier, 64-bit little endian, ELF version 1 emit_qword(0) ; reserved emit_word(2) ; executable file emit_word(0x3e) ; architecture x86-64 emit_dword(1) ; ELF version 1 emit_qword(ENTRY_ADDR) ; entry point emit_qword(0x40) ; program header table offset emit_qword(0) ; section header table offset emit_dword(0) ; flags emit_word(0x40) ; size of header emit_word(0x38) ; size of program header emit_word(3) ; # of program headers = 3 (code, rwdata, rodata) emit_word(0) ; size of section header emit_word(0) ; # of section headers emit_word(0) ; index of .shstrtab ; from /usr/include/elf.h: ;#define PF_X (1 << 0) /* Segment is executable */ ;#define PF_W (1 << 1) /* Segment is writable */ ;#define PF_R (1 << 2) /* Segment is readable */ ; program header 1 (code) emit_dword(1) ; loadable segment emit_dword(1) ; execute only emit_qword(ENTRY_ADDR) ; offset in file emit_qword(ENTRY_ADDR) ; virtual address emit_qword(0) ; physical address emit_qword(TOTAL_CODE_SIZE) ; size in executable file emit_qword(TOTAL_CODE_SIZE) ; size when loaded into memory emit_qword(4096) ; alignment ; program header 2 (rodata) emit_dword(1) ; loadable segment emit_dword(4) ; read only emit_qword(RODATA_ADDR) ; offset in file emit_qword(RODATA_ADDR) ; virtual address emit_qword(0) ; physical address emit_qword(RODATA_SIZE) ; size in executable file emit_qword(RODATA_SIZE) ; size when loaded into memory emit_qword(4096) ; alignment ; program header 3 (rwdata) emit_dword(1) ; loadable segment emit_dword(6) ; read/write emit_qword(RWDATA_ADDR) ; offset in file emit_qword(RWDATA_ADDR) ; virtual address emit_qword(0) ; physical address emit_qword(RWDATA_SIZE) ; size in executable file emit_qword(RWDATA_SIZE) ; size when loaded into memory emit_qword(4096) ; alignment local p_func code_output = output_file_data + FUNCTIONS_ADDR codegen_second_pass = 0 generate_functions() code_output = output_file_data + FUNCTIONS_ADDR codegen_second_pass = 1 generate_functions() ; generate code at the entry point of the executable local main_addr main_addr = ident_list_lookup(functions_addresses, .str_main) if main_addr == 0 goto no_main_function ; on entry, we will have: ; argc = *rsp ; argv = rsp + 8 code_output = output_file_data + ENTRY_ADDR ; add rsp, 8 emit_add_rsp_imm32(8) ; mov rax, rsp (set rax to argv) emit_mov_reg(REG_RAX, REG_RSP) ; sub rsp, 32 (undo add rsp, 8 from before and add space for argv, argc, return value) emit_sub_rsp_imm32(32) ; mov [rsp+16], rax (put argv in the right place) emit_mov_qword_rsp_plus_imm32_rax(16) ; mov rax, [rsp+24] (set rax to argc) emit_mov_rax_qword_rsp_plus_imm32(24) ; mov [rsp+8], rax (put argc in the right place) emit_mov_qword_rsp_plus_imm32_rax(8) ; mov rax, main emit_mov_rax_imm64(main_addr) ; call rax emit_call_rax() ; mov rax, [rsp] emit_mov_rax_qword_rsp() ; mov rdi, rax emit_mov_reg(REG_RDI, REG_RAX) ; mov rax, 0x3c (SYS_exit) emit_mov_rax_imm64(0x3c) ; syscall emit_syscall() return :no_main_function die(.str_no_main_function) :str_no_main_function string Error: No main function. byte 0