wall, wextra, compiler: fix warnings & todos

This commit is contained in:
ProgramSnail 2025-01-23 15:26:54 +03:00
parent c811d5ec50
commit 6d774ad42f
2 changed files with 110 additions and 161 deletions

View file

@ -11,7 +11,7 @@
(mode (mode
(promote (until-clean))) (promote (until-clean)))
(action (action
(run g++ -std=c++20 -DWITH_CHECK -Iinclude/ %{main} %{parser} %{analyzer} %{module_manager} %{runtime} %{obj} -o %{target}))) (run g++ -Wall -Wextra -std=c++20 -DWITH_CHECK -Iinclude/ %{main} %{parser} %{analyzer} %{module_manager} %{runtime} %{obj} -o %{target})))
(rule (rule
(target types.o) (target types.o)
@ -22,7 +22,7 @@
(mode (mode
(promote (until-clean))) (promote (until-clean)))
(action (action
(run gcc -Iinclude/ -DWITH_CHECK -c %{src} -o %{target}))) (run gcc -Wall -Wextra -Iinclude/ -DWITH_CHECK -c %{src} -o %{target})))
(rule (rule
(target interpreter.o) (target interpreter.o)
@ -33,7 +33,7 @@
(mode (mode
(promote (until-clean))) (promote (until-clean)))
(action (action
(run gcc -Iinclude/ -DWITH_CHECK -c %{src} -o %{target}))) (run gcc -Wall -Wextra -Iinclude/ -DWITH_CHECK -c %{src} -o %{target})))
(rule (rule
(target module_manager.o) (target module_manager.o)
@ -44,4 +44,4 @@
(mode (mode
(promote (until-clean))) (promote (until-clean)))
(action (action
(run g++ -Iinclude/ -c %{src} -o %{target}))) (run g++ -Wall -Wextra -Iinclude/ -c %{src} -o %{target})))

View file

@ -32,7 +32,7 @@ template <class... Ts> multifunc(Ts...) -> multifunc<Ts...>;
#endif #endif
} }
// TODO: use ranges transform // BETTER: use ranges transform
template <typename T, typename U> template <typename T, typename U>
std::vector<U> transform(std::vector<T> v, const std::function<U(T &&)> &f) { std::vector<U> transform(std::vector<T> v, const std::function<U(T &&)> &f) {
std::vector<U> result; std::vector<U> result;
@ -122,7 +122,7 @@ std::string labeled_scoped(int64_t i, const std::string_view s) {
} // namespace utils } // namespace utils
enum class OS { // TODO: other oses enum class OS {
LINUX, LINUX,
DARWIN, DARWIN,
}; };
@ -186,7 +186,7 @@ T of_64bit(const T &r) { return {.name = r.reg.name64, .reg = r.reg}; }
const std::string &to_string(const T &r) { return r.name; } const std::string &to_string(const T &r) { return r.name; }
const auto none = Register::T{}; // const auto none = Register::T{}; // NOTE: not used
} // namespace Register } // namespace Register
namespace Registers { namespace Registers {
@ -442,12 +442,12 @@ struct Instr {
}; };
/* a conditional jump */ /* a conditional jump */
struct CJmp { struct CJmp {
std::string left; std::string cmp;
std::string right; std::string label;
}; // TODO: right names (?) };
/* a non-conditional jump by a name */ /* a non-conditional jump by a name */
struct Jmp { struct Jmp {
std::string name; std::string label;
}; };
/* a non-conditional jump by indirect address */ /* a non-conditional jump by indirect address */
struct JmpI { struct JmpI {
@ -565,10 +565,10 @@ template <typename U> struct AbstractSymbolicStack {
/* Last allocated position on symbolic stack */ /* Last allocated position on symbolic stack */
struct StackState { struct StackState {
struct S { struct S {
int n; size_t n;
}; };
struct R { struct R {
int n; size_t n;
}; };
struct E {}; struct E {};
@ -626,7 +626,7 @@ template <typename U> struct AbstractSymbolicStack {
return {R(x.n + 1)}; return {R(x.n + 1)};
} }
}, },
[](const E &x) -> StackState { return {R(0)}; }, [](const E &) -> StackState { return {R(0)}; },
}, },
*v.state); *v.state);
return {new_state, std::move(v.registers)}; return {new_state, std::move(v.registers)};
@ -636,14 +636,13 @@ template <typename U> struct AbstractSymbolicStack {
StackState new_state = std::visit( StackState new_state = std::visit(
utils::multifunc{ utils::multifunc{
[&v](const S &x) -> StackState { [&v](const S &x) -> StackState {
return x.n == 0 ? StackState{R{ return x.n == 0 ? StackState{R{v.registers.size() - 1}}
static_cast<int>(v.registers.size() - 1)}}
: StackState{S{x.n - 1}}; : StackState{S{x.n - 1}};
}, },
[&v](const R &x) -> StackState { [](const R &x) -> StackState {
return x.n == 0 ? StackState{E{}} : StackState{R{x.n - 1}}; return x.n == 0 ? StackState{E{}} : StackState{R{x.n - 1}};
}, },
[](const E &x) -> StackState { [](const E &) -> StackState {
failure("Empty stack %s: %d", __FILE__, __LINE__); failure("Empty stack %s: %d", __FILE__, __LINE__);
utils::unreachable(); utils::unreachable();
}, },
@ -659,7 +658,7 @@ template <typename U> struct AbstractSymbolicStack {
[&v](const R &x) -> SymbolicLocation { [&v](const R &x) -> SymbolicLocation {
return {Register{v.registers[x.n]}}; return {Register{v.registers[x.n]}};
}, },
[](const E &x) -> SymbolicLocation { [](const E &) -> SymbolicLocation {
failure("Empty stack %s: %d", __FILE__, __LINE__); failure("Empty stack %s: %d", __FILE__, __LINE__);
utils::unreachable(); utils::unreachable();
}, },
@ -671,11 +670,11 @@ template <typename U> struct AbstractSymbolicStack {
return std::holds_alternative<typename StackState::E>(*v.state); return std::holds_alternative<typename StackState::E>(*v.state);
} }
// TODO: replace with range // BETTER: replace with range
static std::vector<U> live_registers(const T &v) { static std::vector<U> live_registers(const T &v) {
return std::visit( // return std::visit( //
utils::multifunc{ utils::multifunc{
[&v](const S &x) { return v.registers; }, [&v](const S &) { return v.registers; },
[&v](const R &x) { [&v](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(),
@ -684,7 +683,7 @@ template <typename U> struct AbstractSymbolicStack {
// 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 &x) { return std::vector<U>{}; }, [](const E &) { return std::vector<U>{}; },
}, },
*v.state); *v.state);
} }
@ -692,7 +691,7 @@ template <typename U> struct AbstractSymbolicStack {
static size_t stack_size(const T &v) { static size_t stack_size(const T &v) {
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 &x) { return 0; }, [](const auto &) -> size_t { return 0; },
}, },
*v.state); *v.state);
} }
@ -795,38 +794,19 @@ using SetS = std::unordered_set<std::string>;
/* A map indexed by strings */ /* A map indexed by strings */
template <typename T> using MapS = std::unordered_map<std::string, T>; template <typename T> using MapS = std::unordered_map<std::string, T>;
// TODO: any func required (?)
template <typename Prg> struct Indexer {
// let rec make_env m = function
// | [] -> m
// | LABEL l :: tl | FLABEL l :: tl -> make_env (M.add l tl m) tl
// | _ :: tl -> make_env m tl
// in
// let m = make_env M.empty prg in
// object
// method is_label l = M.mem l m
// method labeled l = M.find l m
// end
MapS<Prg> m;
};
struct Mode { struct Mode {
bool is_debug; bool is_debug;
OS target_os; OS target_os;
}; };
// FIXME: TODO: find out what is Prg
using Prg = std::string;
// TODO: rebuild in c++ way // TODO: rebuild in c++ way
struct Env : public Indexer<Prg> { struct Env {
private: private:
const std::string chars = const std::string chars =
"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'"; "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'";
// NOTE: is not required
// const std::vector<Opnd> argument_registers = Registers.argument_registers // const std::vector<Opnd> argument_registers = Registers.argument_registers
// // TODO: cast
const size_t argument_registers_size = Registers::argument_registers.size();
private: private:
SetS globals; /* a set of global variables */ SetS globals; /* a set of global variables */
@ -836,7 +816,6 @@ private:
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::T stack = SymbolicStack::empty(0); /* symbolic stack */
std::vector<std::vector<std::string>> locals; std::vector<std::vector<std::string>> locals;
/* function local variables */ // TODO: never used (?)
MapS<SymbolicStack::T> stackmap; /* labels to stack map */ MapS<SymbolicStack::T> stackmap; /* labels to stack map */
bool barrier = false; /* barrier condition */ bool barrier = false; /* barrier condition */
@ -886,23 +865,6 @@ public:
return result.str(); return result.str();
} }
std::string print_locals() const {
std::stringstream result;
// TODO
result << std::format("LOCALS: size = {}\n", static_size);
for (const auto &l : locals) {
result << "(";
// NOTE: same to List.iter (fun (a, i) -> Printf.printf "%s=%d " a i) l;
for (size_t i = 0; i < l.size(); ++i) {
result << std::format("{}={} ", l[i],
i); // TODO: exact format of locals
}
result << ")\n";
}
result << "END LOCALS\n";
return result.str();
}
/* Assert empty stack */ /* Assert empty stack */
void assert_empty_stack() const { assert(SymbolicStack::is_empty(stack)); } void assert_empty_stack() const { assert(SymbolicStack::is_empty(stack)); }
@ -953,10 +915,10 @@ public:
return M{DataKind::F, ext, Addressed::A, x.s}; return M{DataKind::F, ext, Addressed::A, x.s};
}, },
[](const ValT::Local &x) -> Opnd { return S{x.n}; }, [](const ValT::Local &x) -> Opnd { return S{x.n}; },
[this](const ValT::Arg &x) -> Opnd { [](const ValT::Arg &x) -> Opnd {
return x.n < argument_registers_size return x.n < Registers::argument_registers.size()
? Opnd{argument_registers[x.n]} ? Opnd{argument_registers[x.n]}
: S{-(x.n - argument_registers_size) - 1}; : S{-(x.n - Registers::argument_registers.size()) - 1};
}, },
[](const ValT::Access &x) -> Opnd { [](const ValT::Access &x) -> Opnd {
return I{static_cast<int>(word_size * (x.n + 1)), r15}; return I{static_cast<int>(word_size * (x.n + 1)), r15};
@ -986,13 +948,11 @@ public:
std::pair<std::vector<SymbolicStack::SymbolicLocation>, size_t> std::pair<std::vector<SymbolicStack::SymbolicLocation>, size_t>
arguments_locations(size_t n) { arguments_locations(size_t n) {
// TODO
using SymbolicLocation = SymbolicStack::SymbolicLocation; using SymbolicLocation = SymbolicStack::SymbolicLocation;
using Register = SymbolicStack::Register; using Register = SymbolicStack::Register;
using Stack = SymbolicStack::Stack; using Stack = SymbolicStack::Stack;
if (n < argument_registers_size) { if (n < Registers::argument_registers.size()) {
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);
@ -1008,9 +968,9 @@ public:
[](const auto &r) -> SymbolicLocation { [](const auto &r) -> SymbolicLocation {
return {Register{r}}; return {Register{r}};
}), }),
std::vector<SymbolicLocation>(n - argument_registers_size, std::vector<SymbolicLocation>(
{Stack{}})), n - Registers::argument_registers.size(), {Stack{}})),
n - argument_registers_size}; n - Registers::argument_registers.size()};
} }
} }
@ -1099,17 +1059,6 @@ public:
return result; return result;
} }
/* gets all string definitions */
SetS get_strings() const {
SetS result;
result.reserve(stringm.size());
for (const auto &str : stringm) {
result.insert(
str.first); // TODO: M.bindings -> take first elem argument (?)
}
return result;
}
/* gets a number of stack positions allocated */ /* gets a number of stack positions allocated */
size_t get_allocated() const { return stack_slots; } size_t get_allocated() const { return stack_slots; }
std::string get_allocated_size() const { std::string get_allocated_size() const {
@ -1206,9 +1155,7 @@ std::string to_code(const Env &env, const Opnd &opnd) {
return std::format("{}(%rip)", x.name); return std::format("{}(%rip)", x.name);
} }
// else -> x.ext == Externality::E && x.kind == DataKind::D // else -> x.ext == Externality::E && x.kind == DataKind::D
return std::format( return std::format("{}@GOTPCREL(%rip)", env.prefixed(x.name));
"{}@GOTPCREL(%rip)",
env.prefixed(x.name)); // TODO: does @ mean something (?)
}, },
[&env](const Opnd::C &x) { [&env](const Opnd::C &x) {
return std::format("${}", env.prefixed(x.name)); return std::format("${}", env.prefixed(x.name));
@ -1248,7 +1195,7 @@ std::string to_code(const Env &env, const Instr &instr) {
return std::visit( return std::visit(
utils::multifunc{ utils::multifunc{
[](const Cltd &x) -> std::string { return "\tcqo"; }, [](const Cltd &) -> std::string { return "\tcqo"; },
[](const Set &x) -> std::string { [](const Set &x) -> std::string {
auto r = to_string(Register::of_8bit(x.reg)); auto r = to_string(Register::of_8bit(x.reg));
return std::format("\tset{}\t{}", x.suffix, std::move(r)); return std::format("\tset{}\t{}", x.suffix, std::move(r));
@ -1283,7 +1230,7 @@ std::string to_code(const Env &env, const Instr &instr) {
[&opnd_to_code](const Pop &x) -> std::string { [&opnd_to_code](const Pop &x) -> std::string {
return std::format("\tpopq\t{}", opnd_to_code(x.opnd)); return std::format("\tpopq\t{}", opnd_to_code(x.opnd));
}, },
[](const Ret &x) -> std::string { return "\tret"; }, [](const Ret &) -> std::string { return "\tret"; },
[&env](const Call &x) -> std::string { [&env](const Call &x) -> std::string {
return std::format("\tcall\t{}", env.prefixed(x.name)); return std::format("\tcall\t{}", env.prefixed(x.name));
}, },
@ -1294,13 +1241,13 @@ std::string to_code(const Env &env, const Instr &instr) {
return std::format("{}:\n", env.prefixed(x.name)); return std::format("{}:\n", env.prefixed(x.name));
}, },
[&env](const Jmp &x) -> std::string { [&env](const Jmp &x) -> std::string {
return std::format("\tjmp\t{}", env.prefixed(x.name)); return std::format("\tjmp\t{}", env.prefixed(x.label));
}, },
[&opnd_to_code](const JmpI &x) -> std::string { [&opnd_to_code](const JmpI &x) -> std::string {
return std::format("\tjmp\t*({})", opnd_to_code(x.opnd)); return std::format("\tjmp\t*({})", opnd_to_code(x.opnd));
}, },
[&env](const CJmp &x) -> std::string { [&env](const CJmp &x) -> std::string {
return std::format("\tj{}\t{}", x.left, env.prefixed(x.right)); return std::format("\tj{}\t{}", x.cmp, env.prefixed(x.label));
}, },
[](const Meta &x) -> std::string { [](const Meta &x) -> std::string {
return std::format("{}\n", x.name); return std::format("{}\n", x.name);
@ -1317,7 +1264,7 @@ std::string to_code(const Env &env, const Instr &instr) {
[&opnd_to_code](const Sar1 &x) -> std::string { [&opnd_to_code](const Sar1 &x) -> std::string {
return std::format("\tsarq\t{}", opnd_to_code(x.opnd)); return std::format("\tsarq\t{}", opnd_to_code(x.opnd));
}, },
[](const Repmovsl &x) -> std::string { return "\trep movsq\t"; }, [](const Repmovsl &) -> std::string { return "\trep movsq\t"; },
}, },
*instr); *instr);
} }
@ -1604,7 +1551,6 @@ std::pair<size_t, std::vector<Instr>> setup_arguments(Env &env, size_t nargs) {
[](std::vector<Opnd> &&args, [](std::vector<Opnd> &&args,
std::vector<SymbolicStack::SymbolicLocation> &&arg_locs) { std::vector<SymbolicStack::SymbolicLocation> &&arg_locs) {
using Register = SymbolicStack::Register; using Register = SymbolicStack::Register;
using Stack = SymbolicStack::Stack;
assert(args.size() == arg_locs.size()); assert(args.size() == arg_locs.size());
@ -2037,7 +1983,7 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
env.register_extern(x.name); env.register_extern(x.name);
return {}; return {};
}, },
[](const SMInstr::IMPORT &x) [](const SMInstr::IMPORT &)
-> std::vector<Instr> { // NOTE: not required in bytecode -> std::vector<Instr> { // NOTE: not required in bytecode
return {}; return {};
}, },
@ -2093,7 +2039,7 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
Mov{rax, env.loc(x.v)}} Mov{rax, env.loc(x.v)}}
: std::vector<Instr>{Mov{s, env.loc(x.v)}}; : std::vector<Instr>{Mov{s, env.loc(x.v)}};
}, },
[&env](const SMInstr::STA &x) -> std::vector<Instr> { [&env](const SMInstr::STA &) -> std::vector<Instr> {
return compile_call(env, ".sta", 3, false); return compile_call(env, ".sta", 3, false);
}, },
[&env](const SMInstr::STI &) -> std::vector<Instr> { [&env](const SMInstr::STI &) -> std::vector<Instr> {
@ -2107,13 +2053,13 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
[&env](const SMInstr::BINOP &x) -> std::vector<Instr> { [&env](const SMInstr::BINOP &x) -> std::vector<Instr> {
return compile_binop(env, x.opr); return compile_binop(env, x.opr);
}, },
[&env](const SMInstr::LABEL &x) -> std::vector<Instr> { [](const SMInstr::LABEL &x) -> std::vector<Instr> {
return {Label{x.s}}; return {Label{x.s}};
}, },
[&env](const SMInstr::FLABEL &x) -> std::vector<Instr> { [](const SMInstr::FLABEL &x) -> std::vector<Instr> {
return {Label{x.s}}; return {Label{x.s}};
}, },
[&env](const SMInstr::SLABEL &x) -> std::vector<Instr> { [](const SMInstr::SLABEL &x) -> std::vector<Instr> {
return {Label{x.s}}; return {Label{x.s}};
}, },
[&env](const SMInstr::JMP &x) -> std::vector<Instr> { [&env](const SMInstr::JMP &x) -> std::vector<Instr> {
@ -2140,8 +2086,7 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
const std::string name = (x.f[0] == 'L' ? x.f.substr(1) : x.f); const std::string name = (x.f[0] == 'L' ? x.f.substr(1) : x.f);
const auto stabs = [&env, &cmd, &x, const auto stabs = [&env, &x, &name]() -> std::vector<Instr> {
&name]() -> std::vector<Instr> {
if (!env.do_opt_stabs()) { if (!env.do_opt_stabs()) {
return {}; return {};
} }
@ -2176,7 +2121,8 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
} }
auto argc_correct_label = x.f + "_argc_correct"; auto argc_correct_label = x.f + "_argc_correct";
auto pat_addr = // TODO: check that the same string auto pat_addr = // TODO: check is that is the same string to
// ocaml version one
env.register_string( env.register_string(
"Function %s called with incorrect arguments count. \ "Function %s called with incorrect arguments count. \
Expected: %d. Actual: %d\\n"); Expected: %d. Actual: %d\\n");
@ -2205,7 +2151,9 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
env.assert_empty_stack(); env.assert_empty_stack();
const bool has_closure = !x.closure.empty(); const bool has_closure = !x.closure.empty();
env.enter(x.f, x.nargs, x.nlocals, has_closure); env.enter(x.f, x.nargs, x.nlocals, has_closure);
return utils::concat(std::move(stabs_code), Instr{Meta{"\t.cfi_startproc"}}, return utils::concat(
std::move(stabs_code),
Instr{Meta{"\t.cfi_startproc"}},
(x.f == cmd.topname ? std::vector<Instr>{ (x.f == cmd.topname ? std::vector<Instr>{
Mov{M{DataKind::D, Externality::I, Addressed::V, "init"}, rax}, Mov{M{DataKind::D, Externality::I, Addressed::V, "init"}, rax},
Binop{Opr::TEST, rax, rax}, Binop{Opr::TEST, rax, rax},
@ -2219,7 +2167,7 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
Ret{}, Ret{},
Label{"continue"}, Label{"continue"},
Mov {L {1}, M {DataKind::D, Externality::I, Addressed::V, "init"}}, Mov {L {1}, M {DataKind::D, Externality::I, Addressed::V, "init"}},
} :std::vector<Instr>{}), } : std::vector<Instr>{}),
std::vector<Instr>{ std::vector<Instr>{
Push{rbp}, Push{rbp},
Meta{"\t.cfi_def_cfa_offset\t8"}, Meta{"\t.cfi_def_cfa_offset\t8"},
@ -2252,18 +2200,19 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
Pop{rsi}, Pop{rsi},
Pop{rdi}, Pop{rdi},
Call{"set_args"}, Call{"set_args"},
} : } : std::vector<Instr>{}),
std::vector<Instr>{}), (x.f == cmd.topname ? // TODO: optimize filter
(x.f == cmd.topname ? utils::transform<std::string, Instr>(
// TODO: optimize filter utils::filter<std::string>(
utils::transform<std::string, Instr>(utils::filter<std::string>(std::vector<std::string>{imports}, [](const auto& i) { return i != "Std"; }), std::vector<std::string>{imports},
[](const auto& i) -> Instr { return Call{std::format("init" + i)}; }) [](const auto &i) { return i != "Std"; }),
: std::vector<Instr>{}), [](const auto &i) -> Instr {
return Call{std::format("init" + i)};
}) : std::vector<Instr>{}),
std::move(check_argc_code) std::move(check_argc_code)
); );
// TODO
}, },
[&env](const SMInstr::END &y) -> std::vector<Instr> { [&env](const SMInstr::END &) -> std::vector<Instr> {
const auto x = env.pop(); const auto x = env.pop();
env.assert_empty_stack(); env.assert_empty_stack();
const auto &name = env.fname; const auto &name = env.fname;
@ -2303,13 +2252,13 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
std::move(stabs)); std::move(stabs));
env.leave(); env.leave();
return std::move(result); return result;
}, },
[&env](const SMInstr::RET &) -> std::vector<Instr> { [&env](const SMInstr::RET &) -> std::vector<Instr> {
const auto x = env.peek(); const auto x = env.peek();
return {Mov{x, rax}, Jmp{env.epilogue()}}; return {Mov{x, rax}, Jmp{env.epilogue()}};
}, },
[&env](const SMInstr::ELEM &x) -> std::vector<Instr> { [&env](const SMInstr::ELEM &) -> std::vector<Instr> {
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> {
@ -2324,7 +2273,7 @@ std::vector<Instr> compile(const Options &cmd, Env &env,
return utils::concat(mov(L{box(env.hash(x.tag))}, s), return utils::concat(mov(L{box(env.hash(x.tag))}, s),
std::move(code)); std::move(code));
}, },
[&env](const SMInstr::DROP &x) -> std::vector<Instr> { [&env](const SMInstr::DROP &) -> std::vector<Instr> {
env.pop(); env.pop();
return {}; return {};
}, },