diff --git a/lang/src/codegen.asm b/lang/src/codegen.asm index e062340..b58e0f1 100644 --- a/lang/src/codegen.asm +++ b/lang/src/codegen.asm @@ -7,7 +7,9 @@ extern vec_extend extern vec_get extern vec_push extern vec_insert_sorted +extern vec_insert_many extern vec_init_with +extern int_to_str2 global codegen_function global get_register_name @@ -29,9 +31,27 @@ section .rdata MOV_RAX_LEN equ $ - MOV_RAX JMP_EPILOGUE db 10, "jmp .epilogue", 10 JMP_EPILOGUE_LEN equ $ - JMP_EPILOGUE + DOT_ARGS db ".args:", 10 + DOT_ARGS_LEN equ $ - DOT_ARGS + DOT_BODY db ".body:", 10 + DOT_BODY_LEN equ $ - DOT_BODY + DOT_PROLOGUE db ".prologue:", 10 + DOT_PROLOGUE_LEN equ $ - DOT_PROLOGUE + DOT_EPILOGUE db ".epilogue:", 10 + DOT_EPILOGUE_LEN equ $ - DOT_EPILOGUE + JMP_ARGS db 10, "jmp .args", 10 + JMP_ARGS_LEN equ $ - JMP_ARGS + JMP_BODY db 10, "jmp .body", 10 + JMP_BODY_LEN equ $ - JMP_BODY + JMP_PROLOGUE db 10, "jmp .prologue", 10 + JMP_PROLOGUE_LEN equ $ - JMP_PROLOGUE + SUB_RSP db "sub rsp, " + SUB_RSP_LEN equ $ - SUB_RSP + ADD_RSP db "add rsp, " + ADD_RSP_LEN equ $ - ADD_RSP + REGISTER_NAMES db "abcdsidibpspr8r9r10r11r12r13r14r15" - WIDTHS db "erxliwdbp" section .text @@ -153,6 +173,73 @@ write_register_name: ret +;; rdi: arg index +;; Returns the `Operand` for the given argument index +codegen_arg_to_operand: + push rbp + mov rbp, rsp + + cmp rdi, 6 + jge .stack_arg + + ; register args in sysV make this strange movement through the canonical register indices: + ; 3 2 1 0 4 5 + ; [a,b,c,d,si,di,bp,sp,8,9,10,11,12,13,14,15] + ; at least there's 6 register args and not 4 line on win64.. + + cmp rdi, 2 + jge .rdx + mov rax, 5 + sub rax, rdi + jmp .reg_arg + +.rdx: + cmp rdi, 4 + jge .r8 + mov rax, 5 + sub rax, rdi + jmp .reg_arg + +.r8: + add rdi, 4 ; offset to r8 + mov rax, rdi + +.reg_arg: + mov rdi, rax + + xor rax, rax + mov eax, 0 ; Operand.len = 0 + shl eax, 16 + or eax, 8 ; Operand.width = 8 + shl eax, 4 + or eax, edi ; Operand.register + shl eax, 8 + or eax, OPERAND_REGISTER ; Operand.kind + mov rdx, 0 ; Operand.value = 0 + jmp .epilogue + +.stack_arg: + sub rdi, 6 + mov rax, 8 + mul rdi + add rax, 16 ; return address + old rbp + + ; construct Operand + mov rdx, rax ; Operand.value = offset + + xor rax, rax + mov eax, 0 ; Operand.len = 0 + shl eax, 16 + or eax, 8 ; Operand.width = 8 + shl eax, 4 + ; or eax, 0 ; Operand.register = undef + shl eax, 8 + or eax, OPERAND_RBP_OFFSET ; Operand.kind + +.epilogue: + pop rbp + ret + ;; rdi: ctx ;; rsi: a: *const (index, offset) ;; rdx: b: *const (index, offset) @@ -259,6 +346,7 @@ codegen_function: push rbx push r15 push r14 + push r13 ; scratch [104..120] ; dirtied-register-bitset [88..104] [a,b,c,d,si,di,bp,sp,8,9,10,11,12,13,14,15] @@ -340,6 +428,21 @@ codegen_function: ; allocate args on stack ; rbx = *AstFunction + + ; "jmp .prologue\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel JMP_PROLOGUE] + mov rdx, JMP_PROLOGUE_LEN + call vec_extend + + ; ".args:\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel DOT_ARGS] + mov rdx, DOT_ARGS_LEN + call vec_extend + mov r15, [rbx + 24] ; AstFunction.args_len xor r14, r14 ; arg index .arg_loop: @@ -359,28 +462,126 @@ codegen_function: mov rdx, stackvar_cmp mov rcx, 0 call vec_insert_sorted + + ; spill arg from register to newly allocated stack slot + ; get source Operand + mov r13, [rsp + 112] ; current_stack_size before increment + mov rdi, [rsp + 104] ; arg index + call codegen_arg_to_operand + mov [rsp + 104], rax + mov [rsp + 112], rdx + mov rdx, r13 ; offset + neg rdx + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rsp + 104] ; src + call codegen_move_rbp_slot_src + inc r14 jmp .arg_loop .arg_loop_done: + ; "jmp .body\n" + ; ".body:\n" + mov rdi, [rsp] ; ctx lea rsi, [rsp + 24] ; &function_ctx mov rdx, [rbx + 48] ; AstFunction.body - call codegen_block + call codegen_expr ; TODO: generate function body - ; push "ret\n" + ; ".epilogue:\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel DOT_EPILOGUE] + mov rdx, DOT_EPILOGUE_LEN + call vec_extend + + ; "pop {dirtied registers}\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + mov rsi, [rsp + 24] ; &function_ctx + mov rdx, 0 ; push = false + call codegen_push_pop_dirtied_registers + + ; "add rsp, {current_stack_size}\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel ADD_RSP] + mov rdx, ADD_RSP_LEN + call vec_extend + + mov rdi, [rsp + 24] ; &function_ctx + mov rdi, [rdi + 0] ; current_stack_size + lea rsi, [rsp + 104] ; scratch + mov rdx, 16 ; buffer length + mov rcx, 10 ; radix + call int_to_str2 + + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rax + rdx] + mov byte [rsi], 10 ; add newline + mov rsi, rax + mov rdx, rdx ; length from int_to_str2 + call vec_extend + + ; "ret\n" mov rdi, [rsp] ; ctx lea rdi, [rdi + 8] ; &ctx.text lea rsi, [rel RET_NL] mov rdx, RET_NL_LEN call vec_extend + ; ".prologue:\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel DOT_PROLOGUE] + mov rdx, DOT_PROLOGUE_LEN + call vec_extend + + ; "sub rsp, {current_stack_size}\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel SUB_RSP] + mov rdx, SUB_RSP_LEN + call vec_extend + + mov rdi, [rsp + 24] ; &function_ctx + mov rdi, [rdi + 0] ; current_stack_size + lea rsi, [rsp + 104] ; scratch + mov rdx, 16 ; buffer length + mov rcx, 10 ; radix + call int_to_str2 + + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rax + rdx] + mov byte [rsi], 10 ; add newline + mov rsi, rax + mov rdx, rdx ; length from int_to_str2 + call vec_extend + + ; "push{dirtied registers}\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + mov rsi, [rsp + 24] ; &function_ctx + mov rdx, 1 ; push = false + call codegen_push_pop_dirtied_registers + + ; "jmp .args\n" + mov rdi, [rsp] ; ctx + lea rdi, [rdi + 8] ; &ctx.text + lea rsi, [rel JMP_ARGS] + mov rdx, JMP_ARGS_LEN + call vec_extend + add rsp, 120 - pop r15 - pop r14 + push r13 + push r14 + push r15 pop rbx pop rbp ret @@ -388,6 +589,116 @@ codegen_function: .panic: call panic +;; rdi: *text +;; rsi: &function_ctx +;; rdx: push: bool +codegen_push_pop_dirtied_registers: + push rbp + mov rbp, rsp + push rbx + push r15 + push r14 + + sub rsp, 32 + mov [rsp], rdi + mov [rsp + 8], rsi + + mov byte [rsp + 29 + 3], 10 ; newline + mov qword [rsp + 16], -1 + mov rax, ' hsup' + mov qword [rsp + 24], rax + test rdx, rdx + jz .skip_setup_pop + mov rax, ' pop' + mov qword [rsp + 24], rax + + mov rdi, [rsp] ; text + mov rax, [rdi + 8] ; text.len() + mov [rsp + 16], rax +.skip_setup_pop: + + ; low: 0b01000000 high: 0b00001111 + ; preserved registers: + mov rax, [rsi + 48 + 16] ; dirtied_register_bitset low + mov rbx, 0b01000000 + and rax, rbx + test rax, rax + jz .skip_rbp + + mov rdi, 2 + mov rsi, 8 + lea rdx, [rsp + 29] + call get_register_name + + mov rax, -1 + cmp [rsp + 16], rax + jne .rbx_pop + + mov rdi, [rsp] ; text + lea rsi, [rsp + 24] + mov rdx, 9 + call vec_extend + jmp .skip_rbp +.rbx_pop: + + mov rdi, [rsp] ; text + lea rsi, [rsp + 24] + mov rdx, 9 + mov rcx, [rsp + 16] ; text.len() + call vec_insert_many + +.skip_rbp: + mov rax, [rsi + 48 + 16 + 8] ; dirtied_register_bitset high + mov rbx, 0b00001111 + and rax, rbx + test rax, rax + jz .done + + mov r15, 8 + xor r14, r14 +.reg_loop: + cmp r14, r15 + jge .done + bt rax, r14 + jnc .next_reg + + mov rdi, 8 + add rdi, r14 + mov rsi, 8 + lea rdx, [rsp + 29] + call get_register_name + + mov rax, -1 + cmp [rsp + 16], rax + jne .reg_pop + + mov rdi, [rsp] ; text + lea rsi, [rsp + 24] + mov rdx, 9 + call vec_extend + jmp .next_reg +.reg_pop: + + mov rdi, [rsp] ; text + lea rsi, [rsp + 24] + mov rdx, 9 + mov rcx, [rsp + 16] ; text.len() + call vec_insert_many + + +.next_reg: + inc r14 + jmp .reg_loop + +.done: + add rsp, 32 + pop r14 + pop r15 + pop rbx + pop rbp + ret + + ;; rdi: ctx ;; rsi: &function_ctx ;; rdx: block index @@ -405,17 +716,17 @@ codegen_block: mov rdi, [rdi] ; ast mov rsi, rdx ; block index call vec_get - mov 15, [rax + 8] ; AstNode.extra - mov rbx, [rax + 0] ; AstNode.data + mov r15, [rax + 8] ; AstNode.extra + mov rbx, [rax + 0] ; AstNode.data xor r14, r14 ; statement index .stmt_loop: cmp r14, r15 jge .stmt_loop_done mov rdi, [rsp] ; ctx - lea mov, [rsp + 8] ; &function_ctx + lea rbx, [rsp + 8] ; &function_ctx mov rdx, [rbx + r14 * 8] ; statements[i] - call codegen_statement + call codegen_expr inc r14 jmp .stmt_loop @@ -436,11 +747,13 @@ codegen_expr: push rbp mov rbp, rsp push rbx + push r15 + push r14 - ; scratch [16..32] + ; scratch [16..48] ; function_ctx: [8..16] ; ctx [0..8] - sub rsp, 32 + sub rsp, 48 mov [rsp], rdi ; ctx mov [rsp + 8], rsi ; &function_ctx @@ -475,52 +788,112 @@ codegen_expr: je .address_of jmp .panic +.block: + mov rbx, [rax + 8] ; AstNode.data + mov r15, [rax + 16] ; AstNode.extra + + xor r14, r14 ; statement index +.stmt_loop: + cmp r14, r15 + jge .stmt_loop_done + mov rdi, [rsp] ; ctx + mov rsi, [rsp + 8] ; &function_ctx + mov rdx, [rbx + r14 * 8] ; statements[i] + call codegen_expr + inc r14 + jmp .stmt_loop +.stmt_loop_done: + xor rax, rax + xor rdx, rdx + jmp .done + .return: ; codegen inner expr mov rdi, [rsp] ; ctx mov rsi, [rsp + 8] ; &function_ctx mov rdx, [rax + 8] ; AstNode.data call codegen_expr - mov rbx, rax + + ; mov rax, {inner expr result} + mov [rsp + 16], rax + mov [rsp + 24], rdx + mov rax, qword [rel OPERAND_RAX] + mov rdx, qword [rel OPERAND_RAX + 8] + mov [rsp + 32], rax + mov [rsp + 40], rdx mov rdi, [rsp] ; ctx mov rdi, [rdi + 8] ; &ctx.text - lea rsi, [rel MOV_RAX] - mov rdx, MOV_RAX_LEN - call vec_extend - - mov rdi, rbx - mov rsi, 8 - lea rdx, [rsp + 16] ; scratch - call get_register_name - - mov rdi, [rsp] ; ctx - mov rdi, [rdi + 8] ; &ctx.text - mov rsi, rax - call vec_extend + lea rsi, [rsp + 32] ; dst + lea rdx, [rsp + 16] ; src + call codegen_move_dst_src + ; push "jmp .epilogue\n" mov rdi, [rsp] ; ctx mov rdi, [rdi + 8] ; &ctx.text lea rsi, [rel JMP_EPILOGUE] mov rdx, JMP_EPILOGUE_LEN call vec_extend - mov rax, 0 + ; construct return operand + ; Operand { kind: REGISTER, register_and_width: (register | (width << 4)), len: 0, value: 0 } + mov eax, 0 ; len = 0 + shl eax, 16 + or eax, 8 ; width = 8 + shl eax, 4 + or eax, 0 ; register = rax + shl eax, 8 + or eax, OPERAND_REGISTER ; kind + mov rdx, 0 ; value = 0 jmp .done .number: ; rax = *AstNode mov [rsp + 16], rax ; scratch = *AstNode + mov rbx, [rax + 8] ; AstNode.data = value + mov [rsp + 40], rbx ; Operand.value mov rdi, [rsp + 8] ; &function_ctx call codegen_allocate_register - + xor rbx, rbx + or rbx, 8 ; width = 8 + shl rbx, 4 + or rbx, rax ; register + shl rbx, 8 + or rbx, OPERAND_REGISTER ; kind + mov [rsp + 16], rbx ; + mov qword [rsp + 24], 0 ; value = 0 + mov byte [rsp + 32], OPERAND_IMMEDIATE ; Operand.kind + mov bl, 8 ; width = 8 + shl bl, 4 ; register = undef + mov byte [rsp + 33], bl ; Operand.register_and_width + mov word [rsp + 34], 0 ; Operand.len = 0 + mov rdi, [rsp] ; ctx + mov rdi, [rdi + 8] ; &ctx.text + lea rsi, [rsp + 16] ; dst + lea rdx, [rsp + 16] ; src + call codegen_move_dst_src + + mov rax, qword [rsp + 16] + mov rdx, qword [rsp + 24] + jmp .done + +.var_decl: +.var_ref: +.binary_op: +.assignment: +.place_to_value: +.value_to_place: +.deref: +.address_of: ; TODO .done: - add rsp, 32 + add rsp, 48 + pop r14 + pop r15 pop rbx pop rbp ret @@ -539,19 +912,21 @@ codegen_expr: ;; or: register: u4, width: u4 section .rdata ;; start-consts - OPERAND_REGISTER db 1 ; e.g. rax, rbx - OPERAND_RBP_OFFSET db 2 ; e.g. [rbp - 8] - OPERAND_RSP_OFFSET db 3 ; e.g. [rsp + 16] - OPERAND_ADDRESS db 4 ; e.g. [rel OPERAND_ADDRESS] - OPERAND_IMMEDIATE db 5 ; e.g. 0x10 - OPERAND_CONSTANT db 6 ; e.g. OPERAND_CONSTANT - OPERAND_LABEL db 7 ; e.g. label_1234 + OPERAND_REGISTER equ 1 ; e.g. rax, rbx + OPERAND_RBP_OFFSET equ 2 ; e.g. [rbp - 8] + OPERAND_RSP_OFFSET equ 3 ; e.g. [rsp + 16] + OPERAND_ADDRESS equ 4 ; e.g. [rel OPERAND_ADDRESS] + OPERAND_IMMEDIATE equ 5 ; e.g. 0x10 + OPERAND_CONSTANT equ 6 ; e.g. OPERAND_CONSTANT + OPERAND_LABEL equ 7 ; e.g. label_1234 ;; end-consts WIDTH_BYTE db 'byte ' WIDTH_WORD db 'word ' WIDTH_DWORD db 'dword ' WIDTH_QWORD db 'qword ' +section .text + ;; rdi: *text ;; rsi: op: *Operand codegen_write_operand: @@ -587,7 +962,7 @@ codegen_write_operand: mov rbx, rsi mov rdi, [rbx + 1] ; register_and_width mov rsi, rdi - mov dil, dil ; low 4 bits = register + and dil, 0x0F ; low 4 bits = register shr rsi, 4 ; high 4 bits = width lea rdx, [rsp + 16] ; buffer call get_register_name @@ -601,7 +976,7 @@ codegen_write_operand: .rsp_offset: ; {width} [rbp {+/-} offset] mov rsi, [rsp + 8] ; op - mov sil, byte [rsi + 1] ; register_and_width + mov sil, byte [rsi + 1] ; Operand.register_and_width shr sil, 4 ; width mov rdi, [rsp] ; *text call codegen_write_width @@ -614,10 +989,10 @@ codegen_write_operand: ; if op.kind == OPERAND_RBP_OFFSET mov rax, [rsp + 8] ; op - mov al, byte [rax + 0] ; op.kind + mov al, byte [rax + 0] ; Operand.kind cmp al, OPERAND_RBP_OFFSET jne .rsp_offset_write - mov qword [rsp + 16], ' pbr' + mov qword [rsp + 16], 'rbp ' mov rdi, [rsp] ; *text lea rsi, [rsp + 16] mov rdx, 4 @@ -625,7 +1000,7 @@ codegen_write_operand: jmp .check_sign .rsp_offset_write: - mov qword [rsp + 16], ' psr' + mov qword [rsp + 16], 'rsp ' mov rdi, [rsp] ; *text lea rsi, [rsp + 16] mov rdx, 4 @@ -637,11 +1012,12 @@ codegen_write_operand: mov rdi, [rax + 8] ; op.value cmp rdi, 0 jl .skip_plus - mov qword [rsp + 16], ' + ' + mov qword [rsp + 16], ' + ' mov rdi, [rsp] ; *text lea rsi, [rsp + 16] mov rdx, 3 call vec_extend + nop .skip_plus: ; write offset @@ -668,7 +1044,7 @@ codegen_write_operand: lea rsi, [rsp + 16] call vec_push - mov qword [rsp + 16], ' ler' + mov qword [rsp + 16], 'rel ' mov rdi, [rsp] ; *text lea rsi, [rsp + 16] mov rdx, 4 @@ -678,7 +1054,7 @@ codegen_write_operand: mov rax, [rsp + 8] ; op mov rdi, [rsp] ; *text mov rsi, [rax + 8] ; op.value - mov rdx, [rax + 4] ; op.len + mov rdx, [rax + 2] ; op.len mov dx, dx ; low 16 bits call vec_extend @@ -707,11 +1083,14 @@ codegen_write_operand: mov rax, [rsp + 8] ; op mov rdi, [rsp] ; *text mov rsi, [rax + 8] ; op.value - mov rdx, [rax + 4] ; op.len + mov rdx, [rax + 2] ; op.len mov dx, dx ; low 16 bits call vec_extend jmp .epilogue +.label: + jmp .panic + .epilogue: add rsp, 40 pop rbx @@ -745,7 +1124,33 @@ codegen_write_width: call vec_extend pop rax ; length written ret +.panic: + call panic +;; rdi: *text +;; rsi: src: *Operand +;; rdx: rbp offset +codegen_move_rbp_slot_src: + push rbp + mov rbp, rsp + + sub rsp, 16 + + ; construct dst Operand + + mov byte [rsp + 0], OPERAND_RBP_OFFSET ; Operand.kind + mov byte [rsp + 1], 0x80 ; Operand.register_and_width (width=8) + mov word [rsp + 2], 0 ; Operand.len = 0 + mov dword [rsp + 4], 0 ; padding + mov qword [rsp + 8], rdx ; Operand.value = rbp offset + + mov rdx, rsi + lea rsi, [rsp + 0] ; dst + call codegen_move_dst_src + + add rsp, 16 + pop rbp + ret ;; rdi: *text ;; rsi: dst: *Operand @@ -764,8 +1169,8 @@ codegen_move_dst_src: jg .panic ; unsupported dst kind ; if dst.width != src.width - mov cl, byte [rsi + 2] ; dst.register_and_width - mov bl, byte [rdx + 2] ; src.register_and_width + mov cl, byte [rsi + 1] ; dst.register_and_width + mov bl, byte [rdx + 1] ; src.register_and_width shr cl, 4 shr bl, 4 cmp cl, bl @@ -788,13 +1193,80 @@ codegen_move_dst_src: ; xchg rax, [src] ; mov [dst], rax ; xchg rax, [src] + + mov rdi, [rsp] ; *text + lea rsi, [rel XCHG_RAX] + mov rdx, XCHG_RAX_LEN + call vec_extend + + mov rdi, [rsp] ; *text + mov rsi, [rsp + 16] ; src + call codegen_write_operand + + mov rdi, [rsp] ; *text + lea rsi, [rel COMMA_RAX] + mov rdx, COMMA_RAX_LEN + call vec_extend + + mov rdi, [rsp] ; *text + lea rsi, [rel MOV_RAX_COMMA] + mov rdx, 4 + call vec_extend + + mov rdi, [rsp] ; *text + mov rsi, [rsp + 8] ; dst + call codegen_write_operand + + mov rdi, [rsp] ; *text + lea rsi, [rel COMMA_RAX] + mov rdx, COMMA_RAX_LEN + call vec_extend + + mov rdi, [rsp] ; *text + lea rsi, [rel XCHG_RAX] + mov rdx, XCHG_RAX_LEN + call vec_extend + + mov rdi, [rsp] ; *text + mov rsi, [rsp + 16] ; src + call codegen_write_operand + jmp .epilogue + .do_move: + mov rdi, [rsp] ; *text + lea rsi, [rel MOV_RAX_COMMA] + mov rdx, 4 + call vec_extend + + mov rdi, [rsp] ; *text + mov rsi, [rsp + 8] ; dst + 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] ; src + call codegen_write_operand .epilogue: add rsp, 24 pop rbx pop rbp ret - .panic: call panic + +section .rdata + XCHG_RAX db "xchg rax, " + XCHG_RAX_LEN equ $ - XCHG_RAX + MOV_RAX_COMMA db "mov rax, " + MOV_RAX_COMMA_LEN equ $ - MOV_RAX_COMMA + COMMA_RAX db ", rax" + COMMA_RAX_LEN equ $ - COMMA_RAX + + ; Operand { kind: REGISTER, register: 0, width: 8, len: 0, padding: 0, value: 0 } + align 8 + OPERAND_RAX dq 0x0000_8001, 0 diff --git a/lang/tests/codegen.rs b/lang/tests/codegen.rs index 6a6468b..541166c 100644 --- a/lang/tests/codegen.rs +++ b/lang/tests/codegen.rs @@ -90,12 +90,7 @@ fn main() { print_ast( b"fn main(a: u32) -> void { - let y: u32 = a + 4; - { - let y: u32 = 10; - } - let y: *u32 = &y; - return *y; + return 4; }", |ast| unsafe { parse_func(ast) }, );