diff --git a/compiler/main/CMakeLists.txt b/compiler/main/CMakeLists.txt index 812552fd..a44e0c4b 100644 --- a/compiler/main/CMakeLists.txt +++ b/compiler/main/CMakeLists.txt @@ -62,6 +62,7 @@ add_library("sd-base" x86_code_gen/cg_x86_basic_block.c x86_code_gen/cg_x86_single_function.c x86_code_gen/cg_x86_single_tac.c + x86_code_gen/allocate_registers_x86.c x86_code_gen/compile_ir/compile_tac.h x86_code_gen/compile_ir/compile_tac_binary_op.c x86_code_gen/compile_ir/compile_tac_call.c diff --git a/compiler/main/avr_code_gen/cg_avr.c b/compiler/main/avr_code_gen/cg_avr.c index a80a7fb3..48875a78 100644 --- a/compiler/main/avr_code_gen/cg_avr.c +++ b/compiler/main/avr_code_gen/cg_avr.c @@ -53,8 +53,7 @@ bool compile_and_write_avr(struct AST* ast, struct Ctx* ctx) { struct IBuffer* ibu = ibu_ctor(); - //struct RAT* rat = rat_ctor(ctx_tables(ctx)); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 10); struct TAC* t = makeTACSetupSP(); emit_asm_avr_single_tac(rat, t, ctx, ibu); diff --git a/compiler/main/avr_code_gen/cg_avr_basic_block.c b/compiler/main/avr_code_gen/cg_avr_basic_block.c index bf9ae1b7..69d74810 100644 --- a/compiler/main/avr_code_gen/cg_avr_basic_block.c +++ b/compiler/main/avr_code_gen/cg_avr_basic_block.c @@ -2,6 +2,7 @@ #include #include "cli/flags/flags.h" +#include "liveness/liveness.h" #include "tables/symtable/symtable.h" #include "tac/tac.h" @@ -28,8 +29,10 @@ void emit_asm_avr_basic_block(struct BasicBlock* block, struct Ctx* ctx, struct block->visited_emit_asm = true; + struct Liveness* live = liveness_calc_tacbuffer(block->buffer); + //create register allocation table for the basic block. - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, liveness_ntemps(live)); //simplest naive approach (first iteration): //simply get a new register for each temporary @@ -49,6 +52,8 @@ void emit_asm_avr_basic_block(struct BasicBlock* block, struct Ctx* ctx, struct rat_dtor(rat); + liveness_dtor(live); + //false/default branch gets emitted first, //because there is no label for it in a lot of cases //this way we can avoid an extra jump that's really diff --git a/compiler/main/liveness/liveness.c b/compiler/main/liveness/liveness.c index d4d535b9..5623ecb9 100644 --- a/compiler/main/liveness/liveness.c +++ b/compiler/main/liveness/liveness.c @@ -72,6 +72,10 @@ void liveness_print(struct Liveness* l) { map_print(l->map_out, l->nstmts, l->ntemps, "out[n]"); } +size_t liveness_ntemps(struct Liveness* l) { + return l->ntemps; +} + static bool liveness_get(struct Liveness* l, size_t stmt_index, size_t tmp_index) { return l->map_out[stmt_index][tmp_index] || l->map_in[stmt_index][tmp_index]; } diff --git a/compiler/main/liveness/liveness.h b/compiler/main/liveness/liveness.h index 7033c6d5..67c108b7 100644 --- a/compiler/main/liveness/liveness.h +++ b/compiler/main/liveness/liveness.h @@ -23,6 +23,10 @@ struct Liveness* liveness_calc_tacbuffer(struct TACBuffer* buf); // print tables to stdout for debug void liveness_print(struct Liveness* l); +// @returns number of temporaries that +// the liveness info keeps track of +size_t liveness_ntemps(struct Liveness* l); + // This can be used in register allocation. // If a temporary does not have overlapping liveness with // the other temporaries already occupying a register, diff --git a/compiler/main/x86_code_gen/allocate_registers_x86.c b/compiler/main/x86_code_gen/allocate_registers_x86.c new file mode 100644 index 00000000..8a2b01de --- /dev/null +++ b/compiler/main/x86_code_gen/allocate_registers_x86.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include + +#include "liveness/liveness.h" +#include "tables/symtable/symtable.h" + +#include "tac/tac.h" +#include "tac/tacbuffer.h" +#include "basic_block/basicblock.h" +#include "rat/rat.h" + +#include "allocate_registers_x86.h" + +static void allocate_registers_single_tac(struct TAC* t, struct RAT* rat, struct ST* st, struct Liveness* live); + +void allocate_registers_basicblocks(struct BasicBlock** graph, size_t nblocks, struct RAT* rat, struct ST* st, struct Liveness* live) { + + for (size_t i = 0; i < nblocks; i++) { + allocate_registers(graph[i]->buffer, rat, st, live); + } +} + +void allocate_registers(struct TACBuffer* b, struct RAT* rat, struct ST* st, struct Liveness* live) { + + assert(live != NULL); + + for (size_t i = 0; i < tacbuffer_count(b); i++) { + struct TAC* t = tacbuffer_get(b, i); + allocate_registers_single_tac(t, rat, st, live); + } +} + +static uint32_t rat_ensure_register_x86(struct RAT* rat, uint32_t tmp, struct Liveness* live) { + // on x86 we do not have to use register pairs. + // shoo shoo, the register will be large enough :) + + if (rat_has_register(rat, tmp)) { + return rat_get_register(rat, tmp); + } + + // check for overlap with liveness + const size_t ntemps = liveness_ntemps(live); + + //TODO: overlap also with temporaries which are + //never used anywhere (and therefore have no liveness). + //These need a register too but they do not overlap. + // + //So that's a bug. + + bool* live_overlap = calloc(ntemps, sizeof(bool)); + for (size_t t = 0; t < ntemps; t++) { + if (t == tmp) { + continue; + } + live_overlap[t] = liveness_overlaps(live, tmp, t); + } + + const int32_t reg = rat_find_reg_no_overlap(rat, live_overlap, ntemps); + + assert(reg >= 0); + + free(live_overlap); + + rat_occupy(rat, reg, tmp, false); + + return reg; +} + +static void allocate_registers_single_tac(struct TAC* t, struct RAT* rat, struct ST* st, struct Liveness* live) { + + struct LVST* lvst = st->lvst; + struct SST* sst = st->sst; + + switch (t->kind) { + + case TAC_CONST_VALUE: + case TAC_BINARY_OP_IMMEDIATE: + case TAC_LOAD_LOCAL_ADDR: + case TAC_COPY: + case TAC_BINARY_OP: + case TAC_UNARY_OP: + case TAC_LOAD_CONST_ADDR: + case TAC_LOAD: + case TAC_LOAD_LOCAL: + case TAC_CALL: + rat_ensure_register_x86(rat, t->dest, live); + break; + + default: break; + } +} diff --git a/compiler/main/x86_code_gen/allocate_registers_x86.h b/compiler/main/x86_code_gen/allocate_registers_x86.h new file mode 100644 index 00000000..ab96e6c8 --- /dev/null +++ b/compiler/main/x86_code_gen/allocate_registers_x86.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "util/ctx.h" +#include "rat/rat.h" +#include "basic_block/basicblock.h" + +struct IBuffer; +struct Liveness; + +void allocate_registers_basicblocks(struct BasicBlock** graph, size_t nblocks, struct RAT* rat, struct ST* st, struct Liveness* live); + +void allocate_registers(struct TACBuffer* b, struct RAT* rat, struct ST* st, struct Liveness* live); diff --git a/compiler/main/x86_code_gen/cg_x86_basic_block.c b/compiler/main/x86_code_gen/cg_x86_basic_block.c index 4326f8df..d01afa03 100644 --- a/compiler/main/x86_code_gen/cg_x86_basic_block.c +++ b/compiler/main/x86_code_gen/cg_x86_basic_block.c @@ -49,77 +49,3 @@ void emit_asm_x86_basic_block(struct BasicBlock* block, struct Ctx* ctx, struct emit_asm_x86_basic_block(block->branch_2, ctx, ibu, rat); emit_asm_x86_basic_block(block->branch_1, ctx, ibu, rat); } - -void allocate_registers(struct TACBuffer* b, struct RAT* rat, struct ST* st) { - - for (size_t i = 0; i < tacbuffer_count(b); i++) { - struct TAC* t = tacbuffer_get(b, i); - allocate_registers_single_tac(t, rat, st); - } -} - -static void allocate_registers_single_tac(struct TAC* t, struct RAT* rat, struct ST* st) { - - struct LVST* lvst = st->lvst; - struct SST* sst = st->sst; - - // on x86 we do not have to use register pairs. - // shoo shoo, the register will be large enough :) - const bool iswide = false; - - switch (t->kind) { - - case TAC_CONST_VALUE: - rat_ensure_register(rat, t->dest, true, iswide); - break; - - case TAC_BINARY_OP_IMMEDIATE: - rat_ensure_register(rat, t->dest, true, iswide); - break; - - case TAC_LOAD_LOCAL_ADDR: - //address always needs 2 registers - rat_ensure_register(rat, t->dest, true, true); - break; - - case TAC_CALL: { - - if (sst_size(sst) > t->arg1) { - //in TAC tests the SST might not be fully initialized - struct Type* return_type = sst_at(sst, t->arg1)->return_type; - } - - rat_ensure_register(rat, t->dest, false, iswide); - } break; - - case TAC_COPY: - rat_ensure_register(rat, t->dest, false, iswide); - break; - - case TAC_BINARY_OP: - rat_ensure_register(rat, t->dest, false, iswide); - break; - - case TAC_UNARY_OP: - rat_ensure_register(rat, t->dest, false, iswide); - break; - - case TAC_LOAD_LOCAL: { - //look at the LVST to see the width of the local var - struct Type* local_type = lvst_at(lvst, t->arg1)->type; - rat_ensure_register(rat, t->dest, false, iswide); - } break; - - case TAC_LOAD_CONST_ADDR: - rat_ensure_register(rat, t->dest, false, true); - break; - - case TAC_LOAD: - //sadly we do not know what is all going to be added/subtracted - //from what we load there, could be a pointer, so it must be wide - rat_ensure_register(rat, t->dest, false, true); - break; - - default: break; - } -} diff --git a/compiler/main/x86_code_gen/cg_x86_basic_block.h b/compiler/main/x86_code_gen/cg_x86_basic_block.h index 46b3d7cc..c2cbec5b 100644 --- a/compiler/main/x86_code_gen/cg_x86_basic_block.h +++ b/compiler/main/x86_code_gen/cg_x86_basic_block.h @@ -9,5 +9,3 @@ struct IBuffer; void emit_asm_x86_basic_block(struct BasicBlock* block, struct Ctx* ctx, struct IBuffer* ibu, struct RAT* rat); - -void allocate_registers(struct TACBuffer* b, struct RAT* rat, struct ST* st); diff --git a/compiler/main/x86_code_gen/cg_x86_single_function.c b/compiler/main/x86_code_gen/cg_x86_single_function.c index f16f0640..10aa7e27 100644 --- a/compiler/main/x86_code_gen/cg_x86_single_function.c +++ b/compiler/main/x86_code_gen/cg_x86_single_function.c @@ -16,8 +16,10 @@ #include "tables/symtable/symtable.h" #include "cg_x86_single_function.h" +#include "allocate_registers_x86.h" #include "cli/flags/flags.h" +#include "liveness/liveness.h" void compile_and_write_x86_single_function(struct Method* m, struct Ctx* ctx, struct IBuffer* ibu) { @@ -46,10 +48,11 @@ void compile_and_write_x86_single_function(struct Method* m, struct Ctx* ctx, st struct BasicBlock** graph = basicblock_create_graph(buffer, m->decl->name, &nblocks, ctx); struct BasicBlock* root = graph[0]; - struct RAT* rat = rat_ctor(RAT_ARCH_X86); - for (int i = 0; i < nblocks; i++) { - allocate_registers(graph[i]->buffer, rat, st); - } + struct Liveness* live = liveness_calc(graph, nblocks); + + struct RAT* rat = rat_ctor(RAT_ARCH_X86, liveness_ntemps(live)); + + allocate_registers_basicblocks(graph, nblocks, rat, st, live); emit_asm_x86_basic_block(root, ctx, ibu, rat); diff --git a/compiler/test/testcases.c b/compiler/test/testcases.c index 235fab3f..9f84b605 100644 --- a/compiler/test/testcases.c +++ b/compiler/test/testcases.c @@ -13,7 +13,7 @@ #include "parser/test/commandline/test.h" #include "token/test/test.h" #include "tac/test/test.h" -#include "rat/test.h" +#include "rat/test/test.h" #include "ast/test/test_str_ast.h" void (*tests_ast[])() = { @@ -55,6 +55,7 @@ void (*tests_rat[])() = { test_rat_occupant_pair, test_rat_alloc_n_regs, test_rat_is_wide, + test_rat_multiple_temps_in_register, NULL, }; diff --git a/compiler/test/x86_code_gen/compile_ir/test_compile_tac_param.c b/compiler/test/x86_code_gen/compile_ir/test_compile_tac_param.c index 1abb2b1e..a639bc3d 100644 --- a/compiler/test/x86_code_gen/compile_ir/test_compile_tac_param.c +++ b/compiler/test/x86_code_gen/compile_ir/test_compile_tac_param.c @@ -32,6 +32,9 @@ static void test_param(uint64_t fixed_value, bool debug) { tacbuffer_append(b, makeTACSetupStackframe(stackframe_size_bytes)); tacbuffer_append(b, makeTACConst(0, fixed_value)); tacbuffer_append(b, makeTACConst(1, 0)); + + tacbuffer_append(b, makeTACCopy(1, 1)); + tacbuffer_append(b, makeTACParam(0, false)); struct sd_uc_engine* system = sd_uc_engine_from_tacbuffer_v3(b, debug, true, stackframe_size); diff --git a/compiler/test/x86_code_gen/compile_ir/test_compile_tac_return.c b/compiler/test/x86_code_gen/compile_ir/test_compile_tac_return.c index d061e523..b5a922c2 100644 --- a/compiler/test/x86_code_gen/compile_ir/test_compile_tac_return.c +++ b/compiler/test/x86_code_gen/compile_ir/test_compile_tac_return.c @@ -31,6 +31,9 @@ static void test_fixed_value(uint32_t value, bool debug) { tacbuffer_append(b, makeTACConst(0, value + 1)); tacbuffer_append(b, makeTACConst(1, value)); tacbuffer_append(b, makeTACConst(2, value - 1)); + + tacbuffer_append(b, makeTACCopy(2, 0)); + tacbuffer_append(b, makeTACCopy(0, 2)); tacbuffer_append(b, makeTACReturn(1)); struct sd_uc_engine* system = sd_uc_engine_from_tacbuffer_v3(b, debug, true, stackframe_size); diff --git a/compiler/test/x86_code_gen/test_x86_code_gen_util.c b/compiler/test/x86_code_gen/test_x86_code_gen_util.c index a5d29506..f0d3b5a2 100644 --- a/compiler/test/x86_code_gen/test_x86_code_gen_util.c +++ b/compiler/test/x86_code_gen/test_x86_code_gen_util.c @@ -12,6 +12,7 @@ #include "x86_code_gen/cg_x86.h" #include "x86_code_gen/cg_x86_single_function.h" #include "x86_code_gen/cg_x86_basic_block.h" +#include "x86_code_gen/allocate_registers_x86.h" #include "tac/tacbuffer.h" #include "basic_block/basicblock.h" @@ -21,6 +22,7 @@ #include "cli/flags/flags.h" #include "fake_lvst.h" +#include "liveness/liveness.h" struct sd_uc_engine { // wrapper struct @@ -276,13 +278,17 @@ static void gen_from_tacbuffer(struct TACBuffer* buffer, FILE* fout, struct Ctx* exit(EXIT_FAILURE); } + struct Liveness* live = liveness_calc(graph, nblocks); + + if (flags_debug(ctx_flags(ctx))) { + liveness_print(live); + } + struct IBuffer* ibu = ibu_ctor(); - struct RAT* rat = rat_ctor(RAT_ARCH_X86); + struct RAT* rat = rat_ctor(RAT_ARCH_X86, liveness_ntemps(live)); - for (int i = 0; i < nblocks; i++) { - allocate_registers(graph[i]->buffer, rat, ctx_tables(ctx)); - } + allocate_registers_basicblocks(graph, nblocks, rat, ctx_tables(ctx), live); emit_asm_x86_basic_block(root, ctx, ibu, rat); diff --git a/examples/Makefile b/examples/Makefile index edfa1d5e..b56bcf27 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -3,12 +3,26 @@ #the tests are in the same order as a 'tree examples/' would show them -CALL2 := sd +CALL2 := sd +CALLX86 := sd -x86 -all: test +all: test examples_x86 -test: - @echo "[Examples] Compiling Example Programs..." +examples_x86: + @echo "[Examples] Compiling Example Programs (x86)" + $(CALLX86) ifstatement/ifstatement.dg + $(CALLX86) local_variables/simple.dg + $(CALLX86) loops/whileloop.dg + $(CALLX86) loops/forloop.dg + $(CALLX86) mathematics/fibonacci.dg + $(CALLX86) methodCalls/methodcall.dg + $(CALLX86) other/everything.dg + $(CALLX86) typeinference/localvartypeinference.dg + $(CALLX86) array/array.dg + @echo "[Examples (x86)] done" + +test: + @echo "[Examples] Compiling Example Programs (AVR)" $(CALL2) ifstatement/ifstatement.dg $(CALL2) local_variables/simple.dg $(CALL2) loops/whileloop.dg @@ -20,8 +34,7 @@ test: $(CALL2) array/array.dg $(MAKE) -C led_blink_timer $(MAKE) -C led_blink_no_timer - - @echo "[Examples] done" + @echo "[Examples (AVR)] done" clean: rm -f */*.tokens diff --git a/rat/CMakeLists.txt b/rat/CMakeLists.txt index dca8e61f..f346a73b 100644 --- a/rat/CMakeLists.txt +++ b/rat/CMakeLists.txt @@ -3,14 +3,8 @@ add_library("sd-rat" STATIC rat.c ) -add_library("sd-rat-test" - rat.c - test.c -) - target_link_libraries("sd-rat" sd-util) -target_link_libraries("sd-rat-test" sd-util) -target_include_directories("sd-rat-test" PUBLIC "..") target_compile_options("sd-rat" PUBLIC ${COMMON_OPTS}) -target_compile_options("sd-rat-test" PUBLIC ${COMMON_OPTS}) + +add_subdirectory(test) diff --git a/rat/_struct.h b/rat/_struct.h new file mode 100644 index 00000000..e9fe5253 --- /dev/null +++ b/rat/_struct.h @@ -0,0 +1,52 @@ +#include +#include +#include + +#include "rat.h" + +enum RAT_REG_STATUS { + REG_OCCUPIED, //reg occupied by temporary + REG_RESERVED, //reg reserved, cannot be allocated + REG_FREE, //reg can be allocated +}; + +struct RAT { +//struct RAT should be opaque outside of it's +//implementation file + +//Register Allocation Table +//allocation of the registors used inside a function, +//mapping temporaries to actual registers + +// only used to allocate the rat. +// the implementation should use rat_capacity(rat) +// which respects the target architecture +#define RAT_CAPACITY_MAXIMUM 32 + + char* note[RAT_CAPACITY_MAXIMUM]; + + //who occupies it (which temp) + //uint32_t occupant[RAT_CAPACITY_MAXIMUM]; + + // which temporaries are occupying the register. + // occupant[r8][3] == true + // means that t3 is stored in r8 + // occupant[r8][4] == true + // means that t4 is also stored in r8 + bool* occupant[RAT_CAPACITY_MAXIMUM]; + + // number of temporaries + // that this RAT keeps track of + size_t ntemps; + + enum RAT_REG_STATUS status[RAT_CAPACITY_MAXIMUM]; + +#undef RAT_CAPACITY_MAXIMUM + + //X: r26/r27 + //Y: r28/r29 + //Z: r30/r31 + + // which machine architecture is this table for + enum RAT_ARCH arch; +}; diff --git a/rat/rat.c b/rat/rat.c index b5c2ed7c..e436d5e4 100644 --- a/rat/rat.c +++ b/rat/rat.c @@ -6,61 +6,32 @@ #include "../util/exit_malloc/exit_malloc.h" #include "rat.h" +#include "_struct.h" //gives index of a free register, exits on failure. //the register still has to be occupied static int rat_get_free_register(struct RAT* rat, bool high_regs_only, bool wide); -static bool rat_has_register(struct RAT* rat, uint32_t tmp_index); - -static void rat_occupy(struct RAT* rat, uint8_t reg, uint32_t tmp_index, bool wide); - static void rat_init(struct RAT* rat); static void rat_init_avr(struct RAT* rat); static void rat_init_x86(struct RAT* rat); -enum RAT_REG_STATUS { - REG_OCCUPIED, //reg occupied by temporary - REG_RESERVED, //reg reserved, cannot be allocated - REG_FREE, //reg can be allocated -}; - -struct RAT { -//struct RAT should be opaque outside of it's -//implementation file - -//Register Allocation Table -//allocation of the registors used inside a function, -//mapping temporaries to actual registers - -// only used to allocate the rat. -// the implementation should use rat_capacity(rat) -// which respects the target architecture -#define RAT_CAPACITY_MAXIMUM 32 - - char* note[RAT_CAPACITY_MAXIMUM]; - uint32_t occupant[RAT_CAPACITY_MAXIMUM]; //who occupies it (which temp) - enum RAT_REG_STATUS status[RAT_CAPACITY_MAXIMUM]; - -#undef RAT_CAPACITY_MAXIMUM - - //X: r26/r27 - //Y: r28/r29 - //Z: r30/r31 - - // which machine architecture is this table for - enum RAT_ARCH arch; -}; - -struct RAT* rat_ctor(enum RAT_ARCH arch) { +// @param arch the target architecture +// @param ntemps maximum number of temporaries +// to make space for +struct RAT* rat_ctor(enum RAT_ARCH arch, size_t ntemps) { struct RAT* rat = exit_malloc(sizeof(struct RAT)); rat->arch = arch; + rat->ntemps = ntemps; for (int i = 0; i < rat_capacity(rat); i++) { rat->status[i] = REG_FREE; rat->note[i] = ""; + + // 0-initialized + rat->occupant[i] = calloc(rat->ntemps, sizeof(bool)); } rat_init(rat); @@ -128,6 +99,10 @@ static void rat_init_avr(struct RAT* rat) { } void rat_dtor(struct RAT* rat) { + + for (int i = 0; i < rat_capacity(rat); i++) { + free(rat->occupant[i]); + } free(rat); } @@ -176,37 +151,49 @@ static void rat_print_regname(struct RAT* rat, size_t i) { } } -void rat_print(struct RAT* rat) { +static void rat_print_reg(struct RAT* rat, size_t i) { - printf("Register Allocation Table:\n"); - for (size_t i = 0; i < rat_capacity(rat); i++) { + rat_print_regname(rat, i); + printf(": "); - rat_print_regname(rat, i); - printf(": "); + switch (rat->status[i]) { - switch (rat->status[i]) { - - case REG_OCCUPIED: - printf("t%d", rat->occupant[i]); - break; - case REG_FREE: - printf("%20s", " - "); - break; - case REG_RESERVED: - printf("%20s", rat->note[i]); - break; - } + case REG_OCCUPIED: + for (size_t t = 0; t < rat->ntemps; t++) { - printf("\n"); + if (rat->occupant[i][t]) { + printf("t%ld, ", t); + } + } + break; + case REG_FREE: + printf("%20s", " - "); + break; + case REG_RESERVED: + printf("%20s", rat->note[i]); + break; } + printf("\n"); +} + +void rat_print(struct RAT* rat) { + + printf("Register Allocation Table:\n"); + for (size_t i = 0; i < rat_capacity(rat); i++) { + + rat_print_reg(rat, i); + } printf("------------\n"); } -static bool rat_has_register(struct RAT* rat, uint32_t tmp_index) { +bool rat_has_register(struct RAT* rat, uint32_t tmp_index) { for (int i = 0; i < rat_capacity(rat); i++) { - if (rat->status[i] == REG_OCCUPIED && rat->occupant[i] == tmp_index) { + if (rat->status[i] != REG_OCCUPIED) { + continue; + } + if (rat->occupant[i][tmp_index]) { return true; } } @@ -216,7 +203,10 @@ static bool rat_has_register(struct RAT* rat, uint32_t tmp_index) { int rat_get_register(struct RAT* rat, uint32_t tmp_index) { for (int i = 0; i < rat_capacity(rat); i++) { - if (rat->status[i] == REG_OCCUPIED && (rat->occupant[i] == tmp_index)) { + if (rat->status[i] != REG_OCCUPIED) { + continue; + } + if (rat->occupant[i][tmp_index]) { return i; } } @@ -236,19 +226,64 @@ uint32_t rat_occupant(struct RAT* rat, uint8_t reg) { exit(1); } - return rat->occupant[reg]; + for (size_t i = 0; i < rat->ntemps; i++) { + if (rat->occupant[reg][i]) { + return i; + } + } + + fprintf(stderr, "rat: did not find occupant of register %d\n", reg); + exit(1); + + return 0; } -static void rat_occupy(struct RAT* rat, uint8_t reg, uint32_t tmp_index, bool wide) { +bool rat_occupies(struct RAT* rat, uint8_t reg, uint32_t tmp_index) { + + assert(tmp_index < rat->ntemps); + return rat->occupant[reg][tmp_index]; +} + +void rat_occupy(struct RAT* rat, uint8_t reg, uint32_t tmp_index, bool wide) { //occupy the register rat->status[reg] = REG_OCCUPIED; - rat->occupant[reg] = tmp_index; + rat->occupant[reg][tmp_index] = true; if (wide) { rat->status[reg + 1] = REG_OCCUPIED; - rat->occupant[reg + 1] = tmp_index; + rat->occupant[reg + 1][tmp_index] = true; + } +} + +int32_t rat_find_reg_no_overlap(struct RAT* rat, bool* temps_conflict, size_t ntemps) { + + for (int reg = 0; reg < rat_capacity(rat); reg++) { + if (rat->status[reg] == REG_RESERVED) { + continue; + } + if (rat->status[reg] == REG_FREE) { + return reg; + } + + // reg is REG_OCCUPIED + + bool conflict = false; + for (size_t t = 0; t < ntemps; t++) { + const bool occupies = rat->occupant[reg][t]; + if (temps_conflict[t] && occupies) { + conflict = true; + break; + ; + } + } + + if (!conflict) { + return reg; + } } + + return -1; } uint32_t rat_ensure_register(struct RAT* rat, uint32_t tmp_index, bool high_regs_only, bool wide) { diff --git a/rat/rat.h b/rat/rat.h index 477bed8f..bba9a03b 100644 --- a/rat/rat.h +++ b/rat/rat.h @@ -33,16 +33,41 @@ enum SD_REGISTER { struct RAT; -struct RAT* rat_ctor(enum RAT_ARCH arch); +struct RAT* rat_ctor(enum RAT_ARCH arch, size_t ntemps); void rat_dtor(struct RAT* rat); void rat_print(struct RAT* rat); +// @returns true if 'tmp_index' occupies a register +bool rat_has_register(struct RAT* rat, uint32_t tmp_index); + +// @returns the register where 'tmp_index' resides int rat_get_register(struct RAT* rat, uint32_t tmp_index); +// @returns the lowest temporary that occupies the register uint32_t rat_occupant(struct RAT* rat, uint8_t reg); +// @param reg the register +// @param temp_index the index of the temporary to look for +// @returns true if 'temp' is held by 'reg' +bool rat_occupies(struct RAT* rat, uint8_t reg, uint32_t tmp_index); + +// occupies the register forcefully, without any checks +// @param reg which register to occupy +// @param tmp_index the IR temporary +void rat_occupy(struct RAT* rat, uint8_t reg, uint32_t tmp_index, bool wide); + +// @param temps_conflict 'true' for all temporaries +// we want to avoid sharing +// a register with +// @param ntemps size of 'temps_conflict' +// @returns the register number found that +// does not contain the temporaries listed as +// overlap +// @returns -1 if such a register could not be found +int32_t rat_find_reg_no_overlap(struct RAT* rat, bool* temps_conflict, size_t ntemps); + uint32_t rat_ensure_register(struct RAT* rat, uint32_t tmp_index, bool high_regs_only, bool wide); bool rat_is_wide(struct RAT* rat, uint32_t tmp_index); diff --git a/rat/test/CMakeLists.txt b/rat/test/CMakeLists.txt new file mode 100644 index 00000000..df6d4dfc --- /dev/null +++ b/rat/test/CMakeLists.txt @@ -0,0 +1,9 @@ + +add_library("sd-rat-test" + test.c +) + +target_link_libraries("sd-rat-test" sd-util sd-rat) +target_include_directories("sd-rat-test" PUBLIC "../..") + +target_compile_options("sd-rat-test" PUBLIC ${COMMON_OPTS}) diff --git a/rat/test.c b/rat/test/test.c similarity index 69% rename from rat/test.c rename to rat/test/test.c index 2e8fdda1..b35823e4 100644 --- a/rat/test.c +++ b/rat/test/test.c @@ -2,7 +2,7 @@ #include #include -#include "rat.h" +#include "rat/rat.h" #include "test.h" @@ -12,7 +12,7 @@ void test_rat_scratch_reg() { status("scratch register"); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 1); assert(rat_scratch_reg(rat) == 16); @@ -23,7 +23,7 @@ void test_rat_alloc_different_regs() { status("allocate different registers"); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 1); int reg1 = rat_ensure_register(rat, 0, false, false); @@ -38,7 +38,7 @@ void test_rat_alloc_in_bounds() { status("allocate in bounds"); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 1); int reg1 = rat_ensure_register(rat, 0, false, false); @@ -60,7 +60,7 @@ void test_rat_can_free() { //test that we can give a register back when it's no longer needed //and then get it back later on - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 2); const int reg1 = rat_ensure_register(rat, 0, true, false); @@ -80,7 +80,7 @@ void test_rat_occupant() { //test that a temp can occupy a register and we can find out which //temp it is - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 343); const int reg1 = rat_ensure_register(rat, 342, false, false); @@ -93,7 +93,7 @@ void test_rat_can_free_pair() { status("can free a register pair"); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 2); const int reg1 = rat_ensure_register(rat, 0, true, true); @@ -111,12 +111,12 @@ void test_rat_occupant_pair() { status("can occupy and read occupant for pair"); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 35); - const int reg1 = rat_ensure_register(rat, 342, false, true); + const int reg1 = rat_ensure_register(rat, 34, false, true); - assert(rat_occupant(rat, reg1) == 342); - assert(rat_occupant(rat, reg1 + 1) == 342); + assert(rat_occupant(rat, reg1) == 34); + assert(rat_occupant(rat, reg1 + 1) == 34); rat_dtor(rat); } @@ -125,7 +125,7 @@ void test_rat_alloc_n_regs() { status("can allocate a number of regs without running out"); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 15); for (int i = 0; i < 14; i++) { @@ -139,7 +139,7 @@ void test_rat_is_wide() { status("rat_is_wide(...)"); - struct RAT* rat = rat_ctor(RAT_ARCH_AVR); + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 2); //with low regs rat_ensure_register(rat, 0, false, false); @@ -157,3 +157,25 @@ void test_rat_is_wide() { rat_dtor(rat); } + +void test_rat_multiple_temps_in_register() { + + status("rat: multiple temporaries in a register"); + + struct RAT* rat = rat_ctor(RAT_ARCH_AVR, 2); + + assert(rat_occupies(rat, 0, 0) == false); + assert(rat_occupies(rat, 0, 1) == false); + + rat_occupy(rat, 0, 0, false); + + assert(rat_occupies(rat, 0, 0) == true); + assert(rat_occupies(rat, 0, 1) == false); + + rat_occupy(rat, 0, 1, false); + + assert(rat_occupies(rat, 0, 0) == true); + assert(rat_occupies(rat, 0, 1) == true); + + rat_dtor(rat); +} diff --git a/rat/test.h b/rat/test/test.h similarity index 86% rename from rat/test.h rename to rat/test/test.h index 306fc700..5d322ab0 100644 --- a/rat/test.h +++ b/rat/test/test.h @@ -9,3 +9,4 @@ void test_rat_can_free_pair(); void test_rat_occupant_pair(); void test_rat_alloc_n_regs(); void test_rat_is_wide(); +void test_rat_multiple_temps_in_register(); diff --git a/tac/tac.c b/tac/tac.c index 7f141bb8..aaf88c0a 100644 --- a/tac/tac.c +++ b/tac/tac.c @@ -64,7 +64,6 @@ int tac_mark_used(struct TAC* tac, bool* used_map, size_t map_size) { case TAC_STORE_LOCAL: case TAC_STORE_CONST_ADDR: case TAC_LOAD: - case TAC_STORE: case TAC_UNARY_OP: case TAC_COPY: check_bounds(tac->arg1, map_size); @@ -73,6 +72,7 @@ int tac_mark_used(struct TAC* tac, bool* used_map, size_t map_size) { break; case TAC_BINARY_OP: case TAC_IF_CMP_GOTO: + case TAC_STORE: check_bounds(tac->dest, map_size); check_bounds(tac->arg1, map_size); used_map[tac->dest] = true;