# This Source Code Form is subject to the terms of the Mozilla Public License, # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. .global _start # Program entry point. # # Registers used as global variables: # s1 - Contains the current position in the source text. # s2 - Label counter. # # - The compiler expects valid input, otherwise it will generate invalid # assembly or hang. There is no error checking, no semantic analysis, no # type checking. # # - Imports with only a module name without package, e.g. # "import dummy", can be parsed, but are ignored. # # - No loops. Only labels and goto. # # - Only unsigned number literals are supported (in decimal or # hexadecimal format). # # - Comments are accepted only at the end of a line. # # - Return can be used only as the last statement of a procedure. It # doesn't actually return, but sets a0 to the appropriate value. # # - The lvalue of an assignment can only be an identifier. .include "boot/definitions.inc" .equ SOURCE_BUFFER_SIZE, 81920 .section .rodata section_rodata: .ascii ".section .rodata\n" .equ SECTION_RODATA_SIZE, . - section_rodata section_text: .ascii ".section .text\n" .equ SECTION_TEXT_SIZE, . - section_text section_bss: .ascii ".section .bss\n" .equ SECTION_BSS_SIZE, . - section_bss global_start: .ascii ".global _start\n" .equ GLOBAL_START_SIZE, . - global_start prologue: .ascii "addi sp, sp, -96\nsw ra, 92(sp)\nsw s0, 88(sp)\naddi s0, sp, 96\n" .equ PROLOGUE_SIZE, . - prologue epilogue: .ascii "lw ra, 92(sp)\nlw s0, 88(sp)\naddi sp, sp, 96\nret\n" .equ EPILOGUE_SIZE, . - epilogue asm_exit: .ascii "li a0, 0\nli a7, 93\necall\n" .equ ASM_EXIT_SIZE, . - asm_exit asm_start: .ascii ".type _start, @function\n_start:\n" .equ ASM_START_SIZE, . - asm_start asm_and_a0_a1: .ascii "and a0, a0, a1\n" .equ ASM_AND_A0_A1_SIZE, . - asm_and_a0_a1 asm_or_a0_a1: .ascii "or a0, a0, a1\n" .equ ASM_OR_A0_A1_SIZE, . - asm_or_a0_a1 asm_add_a0_a1: .ascii "add a0, a0, a1\n" .equ ASM_ADD_A0_A1_SIZE, . - asm_add_a0_a1 asm_sub_a0_a1: .ascii "sub a0, a0, a1\n" .equ ASM_SUB_A0_A1_SIZE, . - asm_sub_a0_a1 asm_mul_a0_a1: .ascii "mul a0, a0, a1\n" .equ ASM_MUL_A0_A1_SIZE, . - asm_mul_a0_a1 asm_seqz_a0: .ascii "seqz a0, a0\n" .equ ASM_SEQZ_A0_SIZE, . - asm_seqz_a0 asm_neg_a0: .ascii "neg a0, a0\n" .equ ASM_NEG_A0_SIZE, . - asm_neg_a0 asm_type: .ascii ".type " .equ ASM_TYPE_SIZE, . - asm_type asm_type_function: .ascii ", @function\n" .equ ASM_TYPE_FUNCTION_SIZE, . - asm_type_function asm_type_object: .ascii ", @object\n" .equ ASM_TYPE_OBJECT_SIZE, . - asm_type_object asm_restore_parameters: .ascii "lw a0, 60(sp)\nlw a1, 56(sp)\nlw a2, 52(sp)\nlw a3, 48(sp)\nlw a4, 44(sp)\nlw a5, 40(sp)\n" .equ ASM_RESTORE_PARAMETERS_SIZE, . - asm_restore_parameters asm_preserve_parameters: .ascii "sw a0, 84(sp)\nsw a1, 80(sp)\nsw a2, 76(sp)\nsw a2, 76(sp)\nsw a3, 72(sp)\nsw a4, 68(sp)\nsw a5, 64(sp)\n" .equ ASM_PRESERVE_PARAMETERS_SIZE, . - asm_preserve_parameters .section .bss .type source_code, @object source_code: .zero SOURCE_BUFFER_SIZE .section .text # Ignores the import. .type _compile_import, @function _compile_import: # Prologue. addi sp, sp, -24 sw ra, 20(sp) sw s0, 16(sp) addi s0, sp, 24 .Lcompile_import_loop: mv a0, s1 addi a1, sp, 0 call _tokenize_next li t0, TOKEN_IMPORT lw t1, 0(sp) bne t0, t1, .Lcompile_import_end # a0 is set from the previous _tokenize_next call. Skip the module name. addi a1, sp, 0 call _tokenize_next mv s1, a0 j .Lcompile_import_loop .Lcompile_import_end: # Epilogue. lw ra, 20(sp) lw s0, 16(sp) addi sp, sp, 24 ret .type _build_binary_expression, @function _build_binary_expression: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 li a0, 0 call _build_expression mv a0, s1 addi a1, sp, 12 call _tokenize_next lw t0, 12(sp) li t1, TOKEN_AND beq t0, t1, .Lbuild_binary_expression_and li t1, TOKEN_OR beq t0, t1, .Lbuild_binary_expression_or li t1, TOKEN_PLUS beq t0, t1, .Lbuild_binary_expression_plus li t1, TOKEN_EQUALS beq t0, t1, .Lbuild_binary_expression_equal li t1, TOKEN_ASTERISK beq t0, t1, .Lbuild_binary_expression_product li t1, TOKEN_MINUS beq t0, t1, .Lbuild_binary_expression_minus j .Lbuild_binary_expression_end .Lbuild_binary_expression_equal: mv s1, a0 # Skip =. li a0, 1 call _build_expression la a0, asm_sub_a0_a1 li a1, ASM_SUB_A0_A1_SIZE call _write_out la a0, asm_seqz_a0 li a1, ASM_SEQZ_A0_SIZE call _write_out j .Lbuild_binary_expression_end .Lbuild_binary_expression_and: mv s1, a0 # Skip &. li a0, 1 call _build_expression la a0, asm_and_a0_a1 li a1, ASM_AND_A0_A1_SIZE call _write_out j .Lbuild_binary_expression_end .Lbuild_binary_expression_or: mv s1, a0 # Skip or. li a0, 1 call _build_expression la a0, asm_or_a0_a1 li a1, ASM_OR_A0_A1_SIZE call _write_out j .Lbuild_binary_expression_end .Lbuild_binary_expression_plus: mv s1, a0 # Skip +. li a0, 1 call _build_expression la a0, asm_add_a0_a1 li a1, ASM_ADD_A0_A1_SIZE call _write_out j .Lbuild_binary_expression_end .Lbuild_binary_expression_minus: mv s1, a0 # Skip -. li a0, 1 call _build_expression la a0, asm_sub_a0_a1 li a1, ASM_SUB_A0_A1_SIZE call _write_out j .Lbuild_binary_expression_end .Lbuild_binary_expression_product: mv s1, a0 # Skip *. li a0, 1 call _build_expression la a0, asm_mul_a0_a1 li a1, ASM_MUL_A0_A1_SIZE call _write_out j .Lbuild_binary_expression_end .Lbuild_binary_expression_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Checks whether the given identifier starts with "loca". # Parameters: # a0 - Pointer to the identifier. # a1 - Identifier length. # # Sets a0 to 1 if the identifier starts with "loca", otherwise to 0. .type _is_local_identifier, @function _is_local_identifier: # Prologue. addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) addi s0, sp, 16 li t0, 0x61636f6c # loca sw t0, 4(sp) # a0 is already set. addi a1, sp, 4 li a2, 4 call _memcmp seqz a0, a0 # Epilogue. lw ra, 12(sp) lw s0, 8(sp) addi sp, sp, 16 ret # Checks whether the given identifier is a saved register name, like s1 or s2. # Parameters: # a0 - Pointer to the identifier. # a1 - Identifier length. # # Sets a0 to 1 if the identifier is a preserved register, otherwise to 0. .type _is_register_identifier, @function _is_register_identifier: # Prologue. addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 lbu a0, (a0) addi a1, a1, -2 seqz a1, a1 addi t0, a0, -'s' seqz t0, t0 and a0, a1, t0 # Epilogue. lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 ret # Parameters: # a0 - Identifier length. # a1 - Register number as character. .type _compile_identifier_expression, @function _compile_identifier_expression: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 sw a0, 20(sp) # Identifier length. sw a1, 16(sp) # Register number as character. mv a0, s1 lw a1, 20(sp) call _is_local_identifier bnez a0, .Lcompile_identifier_expression_local mv a0, s1 lw a1, 20(sp) call _is_register_identifier bnez a0, .Lcompile_identifier_expression_saved # Global identifier. lw t1, 16(sp) li t0, 0x00202c00 # \0,_ or t0, t0, t1 sw t0, 12(sp) li t0, 0x6120616c # la a sw t0, 8(sp) addi a0, sp, 8 li a1, 7 call _write_out mv a0, s1 lw a1, 20(sp) call _write_out li a0, '\n' call _put_char lbu a0, (s1) call _is_upper beqz a0, .Lcompile_identifier_expression_end lw t1, 16(sp) li t0, 0x0a290061 # a\0)\n sll t2, t1, 8 or t0, t0, t2 sw t0, 12(sp) li t0, 0x28202c00 # \0, ( or t0, t0, t1 sw t0, 8(sp) li t0, 0x6120776c # lw a sw t0, 4(sp) addi a0, sp, 4 li a1, 12 call _write_out j .Lcompile_identifier_expression_end .Lcompile_identifier_expression_saved: li t0, 0x00202c00 # \0,_ lw t1, 16(sp) or t0, t0, t1 sw t0, 12(sp) li t0, 0x6120766d # mv a sw t0, 8(sp) addi a0, sp, 8 li a1, 7 call _write_out mv a0, s1 lw a1, 20(sp) call _write_out li a0, '\n' call _put_char j .Lcompile_identifier_expression_end .Lcompile_identifier_expression_local: lw t1, 16(sp) li t0, 0x00202c00 # \0,_ or t0, t0, t1 sw t0, 12(sp) li t0, 0x6120776c # lw a sw t0, 8(sp) addi a0, sp, 8 li a1, 7 call _write_out mv a0, s1 lw a1, 20(sp) addi a0, a0, 4 # Skip the "loca" variable prefix. addi a1, a1, -4 # Skip the "loca" variable prefix. call _write_out li t0, 0x29707328 # (sp) sw t0, 12(sp) addi a0, sp, 12 li a1, 4 call _write_out li a0, '\n' call _put_char j .Lcompile_identifier_expression_end .Lcompile_identifier_expression_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Evalutes an expression and saves the result in a0. # # a0 - X in aX, the register number to save the result. .type _build_expression, @function _build_expression: # Prologue. addi sp, sp, -48 sw ra, 44(sp) sw s0, 40(sp) addi s0, sp, 48 addi a0, a0, '0' # Make the register number to a character. sw a0, 36(sp) # And save it. mv a0, s1 addi a1, sp, 24 call _tokenize_next sw a0, 20(sp) lw a0, 24(sp) li t0, TOKEN_MINUS beq a0, t0, .Lbuild_expression_negate li t0, TOKEN_AT beq a0, t0, .Lbuild_expression_address li t0, TOKEN_INTEGER beq a0, t0, .Lbuild_expression_literal lw a0, 32(sp) lbu a0, (a0) li t0, '_' beq a0, t0, .Lbuild_expression_call lw s1, 32(sp) lw a0, 28(sp) lw a1, 36(sp) call _compile_identifier_expression j .Lbuild_expression_advance .Lbuild_expression_negate: lw s1, 20(sp) # Skip the -. mv a0, zero call _build_expression la a0, asm_neg_a0 li a1, ASM_NEG_A0_SIZE call _write_out j .Lbuild_expression_end .Lbuild_expression_address: lw t1, 36(sp) li t0, 0x20 # _ sw t0, 16(sp) li t0, 0x2c707320 # _sp, sw t0, 12(sp) li t0, 0x2c006120 # _a\0, sw t0, 8(sp) sb t1, 10(sp) li t0, 0x69646461 # addi sw t0, 4(sp) addi a0, sp, 4 li a1, 13 call _write_out lw a0, 20(sp) # Skip @. addi a1, sp, 24 call _tokenize_next mv s1, a0 lw a0, 32(sp) lw a1, 28(sp) addi a0, a0, 4 # Skip the "loca" variable prefix. addi a1, a1, -4 # Skip the "loca" variable prefix. call _write_out li a0, '\n' call _put_char j .Lbuild_expression_end .Lbuild_expression_call: lw a0, 20(sp) addi a1, sp, 8 call _tokenize_next mv s1, a0 lw a0, 32(sp) lw a1, 28(sp) call _compile_call j .Lbuild_expression_end .Lbuild_expression_literal: lw t1, 36(sp) li t0, 0x00202c00 # \0,_ or t0, t0, t1 sw t0, 16(sp) li t0, 0x6120696c # li a sw t0, 12(sp) addi a0, sp, 12 li a1, 7 call _write_out lw a0, 32(sp) lw a1, 28(sp) call _write_out li a0, '\n' call _put_char j .Lbuild_expression_advance .Lbuild_expression_advance: lw s1, 20(sp) .Lbuild_expression_end: # Epilogue. lw ra, 44(sp) lw s0, 40(sp) addi sp, sp, 48 ret # Compiles an lvalue. # # Parameters: # a0 - Pointer to the identifier. # a1 - Identifier length. .type _compile_designator_expression, @function _compile_designator_expression: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 sw a0, 20(sp) # Identifier pointer. sw a1, 16(sp) # Identifier length. lw a0, 20(sp) lw a1, 16(sp) call _is_local_identifier bnez a0, .Lcompile_designator_expression_local lw a0, 20(sp) lw a1, 16(sp) call _is_register_identifier bnez a0, .Lcompile_designator_expression_saved .Lcompile_designator_expression_local: li t0, 0x202c30 # 0,_ sw t0, 12(sp) li t0, 0x61207773 # sw a sw t0, 8(sp) addi a0, sp, 8 li a1, 7 call _write_out lw a0, 20(sp) lw a1, 16(sp) addi a0, a0, 4 # Skip the "loca" variable prefix. addi a1, a1, -4 # Skip the "loca" variable prefix. call _write_out li t0, '\n' sw t0, 12(sp) li t0, 0x29707328 # (sp) sw t0, 8(sp) addi a0, sp, 8 li a1, 5 call _write_out j .Lcompile_designator_expression_end .Lcompile_designator_expression_saved: li t0, 0x20766d # mv_ sw t0, 12(sp) addi a0, sp, 12 li a1, 3 call _write_out lw a0, 20(sp) lw a1, 16(sp) call _write_out li t0, 0x0a # \n sw t0, 12(sp) li t0, 0x3061202c # , a0 sw t0, 8(sp) addi a0, sp, 8 li a1, 5 call _write_out j .Lcompile_designator_expression_end .Lcompile_designator_expression_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Compiles a statement beginning with an identifier. # # Left values should be variables named "loca n", where n is the offset # of the variable on the stack, like loca8 or loca4. .type _compile_identifier, @function _compile_identifier: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 # Save the pointer to the identifier and its length on the stack. mv a0, s1 addi a1, sp, 12 call _tokenize_next addi a1, sp, 0 call _tokenize_next mv s1, a0 lw t0, 0(sp) li t1, TOKEN_ASSIGN beq t0, t1, .Lcompile_identifier_assign li t1, TOKEN_LEFT_PAREN beq t0, t1, .Lcompile_identifier_call j .Lcompile_identifier_end .Lcompile_identifier_call: lw a0, 20(sp) lw a1, 16(sp) call _compile_call j .Lcompile_identifier_end .Lcompile_identifier_assign: call _build_binary_expression lw a0, 20(sp) lw a1, 16(sp) call _compile_designator_expression j .Lcompile_identifier_end .Lcompile_identifier_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Compiles a procedure call. Expects s1 to point to the first argument. # a0 - Pointer to the procedure name. # a1 - Length of the procedure name. # # Returns the procedure result in a0. .type _compile_call, @function _compile_call: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 sw a0, 20(sp) sw a1, 16(sp) sw zero, 12(sp) # Argument count for a procedure call. .Lcompile_call_paren: lbu t0, (s1) li t1, 0x29 # ) beq t0, t1, .Lcompile_call_complete .Lcompile_call_argument: li a0, 0 call _build_expression li t0, 0x202c30 # 0,_ sw t0, 8(sp) li t0, 0x61207773 # sw a sw t0, 4(sp) addi a0, sp, 4 li a1, 7 call _write_out lw t0, 12(sp) # Argument count for a procedure call. # Only 6 arguments are supported with a0-a5. # Save all arguments on the stack so they aren't overriden afterwards. li a0, -4 mul a0, t0, a0 addi a0, a0, 60 call _printi li t0, '\n' sw t0, 8(sp) li t0, 0x29707328 # (sp) sw t0, 4(sp) addi a0, sp, 4 li a1, 5 call _write_out lbu t0, (s1) li t1, ',' bne t0, t1, .Lcompile_call_paren lw t0, 12(sp) # Argument count for a procedure call. addi t0, t0, 1 sw t0, 12(sp) addi s1, s1, 1 # Skip the comma between the arguments. j .Lcompile_call_argument .Lcompile_call_complete: sw zero, 12(sp) .Lcompile_call_restore: # Just go through all a0-a5 registers and read them from stack. # If this stack value contains garbage, the procedure just shouldn't use it. la a0, asm_restore_parameters li a1, ASM_RESTORE_PARAMETERS_SIZE call _write_out .Lcompile_call_perform: li t0, 0x20 sw t0, 8(sp) li t0, 0x6c6c6163 # call sw t0, 4(sp) addi a0, sp, 4 li a1, 5 call _write_out lw a0, 20(sp) lw a1, 16(sp) call _write_out li a0, '\n' call _put_char addi s1, s1, 1 # Skip the close paren. # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Walks through the procedure definitions. .type _compile_procedure_section, @function _compile_procedure_section: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 .Lcompile_procedure_section_loop: mv a0, s1 addi a1, sp, 4 call _tokenize_next li t0, TOKEN_PROC lw t1, 4(sp) bne t0, t1, .Lcompile_procedure_section_end call _compile_procedure j .Lcompile_procedure_section_loop .Lcompile_procedure_section_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret .type _compile_module_declaration, @function _compile_module_declaration: # Prologue. addi sp, sp, -24 sw ra, 20(sp) sw s0, 16(sp) addi s0, sp, 24 la a0, global_start li a1, GLOBAL_START_SIZE call _write_out # Skip "program". mv a0, s1 addi a1, sp, 4 call _tokenize_next mv s1, a0 # Epilogue. lw ra, 20(sp) lw s0, 16(sp) addi sp, sp, 24 ret .type _compile_constant_section, @function _compile_constant_section: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 4 call _tokenize_next li t0, TOKEN_CONST lw t1, 4(sp) bne t0, t1, .Lcompile_constant_section_end mv s1, a0 la a0, section_rodata li a1, SECTION_RODATA_SIZE call _write_out .Lcompile_constant_section_item: mv a0, s1 addi a1, sp, 12 call _tokenize_next lw t0, 12(sp) li t1, TOKEN_IDENTIFIER bne t0, t1, .Lcompile_constant_section_end lw s1, 20(sp) call _compile_constant j .Lcompile_constant_section_item .Lcompile_constant_section_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret .type _compile_constant, @function _compile_constant: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 12 call _tokenize_next addi a1, sp, 0 call _tokenize_next # Skip the assignment sign. mv s1, a0 # Write identifier the identifier. lw a0, 20(sp) lw a1, 16(sp) call _write_out # : .long li t0, 0x20676e6f # ong_ sw t0, 4(sp) li t0, 0x6c2e203a # : .l sw t0, 0(sp) mv a0, sp li a1, 8 call _write_out mv a0, s1 addi a1, sp, 12 call _tokenize_next mv s1, a0 lw a0, 20(sp) # Save the literal pointer before advancing it. lw a1, 16(sp) # The literal length. call _write_out li a0, '\n' call _put_char # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret .type _compile_variable_section, @function _compile_variable_section: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 4 call _tokenize_next li t0, TOKEN_VAR lw t1, 4(sp) bne t0, t1, .Lcompile_variable_section_end mv s1, a0 la a0, section_bss li a1, SECTION_BSS_SIZE call _write_out .Lcompile_variable_section_item: mv a0, s1 addi a1, sp, 12 call _tokenize_next lw t0, 12(sp) li t1, TOKEN_IDENTIFIER bne t0, t1, .Lcompile_variable_section_end lw s1, 20(sp) # Advance to the beginning of the variable name. call _compile_variable j .Lcompile_variable_section_item .Lcompile_variable_section_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Compile a global variable. .type _compile_variable, @function _compile_variable: # Prologue. addi sp, sp, -48 sw ra, 44(sp) sw s0, 40(sp) addi s0, sp, 48 # Save the identifier on the stack since it should emitted multiple times. mv a0, s1 addi a1, sp, 28 call _tokenize_next addi a1, sp, 4 call _tokenize_next # Skip the colon in front of the type. addi a1, sp, 4 call _tokenize_next # Skip the opening bracket. addi a1, sp, 16 call _tokenize_next # Save the array size on the stack since it has to be emitted multiple times. addi a1, sp, 4 call _tokenize_next # Skip the closing bracket. addi a1, sp, 4 call _tokenize_next # Skip the type. mv s1, a0 # .type identifier, @object la a0, asm_type li a1, ASM_TYPE_SIZE call _write_out lw a0, 36(sp) lw a1, 32(sp) call _write_out la a0, asm_type_object li a1, ASM_TYPE_OBJECT_SIZE call _write_out # identifier: .zero size lw a0, 36(sp) lw a1, 32(sp) call _write_out li t0, 0x206f7265 # ero_ sw t0, 12(sp) li t0, 0x7a2e203a # : .z sw t0, 8(sp) addi a0, sp, 8 li a1, 8 call _write_out lw a0, 24(sp) lw a1, 20(sp) call _write_out li a0, '\n' call _put_char # Epilogue. lw ra, 44(sp) lw s0, 40(sp) addi sp, sp, 48 ret .type _compile_procedure, @function _compile_procedure: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 12 call _tokenize_next # Skip proc. addi a1, sp, 12 call _tokenize_next mv s1, a0 lw a0, 16(sp) lw a1, 20(sp) call _write_procedure_head # Skip all declarations until we find the "begin" keyword, denoting the # beginning of the procedure body. .Lcompile_procedure_begin: mv a0, s1 addi a1, sp, 4 call _tokenize_next mv s1, a0 lw t0, 4(sp) li t1, TOKEN_BEGIN bne t0, t1, .Lcompile_procedure_begin # Generate the procedure prologue with a predefined stack size. la a0, prologue li a1, PROLOGUE_SIZE call _write_out # Save passed arguments on the stack. la a0, asm_preserve_parameters li a1, ASM_PRESERVE_PARAMETERS_SIZE call _write_out # Generate the body of the procedure. call _compile_statements mv s1, a0 # Skip end. /* DEBUG sw a0, 8(sp) lw a1, 12(sp) li a2, TOKEN_END sub a1, a1, a2 seqz a1, a1 seqz a0, a0 addi a0, a0, '0' addi a1, a1, '0' sb a0, 4(sp) sb a1, 5(sp) addi a0, sp, 4 li a1, 2 call _write_error lw a0, 8(sp) */ # Generate the procedure epilogue with a predefined stack size. la a0, epilogue li a1, EPILOGUE_SIZE call _write_out # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Compiles a goto statement to an uncoditional jump. .type _compile_goto, @function _compile_goto: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 0 call _tokenize_next # Skip the goto keyword. addi a1, sp, 0 call _tokenize_next # We should be on dot the label is beginning with. addi a1, sp, 0 call _tokenize_next# Save the label name. mv s1, a0 li t0, 0x2e206a # j . sw t0, 12(sp) addi a0, sp, 12 li a1, 3 call _write_out lw a0, 8(sp) # Saved dot position. lw a1, 4(sp) call _write_out li a0, '\n' call _put_char # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Rewrites a label to assembly. .type _compile_label, @function _compile_label: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 8 call _tokenize_next # Dot starting the label. addi a1, sp, 8 call _tokenize_next mv s1, a0 li a0, '.' call _put_char lw a0, 16(sp) lw a1, 12(sp) call _write_out li a0, ':' call _put_char li a0, '\n' call _put_char # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Just skips the return keyword and evaluates the return expression. .type _compile_return, @function _compile_return: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 12 call _tokenize_next mv s1, a0 # Skip return. call _build_binary_expression # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret .type _compile_if, @function _compile_if: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 0 call _tokenize_next mv s1, a0 # Skip the if. call _build_binary_expression mv a0, s1 addi a1, sp, 0 call _tokenize_next mv s1, a0 # Skip the then. # Label prefix. li t0, 0x66694c2e # .Lif sw t0, 20(sp) li t0, 0x202c3061 # a0,_ sw t0, 16(sp) li t0, 0x207a7165 # eqz_ sw t0, 12(sp) li t0, 0x62626262 # bbbb sb t0, 11(sp) addi a0, sp, 11 li a1, 13 call _write_out # Write the label counter. mv a0, s2 call _printi li a0, '\n' call _put_char call _compile_statements mv s1, a0 # Skip end. # Write the label prefix. addi a0, sp, 20 li a1, 4 call _write_out # Write the label counter. mv a0, s2 call _printi # Finalize the label. li t0, 0x0a3a # :\n sh t0, 16(sp) addi a0, sp, 16 li a1, 2 call _write_out addi s2, s2, 1 # Increment the label counter. # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Writes: # .type identifier, @function # identifier: # # Parameters: # a0 - Identifier length. # a0 - Identifier pointer. .type _write_procedure_head, @function _write_procedure_head: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 sw a0, 16(sp) sw a1, 20(sp) # .type identifier, @function la a0, asm_type li a1, ASM_TYPE_SIZE call _write_out lw a0, 20(sp) lw a1, 16(sp) call _write_out la a0, asm_type_function li a1, ASM_TYPE_FUNCTION_SIZE call _write_out lw a0, 20(sp) lw a1, 16(sp) call _write_out li t0, 0x0a3a # :\n sw t0, 12(sp) addi a0, sp, 12 li a1, 2 call _write_out # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Compiles a list of statements delimited by semicolons. # # Sets a0 to the end of the token finishing the list # (should be the "end" token in a valid program). .type _compile_statements, @function _compile_statements: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 # Generate the body of the procedure. mv a0, s1 addi a1, sp, 0 call _tokenize_next lw t0, 0(sp) li t1, TOKEN_END beq t0, t1, .Lcompile_statements_end .Lcompile_statements_body: call _compile_statement mv a0, s1 addi a1, sp, 0 call _tokenize_next lw t0, 0(sp) li t1, TOKEN_SEMICOLON bne t0, t1, .Lcompile_statements_end mv s1, a0 j .Lcompile_statements_body .Lcompile_statements_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Checks for the type of the current statement and compiles it. .type _compile_statement, @function _compile_statement: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 mv a0, s1 addi a1, sp, 0 call _tokenize_next lw t0, 0(sp) li t1, TOKEN_IDENTIFIER beq t0, t1, .Lcompile_statement_identifier li t1, TOKEN_GOTO beq t0, t1, .Lcompile_statement_goto li t1, TOKEN_RETURN beq t0, t1, .Lcompile_statement_return li t1, TOKEN_IF beq t0, t1, .Lcompile_statement_if li t1, TOKEN_DOT beq t0, t1, .Lcompile_statement_label unimp # Else. .Lcompile_statement_if: call _compile_if j .Lcompile_statement_end .Lcompile_statement_label: call _compile_label j .Lcompile_statement_end .Lcompile_statement_return: call _compile_return j .Lcompile_statement_end .Lcompile_statement_goto: call _compile_goto j .Lcompile_statement_end .Lcompile_statement_identifier: call _compile_identifier j .Lcompile_statement_end .Lcompile_statement_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Prints ".section .text" and exits. .type _compile_text_section, @function _compile_text_section: # Prologue. addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) addi s0, sp, 16 la a0, section_text li a1, SECTION_TEXT_SIZE call _write_out # Epilogue. lw ra, 12(sp) lw s0, 8(sp) addi sp, sp, 16 ret .type _compile_entry_point, @function _compile_entry_point: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 # .type _start, @function la a0, asm_start li a1, ASM_START_SIZE call _write_out mv a0, s1 addi a1, sp, 4 call _tokenize_next mv s1, a0 # Skip begin. # Generate the body of the procedure. call _compile_statements mv s1, a0 # Skip end. la a0, asm_exit li a1, ASM_EXIT_SIZE call _write_out # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret .type _compile, @function _compile: # Prologue. addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) addi s0, sp, 16 call _compile_module_declaration call _compile_import call _compile_constant_section call _compile_variable_section call _compile_text_section call _compile_procedure_section call _compile_entry_point # Epilogue. lw ra, 12(sp) lw s0, 8(sp) addi sp, sp, 16 ret # Entry point. .type _start, @function _start: # Read the source from the standard input. la a0, source_code li a1, SOURCE_BUFFER_SIZE # Buffer size. call _read_file li s2, 1 call symbol_table_build call _compile # Call exit. li a0, 0 # Use 0 return code. call _exit