diff --git a/Makefile b/Makefile index 08ff073b..aa8374ee 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all tests deps cleandeps clean run +.PHONY: all debug release tests deps cleandeps clean run all: compiler tests run: compiler ./compiler @@ -31,9 +31,7 @@ GIT_COMMIT := "$(shell git describe --abbrev=8 --dirty --always --tags)" override CXXFLAGS+= \ -std=c++20 \ - -O3 \ -pthread \ - -g \ -Wall \ -Wextra \ -Wno-unused-parameter \ @@ -44,8 +42,13 @@ override CXXFLAGS+= \ -pipe \ $(INCS) \ -DVERSION=\"$(VERSION)\" \ - -DGIT_COMMIT=\"$(GIT_COMMIT)\" \ - -DNDEBUG + -DGIT_COMMIT=\"$(GIT_COMMIT)\" + +debug: CXXFLAGS += -O0 -g +debug: compiler + +release: CXXFLAGS += -O3 -DNDEBUG +release: compiler VPATH=$(SRCDIR) diff --git a/graph.sh b/graph.sh index 8ee40f10..4293b9b8 100755 --- a/graph.sh +++ b/graph.sh @@ -5,7 +5,7 @@ for f in graphs/*.gv; do echo $f - dot $f -T svg -o ${f%%.*}.svg + dot $f -Goverlap=scale -T svg -o ${f%%.*}.svg done #rm graphs/*.gv diff --git a/src/array_pool.hpp b/src/array_pool.hpp index c45bdbe2..32650613 100644 --- a/src/array_pool.hpp +++ b/src/array_pool.hpp @@ -3,8 +3,10 @@ #include #include +#include #include #include +#include // A simple allocator that only supports allocation, not free. // Memory is still cleared on pool destruction or the calling of 'clear()'. @@ -44,6 +46,7 @@ class array_pool_t { used_size += 1; reserve(1); + assert(used); storage_t* storage = used->data + used->size; new(storage) T(std::forward(args)...); ++used->size; @@ -106,11 +109,17 @@ class array_pool_t oversized.clear(); used_size = 0; + if(used) + used->free(); + // Move the used list onto the free list. - std::unique_ptr* last = &free; - while(*last) - last = &(*last)->prev; - *last = std::move(used); + if(first_free) + first_free->set_prev(used.release()); + else + free = std::move(used); + + first_free = first_used; + first_used = nullptr; } void shrink_to_fit() @@ -119,7 +128,6 @@ class array_pool_t } // Calls Func on every allocated value. - template void for_each(Func func) { @@ -134,6 +142,27 @@ class array_pool_t std::size_t size() const { return used_size; } + // Steals all the allocations from 'other'. + // (Does not steal the free list) + void splice(array_pool_t& other) + { + if(!other.first_used) + return; + + if(first_used) + first_used->set_prev(other.used.release()); + else + used = std::move(other.used); + + first_used = other.first_used; + other.first_used = nullptr; + + oversized.splice(oversized.begin(), other.oversized); + + used_size += other.used_size; + other.used_size = 0; + } + private: using storage_t = typename std::aligned_storage::type; @@ -148,17 +177,25 @@ class array_pool_t if(free) { used = std::move(free); - free = std::move(used->prev); + free.reset(used->release_prev()); + if(!free) + first_free = nullptr; } else used.reset(new buffer_t()); + assert(!used->prev); + // Zero the size. used->size = 0; - // Update the pointer - used->prev = std::move(old_buffer); + // Update the pointers + used->set_prev(old_buffer.release()); + if(!first_used) + first_used = used.get(); } + + assert(used && used->size + size <= ChunkSize); } bool use_oversized(std::size_t size) const @@ -181,19 +218,45 @@ class array_pool_t public: ~buffer_t() { - for(unsigned i = 0; i < size; ++i) - reinterpret_cast(data[i]).~T(); + free(); + } + + void free() + { + // Implement non-recursively, to avoid stack overflows + for(buffer_t* buf = this; buf; buf = buf->prev) + { + for(unsigned i = 0; i < buf->size; ++i) + reinterpret_cast(buf->data[i]).~T(); + buf->size = 0; + } + } + + void set_prev(buffer_t* new_prev) + { + if(prev) + delete prev; + prev = new_prev; + } + + buffer_t* release_prev() + { + buffer_t* ret = prev; + prev = nullptr; + return ret; } storage_t data[ChunkSize]; - std::size_t size; - std::unique_ptr prev; + std::size_t size = 0; + buffer_t* prev = nullptr; }; std::unique_ptr used; std::unique_ptr free; - std::vector> oversized; + std::list> oversized; std::size_t used_size = 0; + buffer_t* first_used = nullptr; + buffer_t* first_free = nullptr; }; #endif diff --git a/src/asm_proc.cpp b/src/asm_proc.cpp index 768e41ce..c5bc15bd 100644 --- a/src/asm_proc.cpp +++ b/src/asm_proc.cpp @@ -94,11 +94,11 @@ void asm_proc_t::convert_long_branch_ops() dist -= size_diff; // Change to short instruction when in range - if(dist <= 127 || dist >= -128) + if(dist <= 127 && dist >= -128) { inst.op = new_op; progress = true; - + assert(bytes_between(i+1, label_i) <= 127); assert(bytes_between(i+1, label_i) >= -128); } @@ -176,7 +176,6 @@ void asm_proc_t::optimize_short_jumps(bool initial) void asm_proc_t::optimize(bool initial) { - return; // TODO // Order matters here. absolute_to_zp(); optimize_short_jumps(initial); @@ -292,7 +291,7 @@ void asm_proc_t::write_bytes(std::uint8_t* const start, int bank) const auto const write_inst = [&](asm_inst_t const& inst) { - assert(!(op_flags(inst.op) & ASMF_FAKE)); + passert(!(op_flags(inst.op) & ASMF_FAKE), to_string(inst.op), inst.arg); std::uint8_t const op = op_code(inst.op); switch(op_addr_mode(inst.op)) diff --git a/src/bitset.hpp b/src/bitset.hpp index 2aeefc04..b699ca74 100644 --- a/src/bitset.hpp +++ b/src/bitset.hpp @@ -239,7 +239,7 @@ void bitset_for_each(UInt bitset, Fn fn, unsigned span = 0) while(bitset) { std::size_t bit = builtin::ctz(bitset); - bitset ^= 1 << bit; + bitset ^= 1ull << bit; fn(Bit{bit + span}); } } diff --git a/src/byteify.cpp b/src/byteify.cpp index 765ec82d..fe9be49d 100644 --- a/src/byteify.cpp +++ b/src/byteify.cpp @@ -9,8 +9,6 @@ #include "worklist.hpp" #include "format.hpp" -SSA_VERSION(1); - namespace bc = ::boost::container; namespace // anonymous @@ -173,15 +171,20 @@ void byteify(ir_t& ir, fn_t const& fn) if(ssa_flags(ssa_it->op()) & SSAF_INDEXES_PTR) { + using namespace ssai::rw_ptr; + // Pointer accesses may create 'SSA_make_ptr' nodes. - if(ssa_it->input(0).holds_ref()) + if(ssa_it->input(PTR).holds_ref()) { + assert(!ssa_it->input(PTR_HI).holds_ref()); + ssa_ht const lo = cfg_node.emplace_ssa( - SSA_make_ptr_lo, TYPE_U, ssa_it->input(0)); + SSA_make_ptr_lo, TYPE_U, ssa_it->input(PTR)); ssa_ht const hi = cfg_node.emplace_ssa( - SSA_make_ptr_hi, TYPE_U, ssa_it->input(0)); - ssa_it->link_change_input(0, lo); - ssa_it->link_change_input(1, hi); + SSA_make_ptr_hi, TYPE_U, ssa_it->input(PTR)); + + ssa_it->link_change_input(PTR, lo); + ssa_it->link_change_input(PTR_HI, hi); } } @@ -205,8 +208,6 @@ void byteify(ir_t& ir, fn_t const& fn) if(!is_scalar(type.name())) continue; - - SSA_VERSION(1); type_t split_type = TYPE_U; if(ssa_it->type().name() == TYPE_TEA) @@ -245,8 +246,6 @@ void byteify(ir_t& ir, fn_t const& fn) cfg_node_t& cfg_node = *cfg_it; for(ssa_ht ssa_it = cfg_node.ssa_begin(); ssa_it; ++ssa_it) { - SSA_VERSION(1); - switch(ssa_it->op()) { case SSA_fn_call: @@ -515,7 +514,6 @@ void byteify(ir_t& ir, fn_t const& fn) if(ssa_node->type() == TYPE_S) ssa_node->set_type(TYPE_U); - SSA_VERSION(1); switch(ssa_node->op()) { case SSA_rol: diff --git a/src/cg.cpp b/src/cg.cpp index 29238985..2688b91f 100644 --- a/src/cg.cpp +++ b/src/cg.cpp @@ -285,12 +285,14 @@ void code_gen(ir_t& ir, fn_t& fn) // Setup 'ptr_alt's for ptr inputs. if(ssa_flags(ssa_it->op()) & SSAF_INDEXES_PTR) { - assert(ssa_it->input(0).holds_ref() == ssa_it->input(1).holds_ref()); + using namespace ssai::rw_ptr; - if(ssa_it->input(0).holds_ref()) + assert(ssa_it->input(PTR).holds_ref() == ssa_it->input(PTR_HI).holds_ref()); + + if(ssa_it->input(PTR).holds_ref()) { - ssa_ht const lo = ssa_it->input(0).handle(); - ssa_ht const hi = ssa_it->input(1).handle(); + ssa_ht const lo = ssa_it->input(PTR).handle(); + ssa_ht const hi = ssa_it->input(PTR_HI).handle(); auto& lo_d = cg_data(lo); auto& hi_d = cg_data(hi); diff --git a/src/cg_cset.cpp b/src/cg_cset.cpp index ac9f5df6..1e84bb7a 100644 --- a/src/cg_cset.cpp +++ b/src/cg_cset.cpp @@ -32,6 +32,7 @@ ssa_ht cset_head(ssa_ht h) assert(h->op()); while(true) { + // TODO: update pointers to point to head. auto& d = cg_data(h); if(d.cset_head.holds_ref()) { diff --git a/src/cg_isel.cpp b/src/cg_isel.cpp index d39a54cc..f2b53071 100644 --- a/src/cg_isel.cpp +++ b/src/cg_isel.cpp @@ -57,7 +57,7 @@ namespace isel }; // Main global state of the instruction selection algorithm. - static thread_local state_t state; + thread_local state_t state; template struct param @@ -280,20 +280,32 @@ namespace isel cont->call(cpu_copy, sel); } + // These determine how extensive the search is. + constexpr unsigned MAX_MAP_SIZE = 128; + //constexpr unsigned SHRINK_MAP_SIZE = MAX_MAP_SIZE / 2; + constexpr unsigned cost_cutoff(unsigned size) + { + constexpr unsigned BASE = cost_fn * 3; + return BASE >> (size >> 4); + } + // Finishes the selection. template void finish(cpu_t const& cpu, sel_t const* sel, cons_t const*) { state_t::map_t<>::value_type insertion = { cpu, sel }; + unsigned const sel_cost = get_cost(sel); + + if(sel_cost > state.next_best_cost + cost_cutoff(state.next_map.size())) + return; + // If this completes a node's operations, we'll release 'req_store'. if(FinishNode) // TODO: remove cast (std::uint64_t&)insertion.first.req_store &= ~cg_data(state.ssa_node).isel.last_use; auto result = state.next_map.insert(std::move(insertion)); - unsigned const sel_cost = get_cost(sel); - if(!result.second && sel_cost < get_cost(result.first->second)) result.first->second = sel; @@ -312,32 +324,76 @@ namespace isel } #endif - // These determine how extensive the search is. - constexpr unsigned COST_CUTOFF = cost_fn * 3; - constexpr unsigned MAX_MAP_SIZE = 512; - // Runs the function and adds the results to the state map. template void select_step(Fn fn) { + //std::cout << state.sel_pool.size() << std::endl; state.next_map.clear(); - state.next_best_cost = ~0 - COST_CUTOFF; + state.next_best_cost = ~0 - cost_cutoff(0); cons_t const cont = { finish }; //std::cout << "BEST COST = " << state.best_cost << std::endl; + unsigned const cutoff = cost_cutoff(state.map.size()); + unsigned count = 0; + for(auto const& pair : state.map) - if(get_cost(pair.second) < state.best_cost + COST_CUTOFF) + if(get_cost(pair.second) <= state.best_cost + cutoff) + { fn(pair.first, pair.second, &cont); + count += 1; + } if(state.next_map.empty()) throw isel_no_progress_error_t{}; + //std::cout << "FINISH count = " << count << ' ' << state.map.size() << ' ' << state.ssa_node->op() << std::endl; + if(state.next_map.size() > MAX_MAP_SIZE) { - //std::cout << "OLD SIZE = " << state.next_map.size() << std::endl; + std::cout << "OLD SIZE = " << state.next_map.size() << std::endl; + +#if 0 + state.map.clear(); + + auto it = state.next_map.begin(); + auto const end = state.next_map.end() - 1; + for(; it < end; it += 2) + { + auto const next = std::next(it); + if(get_cost(it->second) < get_cost(next->second)) + state.map.insert(*it); + else + state.map.insert(*next); + } + + if(it < state.next_map.end()) + state.map.insert(*it); +#else + + auto const rank = [&](auto const& a) -> int + { + return (int(get_cost(a.second)) + + int(builtin::popcount(a.first.req_store)) + - int(builtin::popcount(unsigned(a.first.known_mask)))); + }; + + std::sort(state.next_map.begin(), state.next_map.end(), [&](auto const& a, auto const& b) + { + return rank(a) < rank(b); + }); + + assert(get_cost(state.next_map.begin()->second) == state.next_best_cost); + + state.map.clear(); + for(unsigned i = 0; i < MAX_MAP_SIZE; ++i) + state.map.insert(state.next_map.begin()[i]); +#endif + + /* state.approx_map.clear(); for(auto const& pair : state.next_map) { @@ -350,11 +406,12 @@ namespace isel state.next_map.clear(); for(auto const& pair : state.approx_map) state.next_map.insert(pair); + */ - //std::cout << "NEW SIZE = " << state.next_map.size() << std::endl; + std::cout << "NEW SIZE = " << state.map.size() << std::endl; } - - state.map.swap(state.next_map); + else + state.map.swap(state.next_map); state.best_cost = state.next_best_cost; } @@ -491,7 +548,7 @@ namespace isel // Thus, prefer pick_op. template [[gnu::noinline]] void exact_op(options_t opt, locator_t def, locator_t arg, locator_t ptr_hi, ssa_value_t ssa_def, ssa_value_t ssa_arg, - cpu_t const& cpu, sel_t const* prev, cons_t const* cont, unsigned extra_penalty = 0) + cpu_t const& cpu, sel_t const* prev, cons_t const* cont) { constexpr auto mode = op_addr_mode(Op); @@ -510,7 +567,7 @@ namespace isel penalty = handle_req_store_penalty(cpu_copy, ssa_def, ssa_arg); } - cont->call(cpu_copy, &alloc_sel(opt, prev, arg, ptr_hi, penalty + extra_penalty)); + cont->call(cpu_copy, &alloc_sel(opt, prev, arg, ptr_hi, penalty)); } } @@ -873,7 +930,7 @@ namespace isel >(cpu, prev, cont); } - template struct pick_op_xy { @@ -884,55 +941,38 @@ namespace isel locator_t const index = array_index::value(); - if(index.is_const_num()) + if(Absolute != BAD_OP && index.is_const_num()) { - if(AbsoluteX != BAD_OP && cpu.is_known(REG_X)) - { - locator_t mem = array_mem::trans(); - int const offset = int(index.data()) - int(cpu.known[REG_X]); - mem.advance_offset(offset); + locator_t mem = array_mem::trans(); + mem.advance_offset(index.data()); - exact_op( - OptN::to_struct, Def::value(), mem, locator_t{}, - Def::node(), array_mem::node(), cpu, prev, cont, - std::abs(offset)); - - return; + exact_op( + OptN::to_struct, Def::value(), mem, locator_t{}, + Def::node(), array_mem::node(), cpu, prev, cont); + } + else + { + if(AbsoluteX != BAD_OP) + { + chain + < load_X> + , exact_op> + >(cpu, prev, cont); } - if(AbsoluteY != BAD_OP && cpu.is_known(REG_Y)) + if(AbsoluteY != BAD_OP) { - locator_t mem = array_mem::trans(); - mem.advance_offset(int(index.data()) - int(cpu.known[REG_Y])); - - exact_op( - OptN::to_struct, Def::value(), mem, locator_t{}, - Def::node(), array_mem::node(), cpu, prev, cont); - - return; + chain + < load_Y> + , exact_op> + >(cpu, prev, cont); } } - - if(AbsoluteX != BAD_OP) - { - chain - < load_X> - , exact_op> - >(cpu, prev, cont); - } - - if(AbsoluteY != BAD_OP) - { - chain - < load_Y> - , exact_op> - >(cpu, prev, cont); - } } }; - template - struct pick_op_xy + template + struct pick_op_xy { [[gnu::noinline]] static void call(cpu_t const& cpu, sel_t const* prev, cons_t const* cont) {} @@ -959,7 +999,7 @@ namespace isel else if(immediate && Arg::trans().is_immediate()) simple_op(Opt::to_struct, Def::value(), Arg::trans(), cpu, prev, cont); else if((absolute_X || absolute_Y) && read_direct) - pick_op_xy::call(cpu, prev, cont); + pick_op_xy::call(cpu, prev, cont); else if(absolute && !Arg::trans().is_immediate() && !read_direct) exact_op(Opt::to_struct, Def::value(), Arg::trans(), Arg::trans_hi(), Def::node(), Arg::node(), cpu, prev, cont); } @@ -1069,19 +1109,19 @@ namespace isel chain < load_A , set_defs - , store + , store >(cpu, prev, cont); chain < load_X , set_defs - , store + , store >(cpu, prev, cont); chain < load_Y , set_defs - , store + , store >(cpu, prev, cont); }; @@ -1661,29 +1701,75 @@ namespace isel template void read_array(cpu_t const& cpu, sel_t const* prev, cons_t const* cont) { - chain - < load_X - , exact_op - , store - >(cpu, prev, cont); + locator_t const index = Index::value(); - chain - < load_X - , exact_op - , store - >(cpu, prev, cont); + if(index.is_const_num()) + { + using temp = param; - chain - < load_Y - , exact_op - , store - >(cpu, prev, cont); + locator_t mem = Array::trans(); + mem.advance_offset(index.data()); + mem.set_is(IS_DEREF); + temp::set(mem); - chain - < load_Y - , exact_op - , store - >(cpu, prev, cont); + load_then_store(cpu, prev, cont); + } + else + { + chain + < load_X + , exact_op + , store + >(cpu, prev, cont); + + chain + < load_X + , exact_op + , store + >(cpu, prev, cont); + + chain + < load_Y + , exact_op + , store + >(cpu, prev, cont); + + chain + < load_Y + , exact_op + , store + >(cpu, prev, cont); + } + } + + template + void write_array(cpu_t const& cpu, sel_t const* prev, cons_t const* cont) + { + locator_t const index = Index::value(); + + if(index.is_const_num()) + { + using temp = param; + + locator_t mem = Array::trans(); + mem.advance_offset(index.data()); + mem.set_is(IS_DEREF); + temp::set(mem); + + load_then_store(cpu, prev, cont); + } + else + { + chain + < load_AX + , exact_op + >(cpu, prev, cont); + + chain + < load_AY + , exact_op + >(cpu, prev, cont); + } } template @@ -2196,21 +2282,15 @@ namespace isel } else { - using p_index = p_arg<0>; - using p_assignment = p_arg<1>; + using p_array = p_arg<0>; + using p_index = p_arg<1>; + using p_assignment = p_arg<2>; + p_array::set(h->input(0)); p_index::set(h->input(2)); p_assignment::set(h->input(3)); - chain - < load_AX - , exact_op - >(cpu, prev, cont); - - chain - < load_AY - , exact_op - >(cpu, prev, cont); + write_array(cpu, prev, cont); } break; @@ -2235,19 +2315,20 @@ namespace isel using p_ptr = set_ptr_hi; using p_index = p_arg<2>; - p_ptr_lo::set(h->input(0)); - p_ptr_hi::set(h->input(1)); - p_index::set(h->input(3)); + using namespace ssai::rw_ptr; - if(h->input(0).is_const()) - { - assert(!h->input(1) || h->input(1).is_const()); + p_ptr_lo::set(h->input(PTR)); + p_ptr_hi::set(h->input(PTR_HI)); + p_index::set(h->input(INDEX)); + if(h->input(PTR).is_const()) // TODO + { + assert(!h->input(PTR_HI) || h->input(PTR_HI).is_const()); read_array(cpu, prev, cont); } else { - if(h->input(3).eq_whole(0)) + if(h->input(INDEX).eq_whole(0)) { chain < load_X> @@ -2279,28 +2360,21 @@ namespace isel using p_index = p_arg<2>; using p_assignment = p_arg<3>; - p_ptr_lo::set(h->input(0)); - p_ptr_hi::set(h->input(1)); - p_index::set(h->input(3)); - p_assignment::set(h->input(4)); + using namespace ssai::rw_ptr; - if(h->input(0).is_const()) - { - assert(!h->input(1) || h->input(1).is_const()); + p_ptr_lo::set(h->input(PTR)); + p_ptr_hi::set(h->input(PTR_HI)); + p_index::set(h->input(INDEX)); + p_assignment::set(h->input(ASSIGNMENT)); - chain - < load_AX - , exact_op - >(cpu, prev, cont); - - chain - < load_AY - , exact_op - >(cpu, prev, cont); + if(h->input(PTR).is_const()) // TODO + { + assert(!h->input(PTR_HI) || h->input(PTR_HI).is_const()); + write_array(cpu, prev, cont); } else { - if(h->input(3).eq_whole(0)) + if(h->input(INDEX).eq_whole(0)) { chain < load_AX> @@ -2639,7 +2713,7 @@ std::vector select_instructions(fn_t const& fn, cfg_ht cfg_node) state.sel_pool.clear(); state.map.clear(); assert(state.map.empty()); - state.best_cost = ~0 - COST_CUTOFF; + state.best_cost = ~0 - cost_cutoff(0); state.best_sel = nullptr; //state.map.reserve(1000000); diff --git a/src/cg_liveness.cpp b/src/cg_liveness.cpp index 00aee2d8..4f3b8960 100644 --- a/src/cg_liveness.cpp +++ b/src/cg_liveness.cpp @@ -249,6 +249,8 @@ static void do_inst_rw(fn_t const& fn, lvars_manager_t const& lvars, // For indirect modes, also test the hi byte. if(indirect_addr_mode(op_addr_mode(inst.op))) { + std::cout << inst.arg << ' ' << inst.ptr_hi << std::endl; + assert(inst.arg); assert(inst.ptr_hi && inst.ptr_hi != inst.arg); test_loc(inst.ptr_hi); } @@ -421,7 +423,7 @@ void build_lvar_interferences(fn_t const& fn, ir_t const& ir, lvars_manager_t& l if(op_flags(inst.op) & ASMF_MAYBE_STORE) { assert(op_output_regs(inst.op) & REGF_M); - if(bitset_test(live, lvar_i)) + if(lvar_i < 0 || bitset_test(live, lvar_i)) { switch(inst.op) { @@ -452,6 +454,8 @@ void build_lvar_interferences(fn_t const& fn, ir_t const& ir, lvars_manager_t& l } } } + else + passert(!(op_flags(inst.op) & ASMF_MAYBE_STORE), to_string(inst.op), inst.arg); } do_inst_rw(fn, lvars, inst, [&](unsigned i, bool read, bool write) diff --git a/src/constraints_tests.cpp b/src/constraints_tests.cpp index e321a4c6..73154249 100644 --- a/src/constraints_tests.cpp +++ b/src/constraints_tests.cpp @@ -15,8 +15,6 @@ namespace bc = ::boost::container; constexpr unsigned TEST_ITER = 1000; -SSA_VERSION(1); - known_bits_t random_bits(constraints_mask_t cm, bool allow_top = false) { known_bits_t bits; diff --git a/src/eternal_new.hpp b/src/eternal_new.hpp index c2dcd33d..cfa6d179 100644 --- a/src/eternal_new.hpp +++ b/src/eternal_new.hpp @@ -1,24 +1,49 @@ #ifndef ETERNAL_NEW_HPP #define ETERNAL_NEW_HPP +#include + // Let's you allocate data that persists until program termination. // (This could simply be 'return new T', but that triggers leak detectors.) #include "array_pool.hpp" template -inline thread_local array_pool_t eternal_new_pool; +class eternal_new_pool_t : public array_pool_t +{ +public: + using array_pool_t::array_pool_t; + + ~eternal_new_pool_t() + { + std::lock_guard lock(mutex); + eternal.splice(*this); + } + +private: + inline static std::mutex mutex; + inline static array_pool_t eternal; +}; + +// This has to be a function to get around a GCC bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81880 +template +inline eternal_new_pool_t& eternal_new_pool() +{ + thread_local eternal_new_pool_t pool; + return pool; +} template T const* eternal_new(T const* begin, T const* end) { - return eternal_new_pool.insert(begin, end); + return eternal_new_pool().insert(begin, end); } template T const* eternal_emplace(Args&&... args) { - return &eternal_new_pool.emplace(std::forward(args)...); + return &eternal_new_pool().emplace(std::forward(args)...); } #endif diff --git a/src/eval.cpp b/src/eval.cpp index 907e94b9..579c3769 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -105,7 +105,7 @@ class eval_t public: spair_t final_result; std::vector paa; // Only used when defining PAA inits - eval_tracked_t* eval_tracked = nullptr; // Various things callers of 'eval_t' may want. + precheck_tracked_t* precheck_tracked = nullptr; // Various things callers of 'eval_t' may want. enum do_t { @@ -126,7 +126,7 @@ class eval_t template eval_t(do_wrapper_t, pstring_t pstring, fn_t const& fn_ref, - eval_tracked_t* tracked, sval_t const* args, unsigned num_args); + precheck_tracked_t* tracked, sval_t const* args, unsigned num_args); eval_t(ir_t& ir, fn_t const& fn); @@ -226,7 +226,7 @@ class eval_t // compiler-specific // /////////////////////// - std::size_t num_vars() const { assert(ir); return num_locals() + ir->gmanager.num_locators(); } + std::size_t num_vars() const { assert(ir); return num_locals() + ir->gmanager.num_slots(); } unsigned to_var_i(gmanager_t::index_t index) const { assert(index); return index.id + num_locals(); } template @@ -294,9 +294,9 @@ std::vector interpret_paa(pstring_t pstring, token_t const* expr) return std::move(i.paa); } -eval_tracked_t build_tracked(fn_t const& fn) +precheck_tracked_t build_tracked(fn_t const& fn) { - eval_tracked_t tracked; + precheck_tracked_t tracked; eval_t eval(eval_t::do_wrapper_t{}, {}, fn, &tracked, nullptr, 0); return tracked; } @@ -320,12 +320,12 @@ eval_t::eval_t(do_wrapper_t, pstring_t pstring, token_t const* expr, type_t e template eval_t::eval_t(do_wrapper_t, pstring_t pstring, fn_t const& fn_ref, - eval_tracked_t* tracked, sval_t const* args, unsigned num_args) + precheck_tracked_t* tracked, sval_t const* args, unsigned num_args) : pstring(pstring) , fn(&fn_ref) , stmt(fn_ref.def().stmts.data()) , start_time(clock::now()) -, eval_tracked(tracked) +, precheck_tracked(tracked) { unsigned const nlocals = num_locals(); @@ -380,7 +380,7 @@ eval_t::eval_t(ir_t& ir_ref, fn_t const& fn_ref) var_types[i] = ::dethunkify(fn->def().local_vars[i].src_type, true, this); // Add global vars to 'var_types': - var_types.reserve(var_types.size() + ir->gmanager.num_gvar_locators()); + var_types.reserve(num_vars()); ir->gmanager.for_each_gvar([&](gvar_ht gvar, auto) { var_types.push_back(gvar->type()); }); assert(num_vars() >= var_types.size()); var_types.resize(num_vars(), TYPE_VOID); @@ -1107,8 +1107,8 @@ token_t const* eval_t::do_token(rpn_stack_t& rpn_stack, token_t const* token) case GLOBAL_VAR: if(D == COMPILE || D == CHECK) { - if(eval_tracked) - eval_tracked->gvars_used.emplace(global->handle(), token->pstring); + if(precheck_tracked) + precheck_tracked->gvars_used.emplace(global->handle(), token->pstring); rpn_value_t new_top = { @@ -1355,17 +1355,17 @@ token_t const* eval_t::do_token(rpn_stack_t& rpn_stack, token_t const* token) if(call->fclass == FN_MODE && is_interpret(D)) compiler_error(call_pstring, "Cannot goto mode at compile-time."); - if(eval_tracked) + if(precheck_tracked) { assert(fn); if(call->fclass == FN_MODE) { // Track that we're going to a mode here: - eval_tracked->goto_modes.push_back(std::make_pair( + precheck_tracked->goto_modes.push_back(std::make_pair( call, stmt_ht{ stmt - fn->def().stmts.data() })); } else - eval_tracked->calls.emplace(call, call_pstring); + precheck_tracked->calls.emplace(call, call_pstring); } // Now do the call! @@ -1854,12 +1854,13 @@ token_t const* eval_t::do_token(rpn_stack_t& rpn_stack, token_t const* token) "Expecting array or pointer type. Got %.", array_val.type)); } - if(is_ptr && eval_tracked) + if(is_ptr && precheck_tracked) { // TODO: Update this when inlining is added. unsigned const size = array_val.type.group_tail_size(); + precheck_tracked->deref_types.insert(array_val.type); for(unsigned i = 0; i < size; ++i) - eval_tracked->deref_groups.emplace(array_val.type.group(i), src_type_t{ array_val.pstring, array_val.type }); + precheck_tracked->deref_groups.emplace(array_val.type.group(i), src_type_t{ array_val.pstring, array_val.type }); } rpn_value_t& array_index = rpn_stack.peek(0); @@ -1896,8 +1897,13 @@ token_t const* eval_t::do_token(rpn_stack_t& rpn_stack, token_t const* token) bool const is_banked = array_val.type.name() == TYPE_BANKED_PTR; assert(array_val.sval.size() == is_banked ? 2 : 1); + ssa_value_t prev_in_order = {}; + if(auto ptr_i = ir->gmanager.ptr_i(array_val.type)) + prev_in_order = var_lookup(builder.cfg, to_var_i(ptr_i), 0); + ssa_ht const h = builder.cfg->emplace_ssa( SSA_read_ptr, TYPE_U, + prev_in_order, from_variant(array_val.sval[0], array_val.type), ssa_value_t(), is_banked ? from_variant(array_val.sval[1], array_val.type) : ssa_value_t(), @@ -1923,6 +1929,7 @@ token_t const* eval_t::do_token(rpn_stack_t& rpn_stack, token_t const* token) if(is_ptr) { + array_val.derefed_from = array_val.type; array_val.type = TYPE_U; array_val.category = is_mptr ? LVAL_PTR : RVAL; } @@ -2256,23 +2263,29 @@ void eval_t::do_assign(rpn_stack_t& rpn_stack, token_t const& token) if(assignee.category == LVAL_PTR) { - assert(assignee.index); - if(D == INTERPRET) { + assert(assignee.index); assert(false); // TODO: implement } else if(D == COMPILE) { + assert(assignee.index); + throwing_cast(assignment, TYPE_U, true); ssa_ht const read = assignee.ssa(0).handle(); assert(read->op() == SSA_read_ptr); + ssa_value_t prev_in_order = {}; + if(auto ptr_i = ir->gmanager.ptr_i(assignee.derefed_from)) + prev_in_order = var_lookup(builder.cfg, to_var_i(ptr_i), 0); + ssa_ht const write = builder.cfg->emplace_ssa( SSA_write_ptr, TYPE_VOID, - read->input(0), read->input(1), - read->input(2), read->input(3), + prev_in_order, + read->input(1), read->input(2), + read->input(3), read->input(4), std::get(assignment.sval[0])); write->append_daisy(); @@ -2362,9 +2375,11 @@ void eval_t::do_assign(rpn_stack_t& rpn_stack, token_t const& token) type_t const type = type_t::tea(member_type(assignment.type, i), mt.size(), assignment.pstring); assert(type.name() == TYPE_TEA); + ssa_value_t const prev_array = var_lookup(builder.cfg, assignee.var_i, i); + ssa_ht write = builder.cfg->emplace_ssa( SSA_write_array, type, - read->input(0), locator_t(), read->input(2), std::get(assignment.sval[i])); + prev_array, locator_t(), read->input(2), std::get(assignment.sval[i])); local[i + assignee.member] = write; } @@ -3118,7 +3133,7 @@ cfg_ht eval_t::insert_cfg(bool seal, pstring_t label_name) assert(num_vars() == var_types.size()); vec.resize(var_types.size()); for(unsigned i = 0; i < var_types.size(); ++i) - vec[i].resize(::num_members(var_types[i])); + vec[i].resize(std::max(1, ::num_members(var_types[i]))); }; init_vector(block_data.vars); diff --git a/src/eval.hpp b/src/eval.hpp index ad9d1670..7cdc9089 100644 --- a/src/eval.hpp +++ b/src/eval.hpp @@ -52,7 +52,7 @@ spair_t interpret_expr(pstring_t pstring, token_t const* expr, std::vector interpret_paa(pstring_t pstring, token_t const* expr); -eval_tracked_t build_tracked(fn_t const& fn); +precheck_tracked_t build_tracked(fn_t const& fn); void build_ir(ir_t& ir, fn_t const& fn); diff --git a/src/file.cpp b/src/file.cpp index b8547672..db1d699f 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -28,7 +28,7 @@ void file_contents_t::reset(unsigned file_i) if(read(fd, reinterpret_cast(m_source.get()), sb.st_size) == -1) throw std::runtime_error("Unable to read file: " + name); - m_source[sb.st_size] = m_source[sb.st_size - 1] = '\0'; + m_source[sb.st_size] = m_source[sb.st_size + 1] = '\0'; m_size = sb.st_size + 2; m_file_i = file_i; } diff --git a/src/globals.cpp b/src/globals.cpp index 4dc11428..1db55245 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -493,7 +493,7 @@ void fn_t::calc_ir_bitsets(ir_t const& ir) bool io_pure = true; // Handle preserved groups - for(auto const& fn_stmt : m_eval_tracked->goto_modes) + for(auto const& fn_stmt : m_precheck_tracked->goto_modes) { if(mods_t const* goto_mods = def().mods_of(fn_stmt.second)) { @@ -575,9 +575,11 @@ void fn_t::calc_ir_bitsets(ir_t const& ir) if(ssa_flags(ssa_it->op()) & SSAF_INDEXES_PTR) { + using namespace ssai::rw_ptr; + io_pure = false; - type_t const ptr_type = ssa_it->input(0).type(); + type_t const ptr_type = ssa_it->input(PTR).type(); assert(is_ptr(ptr_type.name())); unsigned const size = ptr_type.group_tail_size(); @@ -801,13 +803,13 @@ void fn_t::precheck_verify() const void fn_t::precheck_eval() { assert(compiler_phase() == PHASE_PRECHECK); - assert(!m_eval_tracked); + assert(!m_precheck_tracked); - // Run the evaluator to generate 'm_eval_tracked': - m_eval_tracked.reset(new eval_tracked_t(build_tracked(*this))); + // Run the evaluator to generate 'm_precheck_tracked': + m_precheck_tracked.reset(new precheck_tracked_t(build_tracked(*this))); std::cout << "PRECHECK\n"; - for(auto const& pair : m_eval_tracked->gvars_used) + for(auto const& pair : m_precheck_tracked->gvars_used) std::cout << "GVAR USED " << pair.first->global.name << std::endl; } @@ -851,7 +853,7 @@ void fn_t::precheck_propagate() }; // Handle accesses through pointers: - for(auto const& pair : m_eval_tracked->deref_groups) + for(auto const& pair : m_precheck_tracked->deref_groups) { auto const error = [&](group_class_t gclass, auto& groups) { @@ -891,7 +893,7 @@ void fn_t::precheck_propagate() } // Handle accesses through goto modes: - for(auto const& fn_stmt : m_eval_tracked->goto_modes) + for(auto const& fn_stmt : m_precheck_tracked->goto_modes) { stmt_t const& stmt = def()[fn_stmt.second]; mods_t const* goto_mods = def().mods_of(fn_stmt.second); @@ -961,7 +963,7 @@ void fn_t::precheck_propagate() compiler_error(global.pstring(), msg); }; - for(auto const& pair : m_eval_tracked->gvars_used) + for(auto const& pair : m_precheck_tracked->gvars_used) { group_ht const group = pair.first->group(); @@ -971,7 +973,7 @@ void fn_t::precheck_propagate() m_precheck_group_vars.set(group->impl_id()); } - for(auto const& pair : m_eval_tracked->calls) + for(auto const& pair : m_precheck_tracked->calls) { fn_t& call = *pair.first; diff --git a/src/globals.hpp b/src/globals.hpp index 96f7132e..4c8e4b7f 100644 --- a/src/globals.hpp +++ b/src/globals.hpp @@ -29,7 +29,7 @@ #include "mods.hpp" struct rom_array_t; -struct eval_tracked_t; +struct precheck_tracked_t; namespace bc = boost::container; @@ -245,8 +245,6 @@ class struct_t bool m_has_tea_member = false; }; -using deref_groups_t = fc::vector_map; - struct fn_impl_base_t { virtual ~fn_impl_base_t() {} @@ -263,9 +261,10 @@ struct mode_impl_t : public fn_impl_base_t fc::small_map incoming_preserved_groups; }; -struct eval_tracked_t +struct precheck_tracked_t { fc::vector_map deref_groups; + rh::batman_set deref_types; std::vector> goto_modes; fc::vector_map calls; fc::vector_map gvars_used; @@ -295,7 +294,7 @@ class fn_t void compile(); - eval_tracked_t const& precheck_tracked() const { assert(m_eval_tracked); return *m_eval_tracked; } + precheck_tracked_t const& precheck_tracked() const { assert(m_precheck_tracked); return *m_precheck_tracked; } bitset_t const& precheck_group_vars() const { assert(m_precheck_group_vars); return m_precheck_group_vars; } /* bitset_t const& lang_preserves_group_vars() const @@ -347,7 +346,7 @@ class fn_t std::unique_ptr m_pimpl; // TODO - std::unique_ptr m_eval_tracked; + std::unique_ptr m_precheck_tracked; bitset_t m_precheck_group_vars; // 'lang_gvars' is calculated shortly after parsing. diff --git a/src/gmanager.cpp b/src/gmanager.cpp index 446e3d95..22f7e232 100644 --- a/src/gmanager.cpp +++ b/src/gmanager.cpp @@ -10,145 +10,206 @@ std::size_t gmanager_t::bitset_size() { return gmember_ht::bitset_size(); } void gmanager_t::init(fn_ht fn) { - assert(!this->fn); - this->fn = fn; + ////////////// + // GMEMBERS // + ////////////// - unsigned const set_size = bitset_size(); - - // 'named_set' will hold all the members of gvars mentioned by name - // inside this fn's code, ignoring what happens in other functions. - // NOTE: 'named_set' doesn't track preserved groups from 'goto mode's. - bitset_uint_t* const named_set = bitset_pool.alloc(set_size); - assert(bitset_all_clear(set_size, named_set)); - - // TODO: handle inlining - for(auto const& pair : fn->precheck_tracked().gvars_used) { - gvar_ht gvar = pair.first; - gvar_set.container.push_back(gvar); - bitset_set_n(set_size, named_set, gvar->begin().id, gvar->num_members()); - } + assert(!this->fn); + this->fn = fn; - // Start out with a single equivalence class, containing all the - // globals possibly used in this fn (including fn calls). - // Then, this equivalence class will be broken up into multiple - // disjoint sublocs. + unsigned const set_size = bitset_size(); - bitset_uint_t* const initial_set = bitset_pool.alloc(set_size); - assert(bitset_all_clear(set_size, initial_set)); + // 'named_set' will hold all the members of gvars mentioned by name + // inside this fn's code, ignoring what happens in other functions. + // NOTE: 'named_set' doesn't track preserved groups from 'goto mode's. + bitset_uint_t* const named_set = bitset_pool.alloc(set_size); + assert(bitset_all_clear(set_size, named_set)); - for(auto const& pair : fn->precheck_tracked().calls) - { - fn_t const& call = *pair.first; + // TODO: handle inlining + for(auto const& pair : fn->precheck_tracked().gvars_used) + { + gvar_ht gvar = pair.first; + gvar_set.container.push_back(gvar); + bitset_set_n(set_size, named_set, gvar->begin().id, gvar->num_members()); + } - if(call.fclass == FN_CT) - continue; - assert(call.fclass != FN_MODE); + // Start out with a single equivalence class, containing all the + // globals possibly used in this fn (including fn calls). + // Then, this equivalence class will be broken up into multiple + // disjoint sublocs. - assert(set_size == call.ir_reads().size()); - bitset_or(set_size, initial_set, call.ir_reads().data()); - bitset_or(set_size, initial_set, call.ir_writes().data()); - } + bitset_uint_t* const initial_set = bitset_pool.alloc(set_size); + assert(bitset_all_clear(set_size, initial_set)); - for(auto const& pair : fn->precheck_tracked().goto_modes) - { - mods_t const* mods = fn->def().mods_of(pair.second); + for(auto const& pair : fn->precheck_tracked().calls) + { + fn_t const& call = *pair.first; + + if(call.fclass == FN_CT) + continue; + assert(call.fclass != FN_MODE); - if(!mods) - continue; + assert(set_size == call.ir_reads().size()); + bitset_or(set_size, initial_set, call.ir_reads().data()); + bitset_or(set_size, initial_set, call.ir_writes().data()); + } - mods->for_each_group_vars([&](group_vars_ht gv) + for(auto const& pair : fn->precheck_tracked().goto_modes) { - bitset_or(set_size, initial_set, gv->gmembers().data()); - }); - } + mods_t const* mods = fn->def().mods_of(pair.second); - // Needed as we used 'push_back' to generate the set: - std::sort(gvar_set.container.begin(), gvar_set.container.end()); + if(!mods) + continue; - // The eq classes won't involve any global named in the fn. - bitset_difference(set_size, initial_set, named_set); + mods->for_each_group_vars([&](group_vars_ht gv) + { + bitset_or(set_size, initial_set, gv->gmembers().data()); + }); + } - if(bitset_all_clear(set_size, initial_set)) - return; + // Needed as we used 'push_back' to generate the set: + std::sort(gvar_set.container.begin(), gvar_set.container.end()); - // Now break equivalent classes apart: - std::vector eq_classes = { initial_set }; - std::vector new_eq_classes; + // The eq classes won't involve any global named in the fn. + bitset_difference(set_size, initial_set, named_set); - auto const split = [&](bitset_t const& a, bitset_t const& b) - { - bitset_uint_t any_in = 0; - bitset_uint_t any_comp = 0; - bitset_uint_t* comp_set = bitset_pool.alloc(set_size); + if(bitset_all_clear(set_size, initial_set)) + return; + + // Now break equivalent classes apart: + std::vector eq_classes = { initial_set }; + std::vector new_eq_classes; - for(bitset_uint_t* in_set : eq_classes) + auto const split = [&](unsigned bs_size, bitset_uint_t const* a, bitset_uint_t const* b) { - // Split the eq class set into two locs: - // - One which has the intersection of 'in_set' and the idep's reads and writes - // - The complement of that ('comp') + bitset_uint_t any_in = 0; + bitset_uint_t any_comp = 0; + bitset_uint_t* comp_set = bitset_pool.alloc(bs_size); - for(unsigned i = 0; i < set_size; ++i) + for(bitset_uint_t* in_set : eq_classes) { - bitset_uint_t rw = a[i] | b[i]; - any_comp |= (comp_set[i] = in_set[i] & ~rw); - any_in |= (in_set[i] &= rw); + // Split the eq class set into two locs: + // - One which has the intersection of 'in_set' and the idep's reads and writes + // - The complement of that ('comp') + + for(unsigned i = 0; i < bs_size; ++i) + { + bitset_uint_t rw = a[i] | b[i]; + any_comp |= (comp_set[i] = in_set[i] & ~rw); + any_in |= (in_set[i] &= rw); + } + + assert(any_in || any_comp); + + if(any_in) + new_eq_classes.push_back(in_set); + if(any_comp) + { + new_eq_classes.push_back(comp_set); + comp_set = any_in ? bitset_pool.alloc(bs_size) : in_set; + } } - assert(any_in || any_comp); + eq_classes.swap(new_eq_classes); + new_eq_classes.clear(); + }; + + // Split calls. + for(auto const& pair : fn->precheck_tracked().calls) + { + fn_t const& call = *pair.first; + + if(call.fclass == FN_CT) + continue; + assert(call.fclass != FN_MODE); - if(any_in) - new_eq_classes.push_back(in_set); - if(any_comp) + split(set_size, call.ir_reads().data(), call.ir_writes().data()); + } + + // Split goto modes. + for(auto const& pair : fn->precheck_tracked().goto_modes) + { + mods_t const* mods = fn->def().mods_of(pair.second); + + if(!mods) + continue; + + mods->for_each_group_vars([&](group_vars_ht gv) { - new_eq_classes.push_back(comp_set); - comp_set = any_in ? bitset_pool.alloc(set_size) : in_set; - } + split(set_size, gv->gmembers().data(), gv->gmembers().data()); + }); } - eq_classes.swap(new_eq_classes); - new_eq_classes.clear(); - }; + assert(eq_classes.size() >= 1); - // Split calls. - for(auto const& pair : fn->precheck_tracked().calls) - { - fn_t const& call = *pair.first; + // OK! The equivalence classes are built. + // Now associate each variable with its eq class. - if(call.fclass == FN_CT) - continue; - assert(call.fclass != FN_MODE); + gmember_sets.reserve(gmember_sets.size() + eq_classes.size()); + for(unsigned i = 0; i < eq_classes.size(); ++i) + { + bitset_for_each(set_size, eq_classes[i], [this](unsigned bit) + { + gmember_sets_map.insert({ gmember_ht{ bit }, gmember_sets.size() }); + }); + gmember_sets.push_back(eq_classes[i]); + } - split(call.ir_reads(), call.ir_writes()); } - // Split goto modes. - for(auto const& pair : fn->precheck_tracked().goto_modes) + ////////////////// + // DEREF GROUPS // + ////////////////// + { - mods_t const* mods = fn->def().mods_of(pair.second); + rh::batman_map union_map; - if(!mods) - continue; + auto const get_head = [&](group_vars_ht a) -> group_vars_ht + { + while(group_vars_ht next = union_map[a]) + a = next; + return a; + }; - mods->for_each_group_vars([&](group_vars_ht gv) + auto const share = [&](group_vars_ht head_a, group_vars_ht b) { - split(gv->gmembers(), gv->gmembers()); - }); - } + assert(head_a == get_head(head_a)); + group_vars_ht head_b = get_head(b); - assert(eq_classes.size() >= 1); + if(head_a == head_b) + return; - // OK! The equivalence classes are built. - // Now associate each variable with its eq class. + union_map[head_b] = head_a; + assert(get_head(b) == head_a); + }; - gmember_sets.reserve(gmember_sets.size() + eq_classes.size()); - for(unsigned i = 0; i < eq_classes.size(); ++i) - { - bitset_for_each(set_size, eq_classes[i], [this](unsigned bit) + for(type_t const& type : fn->precheck_tracked().deref_types) + { + group_vars_ht prev_head = {}; + + unsigned const size = type.group_tail_size(); + for(unsigned i = 0; i < size; ++i) + { + if(type.group(i)->gclass() != GROUP_VARS) + continue; + group_vars_ht const h = type.group(i)->handle(); + + if(prev_head) + share(prev_head, h); + else + prev_head = get_head(h); + } + } + + rh::robin_map index_map; + unsigned const offset = num_locators(); + + for(auto const& pair : union_map) { - gmember_sets_map.insert({ gmember_ht{ bit }, gmember_sets.size() }); - }); - gmember_sets.push_back(eq_classes[i]); + auto result = index_map.insert({ get_head(pair.first), index_map.size() }); + group_vars_map.insert({ pair.first, index_t{ result.first->second + offset }}); + } } } @@ -170,6 +231,22 @@ auto gmanager_t::var_i(gmember_ht gmember) const -> index_t return {}; } +auto gmanager_t::ptr_i(group_vars_ht group_vars) const -> index_t +{ + if(auto const* pair = group_vars_map.lookup(group_vars)) + return pair->second; + return {}; +} + +auto gmanager_t::ptr_i(type_t const& type) const -> index_t +{ + unsigned const size = type.group_tail_size(); + for(unsigned i = 0; i < size; ++i) + if(type.group(i)->gclass() == GROUP_VARS) + return ptr_i(type.group(i)->handle()); + return {}; +} + /* TODO locator_t gmanager_t::locator(gmember_ht gmember) const { diff --git a/src/gmanager.hpp b/src/gmanager.hpp index 84307a32..2764f4e2 100644 --- a/src/gmanager.hpp +++ b/src/gmanager.hpp @@ -26,10 +26,13 @@ class gmanager_t index_t var_i(gvar_ht gvar) const; index_t var_i(gmember_ht gmember) const; + index_t ptr_i(group_vars_ht group_vars) const; + index_t ptr_i(type_t const& type) const; std::size_t num_gvar_locators() const { return gvar_set.size(); } std::size_t num_gmember_set_locators() const { return gmember_sets.size(); } std::size_t num_locators() const { return num_gvar_locators() + num_gmember_set_locators(); } + std::size_t num_slots() const { return num_locators() + group_vars_map.size(); } // Returns a bitset containing every gmember belonging to 'loc'. bitset_uint_t const* get_set(locator_t loc) const @@ -76,6 +79,7 @@ class gmanager_t fc::small_set gvar_set; rh::batman_map gmember_sets_map; std::vector gmember_sets; + rh::batman_map group_vars_map; std::vector m_types; array_pool_t bitset_pool; //unsigned first_set = 0; TODO diff --git a/src/locator.cpp b/src/locator.cpp index cd167ba7..8ebe58e3 100644 --- a/src/locator.cpp +++ b/src/locator.cpp @@ -49,7 +49,7 @@ std::string to_string(locator_t loc) case LOC_ROM_ARRAY: str = "rom_array"; break; case LOC_LT_GMEMBER_PTR: - str = fmt("gmember ptr % %", loc.gmember()->gvar.global.name, loc.gmember()->member()); break; + str = fmt("gmember_ptr % %", loc.gmember()->gvar.global.name, loc.gmember()->member()); break; case LOC_LT_CONST_PTR: str = fmt("lt const ptr %", loc.const_()->global.name); break; case LOC_LT_EXPR: diff --git a/src/locator.hpp b/src/locator.hpp index fa42e0a6..21efc15a 100644 --- a/src/locator.hpp +++ b/src/locator.hpp @@ -441,6 +441,7 @@ friend class gmember_locator_manager_t; locator_t link(fn_ht fn = {}, int bank = -1) const; + private: std::uint64_t impl = 0; }; @@ -482,4 +483,5 @@ struct std::hash } }; + #endif diff --git a/src/main.cpp b/src/main.cpp index fa1a6939..5d6fac30 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ #include "static_addr.hpp" #include "rom_link.hpp" #include "ram_init.hpp" +#include "eternal_new.hpp" // TODO: remove? #include "eval.hpp" // TODO: remove? @@ -128,10 +129,14 @@ int main(int argc, char** argv) if(file_i >= source_file_names.size()) return; + std::cout << "PARSING " << file_i << std::endl; + file_contents_t file(file_i); parse(file); + std::cout << "DONE PARSING " << file_i << std::endl; } }); + std::cout << "MERGE" << std::endl; // Fix various things after parsing: set_compiler_phase(PHASE_PARSE_CLEANUP); diff --git a/src/o_unused.cpp b/src/o_unused.cpp index fde600ab..9ab6ba23 100644 --- a/src/o_unused.cpp +++ b/src/o_unused.cpp @@ -11,8 +11,6 @@ namespace bc = ::boost::container; static bool _can_prune(ssa_node_t& node) { - SSA_VERSION(1); - // Linked nodes will all be pruned at once. // The pruning only happens at the root of the links, // so skip any non-roots: diff --git a/src/parser.cpp b/src/parser.cpp index 17c10af6..cffa4e26 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -305,7 +305,7 @@ bool parser_t

::parse_token() token_type_t lexed = TOK_START; while(lexed > TOK_LAST_STATE) { -#if 0 // Enable to debug +#if 1 // Enable to debug assert(next_char < source() + file.size()); assert(next_char >= source()); #endif diff --git a/src/robin/collection.hpp b/src/robin/collection.hpp index 191338d1..5deaab3b 100644 --- a/src/robin/collection.hpp +++ b/src/robin/collection.hpp @@ -109,9 +109,6 @@ class robin_collection bool empty() const { return size() == 0; } void reserve(hash_type size) { table.reserve(size); } - template - void reduce(Eq const& equals) { table.reduce(equals); } - private: robin_collection const* const_this() const { return this; } @@ -234,16 +231,6 @@ class batman_collection void reserve(hash_type size) { table.reserve(size); } - template - void reduce(Eq const& equals) - { - table.reduce([&](index_type l, index_type r) - { - return equals(static_cast(m_data[l]), - static_cast(m_data[r])); - }); - } - const_iterator cbegin() const noexcept { return data(); } const_iterator begin() const noexcept { return data(); } iterator begin() noexcept { return data(); } @@ -489,15 +476,6 @@ class joker_collection bool empty() const { return size() == 0; } void reserve(hash_type size) { collection.reserve(size); } - template - void reduce(Eq const& equals) - { - collection.reduce([&](unique_ptr_type const& l, unique_ptr_type const& r) - { - return equals(*l, *r); - }); - } - const_iterator cbegin() const noexcept { return const_iterator(collection.cbegin()); } const_iterator begin() const noexcept diff --git a/src/robin/map.hpp b/src/robin/map.hpp index 41ccdd0d..f003aece 100644 --- a/src/robin/map.hpp +++ b/src/robin/map.hpp @@ -125,14 +125,6 @@ class robin_map bool empty() const { return collection.empty(); } void reserve(hash_type size) { collection.reserve(size); } - template - void reduce(Eq const& equals) - { - collection.reduce([&](value_type const& l, value_type const& r) - { - return equals(l.first, r.first); - }); - } private: collection_type collection; }; @@ -217,15 +209,6 @@ class batman_map bool empty() const { return collection.empty(); } void reserve(hash_type size) { collection.reserve(size); } - template - void reduce(Eq const& equals) - { - collection.reduce([&](value_type const& l, value_type const& r) - { - return equals(l.first, r.first); - }); - } - const_iterator cbegin() const { return collection.cbegin(); } const_iterator begin() const { return collection.cbegin(); } iterator begin() { return collection.begin(); } @@ -316,15 +299,6 @@ class joker_map bool empty() const { return collection.empty(); } void reserve(hash_type size) { collection.reserve(size); } - template - void reduce(Eq const& equals) - { - collection.reduce([&](value_type const& l, value_type const& r) - { - return equals(l.first, r.first); - }); - } - const_iterator cbegin() const { return collection.cbegin(); } const_iterator begin() const { return collection.cbegin(); } iterator begin() { return collection.begin(); } diff --git a/src/robin/set.hpp b/src/robin/set.hpp index 655864a6..a17fd309 100644 --- a/src/robin/set.hpp +++ b/src/robin/set.hpp @@ -80,8 +80,6 @@ class robin_set bool empty() const { return collection.empty(); } void reserve(hash_type size) { collection.reserve(size); } - template - void reduce(Eq const& equals) { collection.reduce(equals); } private: collection_type collection; }; @@ -138,9 +136,6 @@ class batman_set bool empty() const { return collection.empty(); } void reserve(hash_type size) { collection.reserve(size); } - template - void reduce(Eq const& equals) { collection.reduce(equals); } - const_iterator cbegin() const { return collection.cbegin(); } const_iterator begin() const { return collection.cbegin(); } iterator begin() { return collection.begin(); } @@ -203,9 +198,6 @@ class joker_set bool empty() const { return collection.empty(); } void reserve(hash_type size) { collection.reserve(size); } - template - void reduce(Eq const& equals) { collection.reduce(equals); } - const_iterator cbegin() const { return collection.cbegin(); } const_iterator begin() const { return collection.cbegin(); } iterator begin() { return collection.begin(); } diff --git a/src/robin/table.hpp b/src/robin/table.hpp index 5ccdd440..201b9717 100644 --- a/src/robin/table.hpp +++ b/src/robin/table.hpp @@ -396,27 +396,6 @@ class robin_table realloc(new_size); } - // Lets you redefine a table using a new 'equals'. - template - void reduce(Eq const& equals) - { - std::size_t const size = allocated_size(); - robin_table new_table(size, mask_); - for(hash_type i = 0; i != size; ++i) - { - if(hash_data()[i] == 0) - continue; - hash_type hash = i - ((hash_data()[i] & mask_) - 1); - hash |= (hash_data()[i] & ~mask_); - value_type& data = value_data()[i]; - new_table.emplace(hash, - [&](auto const& a) -> bool { return equals(a, data); }, - [&]() -> value_type&& { return std::move(data); }); - hash_data()[i] = 0; - } - *this = std::move(new_table); - } - hash_type const* hash_data() const { return hashes.get(); } hash_type* hash_data() { return hashes.get(); } @@ -606,9 +585,6 @@ class robin_auto_table rehash_size = calc_rehash_size(); } - template - void reduce(Eq const& equals) { return table.reduce(equals); } - std::size_t size() const { return used_size; } private: hash_type calc_rehash_size() const diff --git a/src/rpn.hpp b/src/rpn.hpp index 27eda94c..c3c8033c 100644 --- a/src/rpn.hpp +++ b/src/rpn.hpp @@ -47,6 +47,7 @@ struct rpn_value_t value_category_t category = RVAL; value_time_t time = CT; // When the value is computed type_t type = TYPE_VOID; + type_t derefed_from = TYPE_VOID; pstring_t pstring = {}; unsigned var_i = ~0u; diff --git a/src/ssa_op.hpp b/src/ssa_op.hpp index 6f5ca0f6..9c3a3d0e 100644 --- a/src/ssa_op.hpp +++ b/src/ssa_op.hpp @@ -6,10 +6,6 @@ #include #include -#define SSA_VERSION_NUMBER 1 -#define SSA_VERSION(V) \ - static_assert(SSA_VERSION_NUMBER == (V), "SSA version mismatch.") - // SSA flags: constexpr unsigned SSAF_TRACE_INPUTS = 1 << 0; constexpr unsigned SSAF_COPY = 1 << 1; @@ -26,6 +22,20 @@ constexpr unsigned SSAF_COMMUTATIVE = 1 << 11; // First two args can be swapp constexpr unsigned SSAF_BRANCHY_CG = 1 << 12; // Potentially uses a conditional in code gen constexpr unsigned SSAF_NULL_INPUT_VALID = 1 << 13; // Can use nulls as input +// Parameter indexes for SSA ops +namespace ssai +{ + namespace rw_ptr + { + constexpr unsigned ORDER_AFTER = 0; + constexpr unsigned PTR = 1; + constexpr unsigned PTR_HI = 2; + constexpr unsigned BANK = 3; + constexpr unsigned INDEX = 4; + constexpr unsigned ASSIGNMENT = 5; + } +} + enum input_class_t { INPUT_NONE, @@ -94,7 +104,6 @@ constexpr bool ssa_indexes(ssa_op_t op) inline unsigned write_globals_begin(ssa_op_t op) { - SSA_VERSION(1); assert(ssa_flags(op) & SSAF_WRITE_GLOBALS); switch(op) { diff --git a/src/ssa_op.inc b/src/ssa_op.inc index e0c64078..7a68830a 100644 --- a/src/ssa_op.inc +++ b/src/ssa_op.inc @@ -111,12 +111,12 @@ SSA_DEF(make_ptr_lo, 2, INPUT_NONE, SSAF_COPY) // (lo, hi) - combines two bytes into a pointer SSA_DEF(make_ptr_hi, 2, INPUT_NONE, SSAF_COPY) -// (ptr, ptr_hi, bank, index) +// (order_after, ptr, ptr_hi, bank, index) // TODO: should this be impure, because of bankswitches? -SSA_DEF(read_ptr, 4, INPUT_VALUE, SSAF_INDEXES_PTR | SSAF_NULL_INPUT_VALID | SSAF_NULL_INPUT_VALID) +SSA_DEF(read_ptr, 5, INPUT_NONE, SSAF_INDEXES_PTR | SSAF_NULL_INPUT_VALID | SSAF_NULL_INPUT_VALID) -// (ptr, ptr_hi, bank, index, value) -SSA_DEF(write_ptr, 5, INPUT_VALUE, SSAF_IO_IMPURE | SSAF_INDEXES_PTR | SSAF_NULL_INPUT_VALID) +// (order_after, ptr, ptr_hi, bank, index, value) +SSA_DEF(write_ptr, 6, INPUT_NONE, SSAF_IO_IMPURE | SSAF_INDEXES_PTR | SSAF_NULL_INPUT_VALID) ////////////// // HARDWARE // diff --git a/src/type.cpp b/src/type.cpp index 6a54c7d9..a4c8de75 100644 --- a/src/type.cpp +++ b/src/type.cpp @@ -35,7 +35,6 @@ namespace // Anonymous }; rh::robin_auto_table map; - array_pool_t tails; public: T const* get(T const* begin, T const* end) @@ -64,7 +63,7 @@ namespace // Anonymous }, [this, begin, end, size]() -> map_elem_t { - return { size, tails.insert(begin, end) }; + return { size, eternal_new(begin, end) }; }); assert(std::equal(begin, end, result.first->tail)); @@ -442,13 +441,18 @@ bool is_ct(type_t type) unsigned num_members(type_t type) { assert(type.name() != TYPE_STRUCT_THUNK); + unsigned ret = 0; if(type.name() == TYPE_STRUCT) - return type.struct_().num_members(); + ret = type.struct_().num_members(); else if(is_tea(type.name())) - return num_members(type.elem_type()); + ret = num_members(type.elem_type()); else if(is_banked_ptr(type.name())) - return 2; // The bank is a member - return 1; + ret = 2; // The bank is a member + else + ret = 1; + + assert(ret > 0); + return ret; } unsigned num_atoms(type_t type, unsigned member)