#pragma once /*! * @file Env.h * The Env tree. The stores all of the nested scopes/contexts during compilation and also * manages the memory for stuff generated during compiling. */ #include #include #include #include "Label.h" #include "StaticObject.h" #include "Val.h" #include "common/goos/Object.h" #include "common/type_system/TypeSpec.h" #include "goalc/regalloc/allocator_interface.h" class FileEnv; class BlockEnv; class FunctionEnv; class SymbolMacroEnv; class MacroExpandEnv; class IR; enum class EnvKind { FILE_ENV, FUNCTION_ENV, SYMBOL_MACRO_ENV, MACRO_EXPAND_ENV, OTHER_ENV }; /*! * Parent class for Env's */ class Env { public: explicit Env(EnvKind kind, Env* parent); virtual std::string print() = 0; void emit(const goos::Object& form, std::unique_ptr ir); virtual RegVal* make_ireg(const TypeSpec& ts, RegClass reg_class); virtual void constrain_reg(IRegConstraint constraint); // todo, remove! virtual RegVal* lexical_lookup(goos::Object sym); virtual BlockEnv* find_block(const std::string& name); virtual std::unordered_map& get_label_map(); RegVal* make_gpr(const TypeSpec& ts); RegVal* make_fpr(const TypeSpec& ts); RegVal* make_vfr(const TypeSpec& ts); virtual ~Env() = default; Env* parent() { return m_parent; } template void emit_ir(const goos::Object& form, Args&&... args) { emit(form, std::make_unique(std::forward(args)...)); } FileEnv* file_env() { return m_lowest_envs.file_env; } FunctionEnv* function_env() { return m_lowest_envs.function_env; } SymbolMacroEnv* symbol_macro_env() { return m_lowest_envs.symbol_macro_env; } MacroExpandEnv* macro_expand_env() { return m_lowest_envs.macro_expand_env; } protected: EnvKind m_kind; Env* m_parent = nullptr; // cache of the lowest env of the given type, possibly including ourselves struct { FileEnv* file_env = nullptr; FunctionEnv* function_env = nullptr; SymbolMacroEnv* symbol_macro_env = nullptr; MacroExpandEnv* macro_expand_env = nullptr; } m_lowest_envs; }; /*! * The top-level Env. Holds FileEnvs for all files. */ class GlobalEnv : public Env { public: GlobalEnv(); std::string print() override; RegVal* make_ireg(const TypeSpec& ts, RegClass reg_class) override; void constrain_reg(IRegConstraint constraint) override; RegVal* lexical_lookup(goos::Object sym) override; BlockEnv* find_block(const std::string& name) override; ~GlobalEnv() = default; FileEnv* add_file(std::string name); private: std::vector> m_files; }; /*! * An Env for an entire file (or input to the REPL) */ class FileEnv : public Env { public: FileEnv(Env* parent, std::string name); std::string print() override; void add_function(std::unique_ptr fe); void add_top_level_function(std::unique_ptr fe); void add_static(std::unique_ptr s); void debug_print_tl(); const std::vector>& functions() { return m_functions; } const std::vector>& statics() { return m_statics; } std::string get_anon_function_name() { return "anon-function-" + std::to_string(m_anon_func_counter++); } const FunctionEnv& top_level_function() { ASSERT(m_top_level_func); return *m_top_level_func; } const std::string& name() { return m_name; } bool is_empty(); ~FileEnv() = default; template T* alloc_val(Args&&... args) { std::unique_ptr new_obj = std::make_unique(std::forward(args)...); m_vals.push_back(std::move(new_obj)); return (T*)m_vals.back().get(); } int default_segment() const { return m_default_segment; } void set_debug_file() { m_default_segment = DEBUG_SEGMENT; } protected: std::string m_name; std::vector> m_functions; std::vector> m_statics; int m_anon_func_counter = 0; std::vector> m_vals; int m_default_segment = MAIN_SEGMENT; // statics FunctionEnv* m_top_level_func = nullptr; }; /*! * An Env which manages the scope for (declare ...) statements. */ class DeclareEnv : public Env { public: explicit DeclareEnv(EnvKind kind, Env* parent) : Env(kind, parent) {} virtual std::string print() = 0; ~DeclareEnv() = default; struct Settings { bool is_set = false; // has the user set these with a (declare)? bool inline_by_default = false; // if a function, inline when possible? bool save_code = true; // if a function, should we save the code? bool allow_inline = false; // should we allow the user to use this an inline function bool print_asm = false; // should we print out the asm for this function? } settings; }; class IR_GotoLabel; struct UnresolvedGoto { IR_GotoLabel* ir = nullptr; std::string label; }; class IR_ConditionalBranch; struct UnresolvedConditionalGoto { IR_ConditionalBranch* ir = nullptr; std::string label; }; class FunctionEnv : public DeclareEnv { public: FunctionEnv(Env* parent, std::string name, const goos::Reader* reader); std::string print() override; std::unordered_map& get_label_map() override; void set_segment(int seg) { segment = seg; } void emit(const goos::Object& form, std::unique_ptr ir, Env* lowest_env); void finish(); RegVal* make_ireg(const TypeSpec& ts, RegClass reg_class) override; const std::vector>& code() const { return m_code; } const std::vector& code_source() const { return m_code_debug_source; } int max_vars() const { return m_iregs.size(); } const std::vector& constraints() { return m_constraints; } void constrain(const IRegConstraint& c) { m_constraints.push_back(c); } void set_allocations(AllocationResult&& result) { m_regalloc_result = std::move(result); } RegVal* lexical_lookup(goos::Object sym) override; const AllocationResult& alloc_result() { return m_regalloc_result; } bool needs_aligned_stack() const { return m_aligned_stack_required; } void require_aligned_stack() { m_aligned_stack_required = true; } Label* alloc_unnamed_label() { m_unnamed_labels.emplace_back(std::make_unique