compiler: make SymbolicStack to be struct

This commit is contained in:
ProgramSnail 2025-01-23 22:48:38 +03:00
parent 6d774ad42f
commit b5564c6731

View file

@ -562,8 +562,10 @@ template <typename U> struct AbstractSymbolicStack {
// val peek : 'a t -> 'a symbolic_location // val peek : 'a t -> 'a symbolic_location
// val peek2 : 'a t -> 'a symbolic_location * 'a symbolic_location // val peek2 : 'a t -> 'a symbolic_location * 'a symbolic_location
//
/* Last allocated position on symbolic stack */ /* Last allocated position on symbolic stack */
struct StackState { struct State {
struct S { struct S {
size_t n; size_t n;
}; };
@ -579,16 +581,13 @@ template <typename U> struct AbstractSymbolicStack {
const W &operator*() const { return val; } const W &operator*() const { return val; }
const W &operator->() const { return val; } const W &operator->() const { return val; }
}; };
using S = StackState::S; using S = State::S;
using R = StackState::R; using R = State::R;
using E = StackState::E; using E = State::E;
struct T { //
StackState state;
std::vector<U> registers;
};
struct SymbolicLocation { struct Location {
struct Stack { struct Stack {
int n; int n;
}; };
@ -608,122 +607,119 @@ template <typename U> struct AbstractSymbolicStack {
return std::holds_alternative<S>(val); return std::holds_alternative<S>(val);
} }
}; };
using Stack = SymbolicLocation::Stack; using Stack = Location::Stack;
using Register = SymbolicLocation::Register; using Register = Location::Register;
static AbstractSymbolicStack::T empty(std::vector<U> registers) { public:
return {{typename StackState::E{}}, std::move(registers)}; State state;
} std::vector<U> registers;
static T next(T v) { public:
StackState new_state = AbstractSymbolicStack(std::vector<U> registers)
std::visit(utils::multifunc{ : state(typename State::E{}), registers(std::move(registers)) {}
[](const S &x) -> StackState { return {S(x.n)}; },
[&v](const R &x) -> StackState { State next_state(const State &v) const {
if (x.n + 1 >= v.registers.size()) { return std::visit(utils::multifunc{
[](const S &x) -> State { return {S(x.n)}; },
[this](const R &x) -> State {
if (x.n + 1 >= registers.size()) {
return {S(0)}; return {S(0)};
} else { } else {
return {R(x.n + 1)}; return {R(x.n + 1)};
} }
}, },
[](const E &) -> StackState { return {R(0)}; }, [](const E &) -> State { return {R(0)}; },
}, },
*v.state); *v);
return {new_state, std::move(v.registers)};
} }
static T previous(T v) { State previous_state(const State &v) const {
StackState new_state = std::visit( return std::visit(utils::multifunc{
utils::multifunc{ [this](const S &x) -> State {
[&v](const S &x) -> StackState { return x.n == 0 ? State{R{registers.size() - 1}}
return x.n == 0 ? StackState{R{v.registers.size() - 1}} : State{S{x.n - 1}};
: StackState{S{x.n - 1}};
}, },
[](const R &x) -> StackState { [](const R &x) -> State {
return x.n == 0 ? StackState{E{}} : StackState{R{x.n - 1}}; return x.n == 0 ? State{E{}} : State{R{x.n - 1}};
}, },
[](const E &) -> StackState { [](const E &) -> State {
failure("Empty stack %s: %d", __FILE__, __LINE__); failure("Empty stack %s: %d", __FILE__, __LINE__);
utils::unreachable(); utils::unreachable();
}, },
}, },
*v.state); *v);
return {new_state, std::move(v.registers)};
} }
static SymbolicLocation location(const T &v) { Location location(const std::optional<State> &another_state = {}) const {
return std::visit( return std::visit(utils::multifunc{
utils::multifunc{ [](const S &x) -> Location { return {Stack(x.n)}; },
[](const S &x) -> SymbolicLocation { return {Stack(x.n)}; }, [this](const R &x) -> Location {
[&v](const R &x) -> SymbolicLocation { return {Register{registers[x.n]}};
return {Register{v.registers[x.n]}};
}, },
[](const E &) -> SymbolicLocation { [](const E &) -> Location {
failure("Empty stack %s: %d", __FILE__, __LINE__); failure("Empty stack %s: %d", __FILE__, __LINE__);
utils::unreachable(); utils::unreachable();
}, },
}, },
*v.state); another_state ? **another_state : *state);
} }
static bool is_empty(const T &v) { bool is_empty() const {
return std::holds_alternative<typename StackState::E>(*v.state); return std::holds_alternative<typename State::E>(*state);
} }
// BETTER: replace with range // BETTER: replace with range
static std::vector<U> live_registers(const T &v) { std::vector<U> live_registers() const {
return std::visit( // return std::visit( //
utils::multifunc{ utils::multifunc{
[&v](const S &) { return v.registers; }, [this](const S &) { return registers; },
[&v](const R &x) { [this](const R &x) {
std::vector<U> registers_prefix; std::vector<U> registers_prefix;
registers_prefix.insert(registers_prefix.end(), registers_prefix.insert(registers_prefix.end(), registers.begin(),
v.registers.begin(), registers.begin() + x.n + 1);
v.registers.begin() + x.n + 1);
// NOTE: same to (Array.sub registers 0 (n + 1)) // NOTE: same to (Array.sub registers 0 (n + 1))
return registers_prefix; return registers_prefix;
}, },
[](const E &) { return std::vector<U>{}; }, [](const E &) { return std::vector<U>{}; },
}, },
*v.state); *state);
} }
static size_t stack_size(const T &v) { size_t stack_size() const {
return std::visit(utils::multifunc{ return std::visit(utils::multifunc{
[](const S &x) { return x.n + 1; }, [](const S &x) { return x.n + 1; },
[](const auto &) -> size_t { return 0; }, [](const auto &) -> size_t { return 0; },
}, },
*v.state); *state);
} }
static std::pair<T, SymbolicLocation> allocate(T v) { Location allocate() {
// let state = next state in state = next_state(state);
auto loc = location(v); return location();
return {next(std::move(v)), loc};
} }
static std::pair<T, SymbolicLocation> pop(T v) { Location pop() {
auto loc = location(v); state = previous_state(state);
return {previous(std::move(v)), loc}; return location();
} }
static SymbolicLocation peek(const T &v) { return location(v); } Location peek() const { return location(); }
static std::pair<SymbolicLocation, SymbolicLocation> peek2(const T &v) { std::pair<Location, Location> peek2() const {
return {location(v), location(previous(v))}; return {location(), location(previous_state(state))};
} }
}; };
struct SymbolicStack { struct SymbolicStack {
using AbSS = AbstractSymbolicStack<Register::T>; using AbSS = AbstractSymbolicStack<Register::T>;
using S = AbSS::StackState::S; using S = AbSS::State::S;
using R = AbSS::StackState::R; using R = AbSS::State::R;
using E = AbSS::StackState::E; using E = AbSS::State::E;
using SymbolicLocation = AbSS::SymbolicLocation; using Location = AbSS::Location;
using Stack = SymbolicLocation::Stack; using Stack = Location::Stack;
using Register = SymbolicLocation::Register; using Register = Location::Register;
// type t // type t
@ -736,55 +732,45 @@ struct SymbolicStack {
// val peek : t -> opnd // val peek : t -> opnd
// val peek2 : t -> opnd * opnd // val peek2 : t -> opnd * opnd
struct T { public:
AbSS::T state; AbSS state;
size_t nlocals; size_t nlocals;
};
public:
/* To use free argument registers we have to rewrite function call /* To use free argument registers we have to rewrite function call
compilation. Otherwise we will result with the following code in compilation. Otherwise we will result with the following code in
arguments setup: movq %rcx, %rdx movq %rdx, %rsi */ arguments setup: movq %rcx, %rdx movq %rdx, %rsi */
static T empty(size_t nlocals) { SymbolicStack(size_t nlocals)
return { : state(AbSS(Registers::extra_caller_saved_registers)), nlocals(nlocals) {
.state = AbSS::empty(Registers::extra_caller_saved_registers),
.nlocals = nlocals,
};
} }
static Opnd opnd_from_loc(const T &v, const SymbolicLocation &loc) { Opnd opnd_from_loc(const Location &loc) const {
return std::visit( return std::visit(
utils::multifunc{ utils::multifunc{
[](const Register &x) -> Opnd { return {Opnd::R{x.r}}; }, [](const Register &x) -> Opnd { return {Opnd::R{x.r}}; },
[&v](const Stack &x) -> Opnd { return Opnd::S{x.n + v.nlocals}; }, [this](const Stack &x) -> Opnd { return Opnd::S{x.n + nlocals}; },
}, },
*loc); *loc);
} }
static bool is_empty(const T &v) { return AbSS::is_empty(v.state); }; bool is_empty() const { return state.is_empty(); };
static std::vector<Opnd> live_registers(const T &v) { std::vector<Opnd> live_registers() const {
return utils::transform<::Register::T, Opnd>( return utils::transform<::Register::T, Opnd>(
AbSS::live_registers(v.state), state.live_registers(), [](auto &&r) -> Opnd { return Opnd::R{r}; });
[](auto &&r) -> Opnd { return Opnd::R{r}; });
} }
static size_t stack_size(const T &v) { return AbSS::stack_size(v.state); } size_t stack_size() const { return state.stack_size(); }
static std::pair<T, Opnd> allocate(const T &v) { Opnd allocate() { return opnd_from_loc(state.allocate()); }
auto [state, loc] = AbSS::allocate(v.state);
return {{std::move(state), v.nlocals}, opnd_from_loc(v, loc)};
} // TODO: check
static std::pair<T, Opnd> pop(const T &v) { Opnd pop() { return opnd_from_loc(state.pop()); }
auto [state, loc] = AbSS::pop(v.state);
return {{std::move(state), v.nlocals}, opnd_from_loc(v, loc)};
}
static Opnd peek(const T &v) { return opnd_from_loc(v, AbSS::peek(v.state)); } Opnd peek() const { return opnd_from_loc(state.peek()); }
static std::pair<Opnd, Opnd> peek2(const T &v) { std::pair<Opnd, Opnd> peek2() const {
const auto [loc1, loc2] = AbSS::peek2(v.state); const auto [loc1, loc2] = state.peek2();
return {opnd_from_loc(v, loc1), opnd_from_loc(v, loc2)}; return {opnd_from_loc(loc1), opnd_from_loc(loc2)};
} }
}; };
@ -799,11 +785,10 @@ struct Mode {
OS target_os; OS target_os;
}; };
// TODO: rebuild in c++ way // TODO: remove unrequired parts
struct Env { struct Env {
private: private:
const std::string chars = const static std::string chars;
"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'";
// NOTE: is not required // NOTE: is not required
// const std::vector<Opnd> argument_registers = Registers.argument_registers // const std::vector<Opnd> argument_registers = Registers.argument_registers
@ -814,9 +799,9 @@ private:
size_t scount = 0; /* string count */ size_t scount = 0; /* string count */
size_t stack_slots = 0; /* maximal number of stack positions */ size_t stack_slots = 0; /* maximal number of stack positions */
size_t static_size = 0; /* static data size */ size_t static_size = 0; /* static data size */
SymbolicStack::T stack = SymbolicStack::empty(0); /* symbolic stack */ SymbolicStack stack = SymbolicStack(0); /* symbolic stack */
std::vector<std::vector<std::string>> locals; std::vector<std::vector<std::string>> locals;
MapS<SymbolicStack::T> stackmap; /* labels to stack map */ MapS<SymbolicStack> stackmap; /* labels to stack map */
bool barrier = false; /* barrier condition */ bool barrier = false; /* barrier condition */
SetS externs; SetS externs;
@ -833,7 +818,7 @@ public:
size_t nargs = 0; /* number of function arguments */ size_t nargs = 0; /* number of function arguments */
Mode mode; const Mode mode;
public: public:
Env(Mode mode) : mode(std::move(mode)) {} Env(Mode mode) : mode(std::move(mode)) {}
@ -850,13 +835,11 @@ public:
// TODO: add SymbolicStack functions to opimize // TODO: add SymbolicStack functions to opimize
std::string print_stack() const { std::string print_stack() const {
std::stringstream result; std::stringstream result;
SymbolicStack::T stack_copy = stack; SymbolicStack stack_copy = stack;
std::vector<std::string> elements; std::vector<std::string> elements;
while (!SymbolicStack::is_empty(stack_copy)) { while (!stack_copy.is_empty()) {
auto result = SymbolicStack::pop(std::move(stack_copy)); elements.push_back(to_string(stack_copy.pop()));
stack_copy = std::move(result.first);
elements.push_back(to_string(result.second));
} }
std::reverse(elements.begin(), elements.end()); std::reverse(elements.begin(), elements.end());
for (const auto &element : elements) { for (const auto &element : elements) {
@ -866,7 +849,7 @@ public:
} }
/* Assert empty stack */ /* Assert empty stack */
void assert_empty_stack() const { assert(SymbolicStack::is_empty(stack)); } void assert_empty_stack() const { assert(stack.is_empty()); }
/* check barrier condition */ /* check barrier condition */
bool is_barrier() { return barrier; } bool is_barrier() { return barrier; }
@ -878,13 +861,13 @@ public:
void drop_barrier() { barrier = false; } void drop_barrier() { barrier = false; }
/* drop stack */ /* drop stack */
void drop_stack() { stack = SymbolicStack::empty(static_size); } void drop_stack() { stack = SymbolicStack(static_size); }
/* associates a stack to a label */ /* associates a stack to a label */
void set_stack(std::string const &l) { stackmap.insert({l, stack}); } void set_stack(std::string const &l) { stackmap.insert({l, stack}); }
/* retrieves a stack for a label */ /* retrieves a stack for a label */
std::optional<SymbolicStack::T *> retrieve_stack(std::string const &l) { std::optional<SymbolicStack *> retrieve_stack(std::string const &l) {
auto it = stackmap.find(l); auto it = stackmap.find(l);
if (it != stackmap.end()) { if (it != stackmap.end()) {
return &it->second; return &it->second;
@ -929,26 +912,20 @@ public:
/* allocates a fresh position on a symbolic stack */ /* allocates a fresh position on a symbolic stack */
Opnd allocate() { Opnd allocate() {
auto [new_stack, opnd] = SymbolicStack::allocate(stack); auto opnd = stack.allocate();
stack = std::move(new_stack); stack_slots = std::max(stack_slots, (static_size + stack.stack_size()));
stack_slots =
std::max(stack_slots, (static_size + SymbolicStack::stack_size(stack)));
return opnd; return opnd;
} }
/* pops one operand from the symbolic stack */ /* pops one operand from the symbolic stack */
Opnd pop() { Opnd pop() { return stack.pop(); }
auto [new_stack, opnd] = SymbolicStack::pop(stack);
stack = std::move(new_stack);
return opnd;
}
/* is rdx register in use */ /* is rdx register in use */
bool rdx_in_use() const { return nargs > 2; } bool rdx_in_use() const { return nargs > 2; }
std::pair<std::vector<SymbolicStack::SymbolicLocation>, size_t> std::pair<std::vector<SymbolicStack::Location>, size_t>
arguments_locations(size_t n) { arguments_locations(size_t n) {
using SymbolicLocation = SymbolicStack::SymbolicLocation; using Location = SymbolicStack::Location;
using Register = SymbolicStack::Register; using Register = SymbolicStack::Register;
using Stack = SymbolicStack::Stack; using Stack = SymbolicStack::Stack;
@ -956,34 +933,31 @@ public:
std::vector<::Register::T> result; std::vector<::Register::T> result;
result.insert(result.end(), argument_registers.begin(), result.insert(result.end(), argument_registers.begin(),
argument_registers.begin() + n); argument_registers.begin() + n);
return { return {utils::transform<::Register::T, Location>(
utils::transform<::Register::T, SymbolicLocation>(
std::move(result), std::move(result),
[](const auto &r) -> SymbolicLocation { return {Register{r}}; }), [](const auto &r) -> Location { return {Register{r}}; }),
0}; 0};
} else { } else {
return {utils::concat( // return {utils::concat( //
utils::transform<::Register::T, SymbolicLocation>( utils::transform<::Register::T, Location>(
std::move(argument_registers), std::move(argument_registers),
[](const auto &r) -> SymbolicLocation { [](const auto &r) -> Location { return {Register{r}}; }),
return {Register{r}}; std::vector<Location>(
}),
std::vector<SymbolicLocation>(
n - Registers::argument_registers.size(), {Stack{}})), n - Registers::argument_registers.size(), {Stack{}})),
n - Registers::argument_registers.size()}; n - Registers::argument_registers.size()};
} }
} }
/* peeks the top of the stack (the stack does not change) */ /* peeks the top of the stack (the stack does not change) */
Opnd peek() const { return SymbolicStack::peek(stack); } Opnd peek() const { return stack.peek(); }
/* peeks two topmost values from the stack (the stack itself does not /* peeks two topmost values from the stack (the stack itself does not
* change) * change)
*/ */
std::pair<Opnd, Opnd> peek2() const { return ::SymbolicStack::peek2(stack); } std::pair<Opnd, Opnd> peek2() const { return stack.peek2(); }
/* tag hash: gets a hash for a string tag */ /* tag hash: gets a hash for a string tag */
uint64_t hash(const std::string &tag) { static uint64_t hash(const std::string &tag) {
assert(!tag.empty()); assert(!tag.empty());
uint64_t h = 0; uint64_t h = 0;
for (size_t i = 0; i < std::min((tag.size() - 1), 9lu); ++i) { for (size_t i = 0; i < std::min((tag.size() - 1), 9lu); ++i) {
@ -1071,7 +1045,7 @@ public:
nargs = new_nargs; nargs = new_nargs;
static_size = new_nlocals; static_size = new_nlocals;
stack_slots = new_nlocals; stack_slots = new_nlocals;
stack = SymbolicStack::empty(new_nlocals); stack = SymbolicStack(new_nlocals);
fname = f; fname = f;
has_closure = new_has_closure; has_closure = new_has_closure;
first_line = true; first_line = true;
@ -1098,8 +1072,7 @@ public:
std::move(array_registers), std::move(array_registers),
[](Register::T &&r) -> Opnd { return {r}; }); [](Register::T &&r) -> Opnd { return {r}; });
return utils::concat(std::move(array_result), return utils::concat(std::move(array_result), stack.live_registers());
SymbolicStack::live_registers(stack));
} }
bool do_opt_stabs() const { return mode.target_os == OS::LINUX; } bool do_opt_stabs() const { return mode.target_os == OS::LINUX; }
@ -1134,6 +1107,8 @@ public:
return label; return label;
} }
}; };
const std::string Env::chars =
"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'";
int stack_offset(int i) { return (i >= 0 ? (i + 1) : (-i + 1)) * word_size; } int stack_offset(int i) { return (i >= 0 ? (i + 1) : (-i + 1)) * word_size; }
@ -1549,7 +1524,7 @@ namespace common {
std::pair<size_t, std::vector<Instr>> setup_arguments(Env &env, size_t nargs) { std::pair<size_t, std::vector<Instr>> setup_arguments(Env &env, size_t nargs) {
const auto move_arguments = const auto move_arguments =
[](std::vector<Opnd> &&args, [](std::vector<Opnd> &&args,
std::vector<SymbolicStack::SymbolicLocation> &&arg_locs) { std::vector<SymbolicStack::Location> &&arg_locs) {
using Register = SymbolicStack::Register; using Register = SymbolicStack::Register;
assert(args.size() == arg_locs.size()); assert(args.size() == arg_locs.size());
@ -2262,10 +2237,10 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
return compile_call(env, ".elem", 2, false); return compile_call(env, ".elem", 2, false);
}, },
[&env](const SMInstr::CALL &x) -> std::vector<Instr> { [&env](const SMInstr::CALL &x) -> std::vector<Instr> {
return compile_call(env, x.fname, x.n, x.tail); // TODO: call return compile_call(env, x.fname, x.n, x.tail); // call
}, },
[&env](const SMInstr::CALLC &x) -> std::vector<Instr> { [&env](const SMInstr::CALLC &x) -> std::vector<Instr> {
return compile_call(env, {}, x.n, x.tail); // TODO: closure call return compile_call(env, {}, x.n, x.tail); // closure call
}, },
[&env](const SMInstr::SEXP &x) -> std::vector<Instr> { [&env](const SMInstr::SEXP &x) -> std::vector<Instr> {
const auto s = env.allocate(); const auto s = env.allocate();