2020-09-06 12:45:31 -04:00
|
|
|
#include "Env.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
2020-09-06 16:58:25 -04:00
|
|
|
#include "IR.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
|
2021-08-26 20:33:00 -04:00
|
|
|
#include "common/goos/Reader.h"
|
2022-10-01 11:58:36 -04:00
|
|
|
#include "common/log/log.h"
|
2020-09-05 16:37:37 -04:00
|
|
|
|
2022-06-22 23:37:46 -04:00
|
|
|
#include "third-party/fmt/core.h"
|
|
|
|
|
2020-09-06 12:45:31 -04:00
|
|
|
///////////////////
|
|
|
|
// Env
|
|
|
|
///////////////////
|
2020-09-05 16:37:37 -04:00
|
|
|
|
2020-09-06 12:45:31 -04:00
|
|
|
/*!
|
|
|
|
* Allocate an IRegister with the given type.
|
|
|
|
*/
|
2021-08-24 22:15:26 -04:00
|
|
|
RegVal* Env::make_ireg(const TypeSpec& ts, RegClass reg_class) {
|
|
|
|
return m_parent->make_ireg(ts, reg_class);
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Apply a register constraint to the current function.
|
|
|
|
*/
|
|
|
|
void Env::constrain_reg(IRegConstraint constraint) {
|
|
|
|
m_parent->constrain_reg(std::move(constraint));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Lookup the given symbol object as a lexical variable.
|
|
|
|
*/
|
2020-09-13 17:34:02 -04:00
|
|
|
RegVal* Env::lexical_lookup(goos::Object sym) {
|
2020-09-06 12:45:31 -04:00
|
|
|
return m_parent->lexical_lookup(std::move(sym));
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockEnv* Env::find_block(const std::string& name) {
|
|
|
|
return m_parent->find_block(name);
|
|
|
|
}
|
|
|
|
|
2020-09-19 16:50:42 -04:00
|
|
|
RegVal* Env::make_gpr(const TypeSpec& ts) {
|
2020-12-30 15:33:51 -05:00
|
|
|
return make_ireg(coerce_to_reg_type(ts), RegClass::GPR_64);
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
|
|
|
|
2020-12-30 15:33:51 -05:00
|
|
|
RegVal* Env::make_fpr(const TypeSpec& ts) {
|
|
|
|
return make_ireg(coerce_to_reg_type(ts), RegClass::FLOAT);
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
|
|
|
|
2021-02-05 15:00:17 -05:00
|
|
|
RegVal* Env::make_vfr(const TypeSpec& ts) {
|
|
|
|
return make_ireg(coerce_to_reg_type(ts), RegClass::VECTOR_FLOAT);
|
|
|
|
}
|
|
|
|
|
2020-09-07 19:17:48 -04:00
|
|
|
std::unordered_map<std::string, Label>& Env::get_label_map() {
|
|
|
|
return parent()->get_label_map();
|
|
|
|
}
|
|
|
|
|
2021-08-26 20:33:00 -04:00
|
|
|
void Env::emit(const goos::Object& form, std::unique_ptr<IR> ir) {
|
|
|
|
auto e = function_env();
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(e);
|
2021-08-26 20:33:00 -04:00
|
|
|
e->emit(form, std::move(ir), this);
|
|
|
|
}
|
|
|
|
|
2020-09-06 12:45:31 -04:00
|
|
|
///////////////////
|
|
|
|
// GlobalEnv
|
|
|
|
///////////////////
|
|
|
|
|
|
|
|
// Because this is the top of the environment chain, all these end the parent calls and provide
|
|
|
|
// errors, or return that the items were not found.
|
|
|
|
|
2021-08-24 22:15:26 -04:00
|
|
|
GlobalEnv::GlobalEnv() : Env(EnvKind::OTHER_ENV, nullptr) {}
|
2020-09-06 12:45:31 -04:00
|
|
|
|
|
|
|
std::string GlobalEnv::print() {
|
|
|
|
return "global-env";
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Allocate an IRegister with the given type.
|
|
|
|
*/
|
2021-08-24 22:15:26 -04:00
|
|
|
RegVal* GlobalEnv::make_ireg(const TypeSpec& ts, RegClass reg_class) {
|
2020-09-06 12:45:31 -04:00
|
|
|
(void)ts;
|
2020-12-30 15:33:51 -05:00
|
|
|
(void)reg_class;
|
2020-09-06 12:45:31 -04:00
|
|
|
throw std::runtime_error("cannot alloc reg in GlobalEnv");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Apply a register constraint to the current function.
|
|
|
|
*/
|
|
|
|
void GlobalEnv::constrain_reg(IRegConstraint constraint) {
|
|
|
|
(void)constraint;
|
|
|
|
throw std::runtime_error("cannot constrain reg in GlobalEnv");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Lookup the given symbol object as a lexical variable.
|
|
|
|
*/
|
2020-09-13 17:34:02 -04:00
|
|
|
RegVal* GlobalEnv::lexical_lookup(goos::Object sym) {
|
2020-09-06 12:45:31 -04:00
|
|
|
(void)sym;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-09-06 16:58:25 -04:00
|
|
|
BlockEnv* GlobalEnv::find_block(const std::string& name) {
|
2020-09-06 12:45:31 -04:00
|
|
|
(void)name;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-09-06 16:58:25 -04:00
|
|
|
FileEnv* GlobalEnv::add_file(std::string name) {
|
2020-09-06 12:45:31 -04:00
|
|
|
m_files.push_back(std::make_unique<FileEnv>(this, std::move(name)));
|
|
|
|
return m_files.back().get();
|
|
|
|
}
|
|
|
|
|
2022-11-29 19:22:22 -05:00
|
|
|
std::vector<std::string> GlobalEnv::list_files_with_prefix(const std::string& prefix) {
|
|
|
|
std::vector<std::string> matches = {};
|
|
|
|
for (const auto& file : m_files) {
|
|
|
|
if (file->name().rfind(prefix) == 0) {
|
|
|
|
matches.push_back(file->name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return matches;
|
|
|
|
}
|
|
|
|
|
2020-09-07 19:17:48 -04:00
|
|
|
///////////////////
|
|
|
|
// BlockEnv
|
|
|
|
///////////////////
|
|
|
|
|
2021-08-24 22:15:26 -04:00
|
|
|
BlockEnv::BlockEnv(Env* parent, std::string _name)
|
|
|
|
: Env(EnvKind::OTHER_ENV, parent), name(std::move(_name)) {}
|
2020-09-07 19:17:48 -04:00
|
|
|
|
|
|
|
std::string BlockEnv::print() {
|
|
|
|
return "block-" + name;
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockEnv* BlockEnv::find_block(const std::string& block) {
|
|
|
|
if (name == block) {
|
|
|
|
return this;
|
|
|
|
} else {
|
|
|
|
return parent()->find_block(block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 12:45:31 -04:00
|
|
|
///////////////////
|
|
|
|
// FileEnv
|
|
|
|
///////////////////
|
|
|
|
|
2021-08-24 22:15:26 -04:00
|
|
|
FileEnv::FileEnv(Env* parent, std::string name)
|
|
|
|
: Env(EnvKind::FILE_ENV, parent), m_name(std::move(name)) {}
|
2020-09-06 12:45:31 -04:00
|
|
|
|
|
|
|
std::string FileEnv::print() {
|
|
|
|
return "file-" + m_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileEnv::add_function(std::unique_ptr<FunctionEnv> fe) {
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(fe->idx_in_file == -1);
|
2020-09-12 13:11:42 -04:00
|
|
|
fe->idx_in_file = m_functions.size();
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(!fe->name().empty());
|
2020-09-06 12:45:31 -04:00
|
|
|
m_functions.push_back(std::move(fe));
|
|
|
|
}
|
|
|
|
|
2020-09-12 13:11:42 -04:00
|
|
|
void FileEnv::add_static(std::unique_ptr<StaticObject> s) {
|
|
|
|
m_statics.push_back(std::move(s));
|
|
|
|
}
|
|
|
|
|
2020-09-06 12:45:31 -04:00
|
|
|
void FileEnv::add_top_level_function(std::unique_ptr<FunctionEnv> fe) {
|
|
|
|
// todo, set FE as top level segment
|
|
|
|
m_functions.push_back(std::move(fe));
|
|
|
|
m_top_level_func = m_functions.back().get();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileEnv::debug_print_tl() {
|
2020-09-06 16:58:25 -04:00
|
|
|
if (m_top_level_func) {
|
|
|
|
for (auto& code : m_top_level_func->code()) {
|
2022-10-01 11:58:36 -04:00
|
|
|
lg::print("{}\n", code->print());
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("no top level function.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-06 17:42:20 -04:00
|
|
|
bool FileEnv::is_empty() {
|
|
|
|
return m_functions.size() == 1 && m_functions.front().get() == m_top_level_func &&
|
|
|
|
m_top_level_func->code().empty();
|
|
|
|
}
|
2023-02-24 18:32:30 -05:00
|
|
|
|
|
|
|
void FileEnv::cleanup_after_codegen() {
|
|
|
|
m_top_level_func = nullptr;
|
|
|
|
m_functions.clear();
|
|
|
|
m_statics.clear();
|
|
|
|
m_vals.clear();
|
|
|
|
}
|
|
|
|
|
2020-09-06 12:45:31 -04:00
|
|
|
///////////////////
|
|
|
|
// FunctionEnv
|
|
|
|
///////////////////
|
|
|
|
|
2021-08-26 20:33:00 -04:00
|
|
|
FunctionEnv::FunctionEnv(Env* parent, std::string name, const goos::Reader* reader)
|
|
|
|
: DeclareEnv(EnvKind::FUNCTION_ENV, parent), m_name(std::move(name)), m_reader(reader) {}
|
2020-09-06 12:45:31 -04:00
|
|
|
|
|
|
|
std::string FunctionEnv::print() {
|
|
|
|
return "function-" + m_name;
|
|
|
|
}
|
|
|
|
|
2021-08-26 20:33:00 -04:00
|
|
|
void FunctionEnv::emit(const goos::Object& form, std::unique_ptr<IR> ir, Env* lowest_env) {
|
2020-09-06 12:45:31 -04:00
|
|
|
ir->add_constraints(&m_constraints, m_code.size());
|
|
|
|
m_code.push_back(std::move(ir));
|
2021-08-26 20:33:00 -04:00
|
|
|
if (m_reader->db.has_info(form)) {
|
|
|
|
// if we have info, it means we came from real code and we can just use that.
|
|
|
|
m_code_debug_source.push_back(form);
|
|
|
|
} else {
|
|
|
|
// let's see if we're in a macro:
|
|
|
|
auto mac_env = lowest_env->macro_expand_env();
|
|
|
|
if (mac_env) {
|
|
|
|
while (mac_env) {
|
|
|
|
if (m_reader->db.has_info(mac_env->macro_use_location())) {
|
|
|
|
m_code_debug_source.push_back(mac_env->macro_use_location());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto parent = mac_env->parent();
|
|
|
|
if (parent) {
|
|
|
|
mac_env = parent->macro_expand_env();
|
|
|
|
} else {
|
|
|
|
m_code_debug_source.push_back(form);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_code_debug_source.push_back(form);
|
|
|
|
}
|
|
|
|
}
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
2021-08-26 20:33:00 -04:00
|
|
|
|
2020-09-06 12:45:31 -04:00
|
|
|
void FunctionEnv::finish() {
|
2020-09-07 19:17:48 -04:00
|
|
|
resolve_gotos();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FunctionEnv::resolve_gotos() {
|
|
|
|
for (auto& gt : unresolved_gotos) {
|
|
|
|
auto kv_label = m_labels.find(gt.label);
|
|
|
|
if (kv_label == m_labels.end()) {
|
|
|
|
throw std::runtime_error("Invalid goto " + gt.label);
|
|
|
|
}
|
|
|
|
gt.ir->resolve(&kv_label->second);
|
|
|
|
}
|
2020-09-13 17:34:02 -04:00
|
|
|
|
|
|
|
for (auto& gt : unresolved_cond_gotos) {
|
|
|
|
auto kv_label = m_labels.find(gt.label);
|
|
|
|
if (kv_label == m_labels.end()) {
|
|
|
|
throw std::runtime_error("invalid when-goto destination " + gt.label);
|
|
|
|
}
|
|
|
|
gt.ir->label = kv_label->second;
|
|
|
|
gt.ir->mark_as_resolved();
|
|
|
|
}
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
|
|
|
|
2021-08-24 22:15:26 -04:00
|
|
|
RegVal* FunctionEnv::make_ireg(const TypeSpec& ts, RegClass reg_class) {
|
2020-09-06 12:45:31 -04:00
|
|
|
IRegister ireg;
|
2020-12-30 15:33:51 -05:00
|
|
|
ireg.reg_class = reg_class;
|
2020-09-06 12:45:31 -04:00
|
|
|
ireg.id = m_iregs.size();
|
2023-09-22 05:54:49 -04:00
|
|
|
auto rv = std::make_unique<RegVal>(ireg, ts);
|
2020-09-06 12:45:31 -04:00
|
|
|
m_iregs.push_back(std::move(rv));
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(reg_class != RegClass::INVALID);
|
2020-09-06 12:45:31 -04:00
|
|
|
return m_iregs.back().get();
|
2020-09-07 19:17:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unordered_map<std::string, Label>& FunctionEnv::get_label_map() {
|
|
|
|
return m_labels;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unordered_map<std::string, Label>& LabelEnv::get_label_map() {
|
|
|
|
return m_labels;
|
2020-09-12 13:11:42 -04:00
|
|
|
}
|
|
|
|
|
2020-11-22 20:10:33 -05:00
|
|
|
BlockEnv* LabelEnv::find_block(const std::string& name) {
|
|
|
|
(void)name;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2020-09-13 17:34:02 -04:00
|
|
|
RegVal* FunctionEnv::lexical_lookup(goos::Object sym) {
|
2020-09-12 13:11:42 -04:00
|
|
|
if (!sym.is_symbol()) {
|
|
|
|
throw std::runtime_error("invalid symbol in lexical_lookup");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto kv = params.find(sym.as_symbol()->name);
|
|
|
|
if (kv == params.end()) {
|
|
|
|
return parent()->lexical_lookup(sym);
|
|
|
|
}
|
|
|
|
|
|
|
|
return kv->second;
|
|
|
|
}
|
|
|
|
|
2022-01-21 21:11:57 -05:00
|
|
|
FunctionEnv::StackSpace FunctionEnv::allocate_aligned_stack_space(int size_bytes, int align_bytes) {
|
2020-11-22 12:22:19 -05:00
|
|
|
require_aligned_stack();
|
2022-02-08 19:02:47 -05:00
|
|
|
ASSERT(align_bytes <= 16);
|
2020-12-06 15:42:26 -05:00
|
|
|
int align_slots = (align_bytes + emitter::GPR_SIZE - 1) / emitter::GPR_SIZE;
|
|
|
|
while (m_stack_var_slots_used % align_slots) {
|
|
|
|
m_stack_var_slots_used++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we align our size too. The stack versions of the default new methods in kscheme.cpp round up
|
|
|
|
// to 16 bytes and memset this size, which can cause issues if we make this size only 8 byte
|
|
|
|
// aligned.
|
|
|
|
while (size_bytes % align_bytes) {
|
|
|
|
size_bytes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int slots_used = (size_bytes + emitter::GPR_SIZE - 1) / emitter::GPR_SIZE;
|
2022-01-21 21:11:57 -05:00
|
|
|
StackSpace result;
|
|
|
|
result.slot_count = slots_used;
|
|
|
|
result.start_slot = m_stack_var_slots_used;
|
2020-12-06 15:42:26 -05:00
|
|
|
m_stack_var_slots_used += slots_used;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-01-21 21:11:57 -05:00
|
|
|
StackVarAddrVal* FunctionEnv::allocate_aligned_stack_variable(const TypeSpec& ts,
|
|
|
|
int size_bytes,
|
|
|
|
int align_bytes) {
|
|
|
|
if (align_bytes > 16) {
|
2022-10-01 11:58:36 -04:00
|
|
|
lg::print("\n\n\nBad stack align: {} bytes for {}\n\n\n\n", align_bytes, ts.print());
|
2022-01-21 21:11:57 -05:00
|
|
|
}
|
|
|
|
auto space = allocate_aligned_stack_space(size_bytes, align_bytes);
|
|
|
|
return alloc_val<StackVarAddrVal>(ts, space.start_slot, space.slot_count);
|
|
|
|
}
|
|
|
|
|
2021-07-22 20:14:08 -04:00
|
|
|
RegVal* FunctionEnv::push_reg_val(std::unique_ptr<RegVal> in) {
|
|
|
|
m_iregs.push_back(std::move(in));
|
|
|
|
return m_iregs.back().get();
|
|
|
|
}
|
|
|
|
|
2022-01-21 21:11:57 -05:00
|
|
|
StackVarAddrVal* FunctionEnv::allocate_stack_singleton(const TypeSpec& ts,
|
|
|
|
int size_bytes,
|
|
|
|
int align_bytes) {
|
|
|
|
const auto& existing = m_stack_singleton_slots.find(ts.print());
|
|
|
|
if (existing == m_stack_singleton_slots.end()) {
|
|
|
|
auto space = allocate_aligned_stack_space(size_bytes, align_bytes);
|
|
|
|
m_stack_singleton_slots[ts.print()] = space;
|
|
|
|
return alloc_val<StackVarAddrVal>(ts, space.start_slot, space.slot_count);
|
|
|
|
} else {
|
|
|
|
return alloc_val<StackVarAddrVal>(ts, existing->second.start_slot, existing->second.slot_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-12 13:11:42 -04:00
|
|
|
///////////////////
|
|
|
|
// LexicalEnv
|
|
|
|
///////////////////
|
|
|
|
|
|
|
|
std::string LexicalEnv::print() {
|
|
|
|
return "lexical";
|
|
|
|
}
|
|
|
|
|
2020-09-13 17:34:02 -04:00
|
|
|
RegVal* LexicalEnv::lexical_lookup(goos::Object sym) {
|
2020-09-12 13:11:42 -04:00
|
|
|
if (!sym.is_symbol()) {
|
|
|
|
throw std::runtime_error("invalid symbol in lexical_lookup");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto kv = vars.find(sym.as_symbol()->name);
|
|
|
|
if (kv == vars.end()) {
|
|
|
|
return parent()->lexical_lookup(sym);
|
|
|
|
}
|
|
|
|
|
|
|
|
return kv->second;
|
|
|
|
}
|