diff --git a/common/goos/Interpreter.cpp b/common/goos/Interpreter.cpp index 407b95220..243e61484 100644 --- a/common/goos/Interpreter.cpp +++ b/common/goos/Interpreter.cpp @@ -12,8 +12,6 @@ namespace goos { Interpreter::Interpreter(const std::string& username) { // Interpreter startup: - goal_to_goos.reset(); - // create the GOOS global environment global_environment = EnvironmentObject::make_new("global"); @@ -73,7 +71,6 @@ Interpreter::Interpreter(const std::string& username) { {">=", &Interpreter::eval_geq}, {"null?", &Interpreter::eval_null}, {"type?", &Interpreter::eval_type}, - {"current-method-type", &Interpreter::eval_current_method_type}, {"fmt", &Interpreter::eval_format}, {"error", &Interpreter::eval_error}, {"string-ref", &Interpreter::eval_string_ref}, @@ -1550,14 +1547,6 @@ Object Interpreter::eval_type(const Object& form, } } -Object Interpreter::eval_current_method_type(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)env; - vararg_check(form, args, {}, {}); - return SymbolObject::make_new(reader.symbolTable, goal_to_goos.enclosing_method_type); -} - Object Interpreter::eval_format(const Object& form, Arguments& args, const std::shared_ptr& env) { diff --git a/common/goos/Interpreter.h b/common/goos/Interpreter.h index 7fffb1af6..049fa2a74 100644 --- a/common/goos/Interpreter.h +++ b/common/goos/Interpreter.h @@ -49,13 +49,6 @@ class Interpreter { Object global_environment; Object goal_env; - // data passed from GOAL to GOOS available to any evaluation. - struct GoalToGoosData { - std::string enclosing_method_type; - - void reset() { enclosing_method_type = "#f"; } - } goal_to_goos; - private: friend class Goal; void load_goos_library(); @@ -188,9 +181,6 @@ class Interpreter { Object eval_type(const Object& form, Arguments& args, const std::shared_ptr& env); - Object eval_current_method_type(const Object& form, - Arguments& args, - const std::shared_ptr& env); Object eval_format(const Object& form, Arguments& args, const std::shared_ptr& env); diff --git a/common/versions.h b/common/versions.h index 5e99f048b..de8cf234d 100644 --- a/common/versions.h +++ b/common/versions.h @@ -10,7 +10,7 @@ namespace versions { // language version (OpenGOAL) constexpr s32 GOAL_VERSION_MAJOR = 0; -constexpr s32 GOAL_VERSION_MINOR = 8; +constexpr s32 GOAL_VERSION_MINOR = 9; constexpr int DECOMPILER_VERSION = 4; diff --git a/docs/progress-notes/changelog.md b/docs/progress-notes/changelog.md index b55e67b62..46753db89 100644 --- a/docs/progress-notes/changelog.md +++ b/docs/progress-notes/changelog.md @@ -214,4 +214,9 @@ - Asm ops requiring 128-bit inputs will now try harder to convert their inputs when it is appropriate. - 0's that are constant propagated to the input of a 128-bit instruction will use `vpxor` instruction to generate the value, instead of `xor` and a `mov`. - Add a `stack-singleton-no-clear` stack construction type. It will create a "singleton" inside this function - all other `(new 'stack-singleton` forms with the same type will return the same stack object. -- Added support for using `(new 'static 'array ...)` for setting a static field of type `(pointer ...)` \ No newline at end of file +- Added support for using `(new 'static 'array ...)` for setting a static field of type `(pointer ...)` + +## V0.9 Large change to macro expansion and constant propagation +The compiler is now much more aggressive in where and how it expands macros and handles expressions at compiler time. +- Several places where macros could be incorrectly executed more than once (possibly causing unwanted side effects) have been fixed. +- Fixed bug in size calculation of non-inline stack arrays. Previous behavior was a compiler assert. \ No newline at end of file diff --git a/game/graphics/opengl_renderer/DirectRenderer.cpp b/game/graphics/opengl_renderer/DirectRenderer.cpp index fe8b150f6..410731f7e 100644 --- a/game/graphics/opengl_renderer/DirectRenderer.cpp +++ b/game/graphics/opengl_renderer/DirectRenderer.cpp @@ -617,7 +617,7 @@ void DirectRenderer::render_gif(const u8* data, } if (size != UINT32_MAX) { - if (!(offset + 15) / 16 == size / 16) { + if ((offset + 15) / 16 != size / 16) { fmt::print("DirectRenderer size failed in {}\n", name_and_id()); fmt::print("expected: {}, got: {}\n", size, offset); ASSERT(false); diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index cdd1020c3..a359c54cd 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -799,8 +799,8 @@ (defmacro object-new (allocation type-to-make &rest sz) (if (null? sz) - `(the ,(current-method-type) ((method-of-type object new) ,allocation ,type-to-make (the int (-> ,type-to-make size)))) - `(the ,(current-method-type) ((method-of-type object new) ,allocation ,type-to-make ,@sz)) + `(the (current-method-type) ((method-of-type object new) ,allocation ,type-to-make (the int (-> ,type-to-make size)))) + `(the (current-method-type) ((method-of-type object new) ,allocation ,type-to-make ,@sz)) ) ) diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 4b0c61fc5..72dfd374f 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(compiler compiler/compilation/Asm.cpp compiler/compilation/Atoms.cpp compiler/compilation/CompilerControl.cpp + compiler/compilation/ConstantPropagation.cpp compiler/compilation/Block.cpp compiler/compilation/Macro.cpp compiler/compilation/Math.cpp diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 86040ceb5..6766d3db7 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -8,6 +8,7 @@ #include "goalc/regalloc/Allocator.h" #include "goalc/regalloc/Allocator_v2.h" #include "third-party/fmt/core.h" +#include "common/goos/PrettyPrinter.h" using namespace goos; @@ -184,11 +185,6 @@ Val* Compiler::compile_error_guard(const goos::Object& code, Env* env) { return compile(code, env); } catch (CompilerException& ce) { if (ce.print_err_stack) { - auto obj_print = code.print(); - if (obj_print.length() > 80) { - obj_print = obj_print.substr(0, 80); - obj_print += "..."; - } bool term; auto loc_info = m_goos.reader.db.get_info_for(code, &term); if (term) { @@ -197,7 +193,7 @@ Val* Compiler::compile_error_guard(const goos::Object& code, Env* env) { } fmt::print(fg(fmt::color::yellow) | fmt::emphasis::bold, "Code:\n"); - fmt::print("{}\n", obj_print); + fmt::print("{}\n", pretty_print::to_string(code, 120)); if (term) { ce.print_err_stack = false; @@ -212,12 +208,6 @@ Val* Compiler::compile_error_guard(const goos::Object& code, Env* env) { catch (std::runtime_error& e) { fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Compilation Error! --\n"); fmt::print(fmt::emphasis::bold, "{}\n", e.what()); - - auto obj_print = code.print(); - if (obj_print.length() > 80) { - obj_print = obj_print.substr(0, 80); - obj_print += "..."; - } bool term; auto loc_info = m_goos.reader.db.get_info_for(code, &term); if (term) { @@ -226,7 +216,7 @@ Val* Compiler::compile_error_guard(const goos::Object& code, Env* env) { } fmt::print(fg(fmt::color::yellow) | fmt::emphasis::bold, "Code:\n"); - fmt::print("{}\n", obj_print); + fmt::print("{}\n", pretty_print::to_string(code, 120)); CompilerException ce("Compiler Exception"); if (term) { diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 51d70a93f..7bd3d4abf 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -34,6 +34,8 @@ class Compiler { const goos::Object& code, Env* env); Val* compile(const goos::Object& code, Env* env); + Val* compile_no_const_prop(const goos::Object& code, Env* env); + Val* compile_error_guard(const goos::Object& code, Env* env); None* get_none() { return m_none.get(); } std::vector run_test_from_file(const std::string& source_code); @@ -212,7 +214,7 @@ class Compiler { const Val* actual, const std::string& error_message = ""); - TypeSpec parse_typespec(const goos::Object& src); + TypeSpec parse_typespec(const goos::Object& src, Env* env); bool is_local_symbol(const goos::Object& obj, Env* env); emitter::HWRegKind get_preferred_reg_kind(const TypeSpec& ts); Val* compile_real_function_call(const goos::Object& form, @@ -221,8 +223,10 @@ class Compiler { Env* env, const std::string& method_type_name = ""); - bool try_getting_constant_integer(const goos::Object& in, int64_t* out, Env* env); - bool try_getting_constant_float(const goos::Object& in, float* out, Env* env); + s64 get_constant_integer_or_error(const goos::Object& in, Env* env); + ValOrConstInt get_constant_integer_or_variable(const goos::Object& in, Env* env); + ValOrConstFloat get_constant_float_or_variable(const goos::Object& in, Env* env); + Val* compile_heap_new(const goos::Object& form, const std::string& allocation, const goos::Object& type, @@ -385,7 +389,9 @@ class Compiler { int get_size_for_size_of(const goos::Object& form, const goos::Object& rest); template - void throw_compiler_error(const goos::Object& code, const std::string& str, Args&&... args) { + [[noreturn]] void throw_compiler_error(const goos::Object& code, + const std::string& str, + Args&&... args) { fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Compilation Error! --\n"); if (!str.empty() && str.back() == '\n') { fmt::print(fmt::emphasis::bold, str, std::forward(args)...); @@ -399,7 +405,7 @@ class Compiler { } template - void throw_compiler_error_no_code(const std::string& str, Args&&... args) { + [[noreturn]] void throw_compiler_error_no_code(const std::string& str, Args&&... args) { fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Compilation Error! --\n"); if (!str.empty() && str.back() == '\n') { fmt::print(fmt::emphasis::bold, str, std::forward(args)...); @@ -431,6 +437,13 @@ class Compiler { Val*& enter_val); public: + struct ConstPropResult { + goos::Object value; + bool has_side_effects = true; + }; + ConstPropResult try_constant_propagation(const goos::Object& form, Env* env); + ConstPropResult constant_propagation_dispatch(const goos::Object& form, Env* env); + // Asm Val* compile_rlet(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_ret(const goos::Object& form, const goos::Object& rest, Env* env); @@ -441,8 +454,6 @@ class Compiler { Val* compile_asm_load_sym(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_jr(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_mov(const goos::Object& form, const goos::Object& rest, Env* env); - Val* compile_asm_movn(const goos::Object& form, const goos::Object& rest, Env* env); - Val* compile_asm_slt(const goos::Object& form, const goos::Object& rest, Env* env); // Vector Float Operations Val* compile_asm_lvf(const goos::Object& form, const goos::Object& rest, Env* env); @@ -541,6 +552,7 @@ class Compiler { // Block Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env); + ConstPropResult const_prop_begin(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_block(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_return_from(const goos::Object& form, const goos::Object& rest, Env* env); @@ -604,6 +616,7 @@ class Compiler { // Macro Val* compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env); + ConstPropResult const_prop_gscond(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_quote(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_defconstant(const goos::Object& form, const goos::Object& rest, Env* env); @@ -652,6 +665,7 @@ class Compiler { Val* compile_none(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_defenum(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_size_of(const goos::Object& form, const goos::Object& rest, Env* env); + ConstPropResult const_prop_size_of(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_psize_of(const goos::Object& form, const goos::Object& rest, Env* env); // State diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index 80be666c9..eb8751aa4 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -124,7 +124,11 @@ void Compiler::expect_empty_list(const goos::Object& o) { } } -TypeSpec Compiler::parse_typespec(const goos::Object& src) { +TypeSpec Compiler::parse_typespec(const goos::Object& src, Env* env) { + if (src.is_pair() && src.as_pair()->car.is_symbol("current-method-type") && + src.as_pair()->cdr.is_empty_list()) { + return env->function_env()->method_of_type_name; + } return ::parse_typespec(&m_ts, src); } @@ -182,58 +186,6 @@ bool Compiler::is_pair(const TypeSpec& ts) { return m_ts.tc(m_ts.make_typespec("pair"), ts); } -bool Compiler::try_getting_constant_integer(const goos::Object& in, int64_t* out, Env* env) { - (void)env; - if (in.is_int()) { - *out = in.as_int(); - return true; - } - - if (in.is_pair()) { - auto head = in.as_pair()->car; - if (head.is_symbol()) { - auto head_sym = head.as_symbol(); - auto enum_type = m_ts.try_enum_lookup(head_sym->name); - if (enum_type) { - bool success; - u64 as_enum = enum_lookup(in, enum_type, in.as_pair()->cdr, false, &success); - if (success) { - *out = as_enum; - return true; - } - } - - if (head_sym->name == "size-of") { - *out = get_size_for_size_of(in, in.as_pair()->cdr); - return true; - } - } - } - - if (in.is_symbol()) { - auto global_constant = m_global_constants.find(in.as_symbol()); - if (global_constant != m_global_constants.end()) { - // recursively get constant integer, so we can have constants set to constants, etc. - if (try_getting_constant_integer(global_constant->second, out, env)) { - return true; - } - } - } - - return false; -} - -bool Compiler::try_getting_constant_float(const goos::Object& in, float* out, Env* env) { - (void)env; - if (in.is_float()) { - *out = in.as_float(); - return true; - } - - // todo, try more things like constants before giving up. - return false; -} - bool Compiler::get_true_or_false(const goos::Object& form, const goos::Object& boolean) { // todo try other things. if (boolean.is_symbol()) { diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index 94a19c8b9..db8e4cc70 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -293,3 +293,18 @@ class BitFieldVal : public Val { bool m_sign_extend = false; bool m_use_128 = false; }; + +template +struct ValOrConstant { + explicit ValOrConstant(const T& c) : constant(c), val(nullptr) {} + explicit ValOrConstant(Val* v) : val(v) {} + + T constant; + Val* val = nullptr; + + bool is_constant() const { return val == nullptr; } + bool is_variable() const { return val != nullptr; } +}; + +using ValOrConstInt = ValOrConstant; +using ValOrConstFloat = ValOrConstant; \ No newline at end of file diff --git a/goalc/compiler/compilation/Asm.cpp b/goalc/compiler/compilation/Asm.cpp index 091ad8e89..ecbef4fbf 100644 --- a/goalc/compiler/compilation/Asm.cpp +++ b/goalc/compiler/compilation/Asm.cpp @@ -54,7 +54,7 @@ Val* Compiler::compile_rlet(const goos::Object& form, const goos::Object& rest, // get the type of the new place TypeSpec ts = m_ts.make_typespec("object"); if (def_args.has_named("type")) { - ts = parse_typespec(def_args.named.at("type")); + ts = parse_typespec(def_args.named.at("type"), env); } // figure out the class @@ -590,11 +590,7 @@ Val* Compiler::compile_asm_int128_math2_imm_u8(const goos::Object& form, auto dest = compile_error_guard(args.unnamed.at(0), env)->to_reg(form, env); auto src = compile_error_guard(args.unnamed.at(1), env)->to_xmm128(form, env); - s64 imm; - if (!try_getting_constant_integer(args.unnamed.at(2), &imm, env)) { - throw_compiler_error(form, "Could not evaluate {} as a compile-time integer.", - args.unnamed.at(2).print()); - } + s64 imm = get_constant_integer_or_error(args.unnamed.at(2), env); if (imm < 0 || imm > 255) { throw_compiler_error(form, "Immediate {} is invalid. The value {} is out of range for a uint8.", diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 6cbdae333..49df6d10a 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -262,10 +262,7 @@ const std::unordered_map< {"define-virtual-state-hook", &Compiler::compile_define_virtual_state_hook}, }; -/*! - * Highest level compile function - */ -Val* Compiler::compile(const goos::Object& code, Env* env) { +Val* Compiler::compile_no_const_prop(const goos::Object& code, Env* env) { switch (code.type) { case goos::ObjectType::PAIR: return compile_pair(code, env); @@ -285,6 +282,14 @@ Val* Compiler::compile(const goos::Object& code, Env* env) { return get_none(); } +/*! + * Highest level compile function + */ +Val* Compiler::compile(const goos::Object& code, Env* env) { + auto propagated = try_constant_propagation(code, env); + return compile_no_const_prop(propagated.value, env); +} + /*! * Compile a pair/list. * Can be a compiler form, function call (possibly inlined), method call, immediate application of a @@ -297,18 +302,18 @@ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { if (head.is_symbol()) { auto head_sym = head.as_symbol(); - // first try as a goal compiler form - auto kv_gfs = g_goal_forms.find(head_sym->name); - if (kv_gfs != g_goal_forms.end()) { - return ((*this).*(kv_gfs->second))(code, rest, env); - } - - // next try as a macro + // first try as a macro goos::Object macro_obj; if (try_getting_macro_from_goos(head, ¯o_obj)) { return compile_goos_macro(code, macro_obj, rest, head, env); } + // next try as a goal compiler form + auto kv_gfs = g_goal_forms.find(head_sym->name); + if (kv_gfs != g_goal_forms.end()) { + return ((*this).*(kv_gfs->second))(code, rest, env); + } + // next try as an enum auto enum_type = m_ts.try_enum_lookup(head_sym->name); if (enum_type) { @@ -384,6 +389,7 @@ Val* Compiler::compile_get_symbol_value(const goos::Object& form, /*! * Compile a symbol. Can get mlet macro symbols, local variables, constants, or symbols. + * Note: order of checks here should match try_constant_propagation */ Val* Compiler::compile_symbol(const goos::Object& form, Env* env) { auto name = symbol_string(form); diff --git a/goalc/compiler/compilation/ConstantPropagation.cpp b/goalc/compiler/compilation/ConstantPropagation.cpp new file mode 100644 index 000000000..900baa400 --- /dev/null +++ b/goalc/compiler/compilation/ConstantPropagation.cpp @@ -0,0 +1,319 @@ +#include "goalc/compiler/Compiler.h" + +/*! + * Main table for compiler forms that can be constant propagated. + */ +const std::unordered_map + g_const_prop_forms = { + // INLINE ASM + {"begin", &Compiler::const_prop_begin}, + {"size-of", &Compiler::const_prop_size_of}, + {"#cond", &Compiler::const_prop_gscond}}; + +// Note: writing const_prop functions is a bit tricky because you have to try expanding macros, but +// if you decide that you can't constant propagate, then there's no way to "undo" any side effects +// from the macro expansion. So the solution is to return a form with all macro expansions already +// applied. + +// The result should be a goos Object that is either code to be compiled, or some constant +// integer/string/float/symbol. The const prop functions should emit no code. + +/*! + * Constant propagate a form like: + * (begin a b c d ...) + * The head doesn't have to be "begin" and it will still work (the form argument is ignored). + * + * If constant propagation fails, it will return a form like + * (begin a c ..) + * where the head is always begin (even if it wasn't originally), and some of a, b, c... + * may be macro expanded, or omitted if they have no side effects. + * + * If constant propagation succeeds (the expression is a compile time constant with no side effects) + * it will return a single value. The other values don't matter at all. + * + * This applies constant propagation recursively, so elements may be macro expanded and nested + * begins can be eliminated. + * + * This function can generally be used to constant propagate any "body" of code. + */ +Compiler::ConstPropResult Compiler::const_prop_begin(const goos::Object& /*form*/, + const goos::Object& rest, + Env* env) { + ConstPropResult result; + result.has_side_effects = false; + result.value = goos::PairObject::make_new({}, {}); + goos::Object* out_it = &result.value.as_pair()->cdr; + const goos::Object* it = &rest; + while (!it->is_empty_list()) { + const goos::Object& obj = it->as_pair()->car; + + auto this_elt_prop = + result.has_side_effects ? ConstPropResult{obj, true} : try_constant_propagation(obj, env); + if (this_elt_prop.has_side_effects) { + result.has_side_effects = true; + } + it = &it->as_pair()->cdr; + if (it->is_empty_list() && !result.has_side_effects) { + // can throw out the begin and replace it with the last thing + return this_elt_prop; + } + + if (this_elt_prop.has_side_effects) { + *out_it = goos::PairObject::make_new(this_elt_prop.value, {}); + out_it = &out_it->as_pair()->cdr; + } + } + + result.value.as_pair()->car = m_goos.intern("begin"); + *out_it = goos::Object::make_empty_list(); + + return result; +} + +/*! + * Constant propagate a #cond form. + * This will always evaluate the actual conditions. + * In cases where we return none, it gives up constant propagation, as we can't really do anything + * with that. + * In other cases, it tries to constant propagate the body of the matching case. + */ +Compiler::ConstPropResult Compiler::const_prop_gscond(const goos::Object& form, + const goos::Object& rest, + Env* env) { + if (!rest.is_pair()) { + throw_compiler_error(form, "#cond must have at least one clause, which must be a form"); + } + + goos::Object lst = rest; + for (;;) { + if (lst.is_pair()) { + goos::Object current_case = lst.as_pair()->car; + if (!current_case.is_pair()) { + throw_compiler_error(lst, "Bad case in #cond"); + } + + // check condition: + goos::Object condition_result = m_goos.eval_with_rewind( + current_case.as_pair()->car, m_goos.global_environment.as_env_ptr()); + if (m_goos.truthy(condition_result)) { + if (current_case.as_pair()->cdr.is_empty_list()) { + // would return none, let's just return that this has side effects and let the compiler + // handle it. + return {form, true}; + } + // got a match! + return const_prop_begin(current_case, current_case.as_pair()->cdr, env); + } else { + // no match, continue. + lst = lst.as_pair()->cdr; + } + } else if (lst.is_empty_list()) { + return {form, true}; + } else { + throw_compiler_error(form, "malformed #cond"); + } + } +} + +namespace { +size_t code_size(Env* e) { + auto fe = e->function_env(); + if (fe) { + return fe->code().size(); + } else { + return 0; + } +} +} // namespace + +Compiler::ConstPropResult Compiler::try_constant_propagation(const goos::Object& form, Env* env) { + size_t start_size = code_size(env); + auto ret = constant_propagation_dispatch(form, env); + size_t end_size = code_size(env); + if (start_size != end_size) { + fmt::print("Compiler bug in constant propagation. Code was generated: {} vs {}\n", start_size, + end_size); + ASSERT(false); + } + return ret; +} + +/*! + * Main constant propagation dispatch. + * Note that there are some tricky orders to get right here - we don't want to use a global constant + * when the normal compiler would use a lexical variable. + */ +Compiler::ConstPropResult Compiler::constant_propagation_dispatch(const goos::Object& code, + Env* env) { + // first, expand macros. + // this only does something if code is a pair, and for pairs macros are the first check. + auto expanded = expand_macro_completely(code, env); + + switch (expanded.type) { + case goos::ObjectType::INTEGER: + case goos::ObjectType::STRING: + case goos::ObjectType::FLOAT: + // we got a plain value, no code is needed to figure out the value and we can return it + // directly. + return {expanded, false}; + case goos::ObjectType::SYMBOL: { + // NOTE: order must match compile_symbol + // #t/#f + // mlet + // lexical + // constant/symbol + + // first, try to resolve the symbol in an mlet environment. + auto mlet_env = env->symbol_macro_env(); + while (mlet_env) { + auto mlkv = mlet_env->macros.find(expanded.as_symbol()); + if (mlkv != mlet_env->macros.end()) { + // we found a match, substitute and keep trying. + return try_constant_propagation(mlkv->second, env); + } + mlet_env = mlet_env->parent()->symbol_macro_env(); + } + + // see if it's a local variable + auto lexical = env->lexical_lookup(expanded); + if (lexical) { + // if so, that's what we should use, not a constant with the same name. + // give up on constant propagation, we never do it for lexicals (can't really in a single + // pass). + return {expanded, true}; + } + + // it can either be a global or symbol + const auto& global_constant = m_global_constants.find(expanded.as_symbol()); + const auto& existing_symbol = m_symbol_types.find(expanded.as_symbol()->name); + + // see if it's a constant + if (global_constant != m_global_constants.end()) { + // check there is no symbol with the same name, this is likely a bug and complain. + if (existing_symbol != m_symbol_types.end()) { + throw_compiler_error( + code, + "Ambiguous symbol: {} is both a global variable and a constant and it " + "is not clear which should be used here."); + } + + // got a global constant + return try_constant_propagation(global_constant->second, env); + } else { + // return to the compiler, we can't figure it out. + return {expanded, true}; + } + } break; + + case goos::ObjectType::PAIR: { + auto pair = expanded.as_pair(); + auto head = pair->car; + auto rest = pair->cdr; + + // in theory you could write code like: + // ((#if PC_PORT foo bar) ...) + // and you might want the compiler to constant propagate (foo ...) + // but this is not implemented because the logic in compile_function_or_method_call + // is quite complicated and this case seems unlikely to ever be used. + + if (head.is_symbol()) { + auto head_sym = head.as_symbol(); + // the first thing tried should be macros, but we already did that. And it iterates until + // all are expanded, so we don't need to do it again. + + // first try as a goal compiler form + auto kv_gfs = g_const_prop_forms.find(head_sym->name); + if (kv_gfs != g_const_prop_forms.end()) { + return ((*this).*(kv_gfs->second))(expanded, rest, env); + } + + const auto& kv_goal = g_goal_forms.find(head_sym->name); + if (kv_goal != g_goal_forms.end()) { + // it's a compiler form that we can't constant propagate. + return {expanded, true}; + } + } + + return {expanded, true}; + } + default: + return {expanded, true}; + } +} + +s64 Compiler::get_constant_integer_or_error(const goos::Object& in, Env* env) { + auto prop = try_constant_propagation(in, env); + if (prop.value.is_pair()) { + auto head = prop.value.as_pair()->car; + if (head.is_symbol()) { + auto head_sym = head.as_symbol(); + auto enum_type = m_ts.try_enum_lookup(head_sym->name); + if (enum_type) { + bool success; + u64 as_enum = + enum_lookup(prop.value, enum_type, prop.value.as_pair()->cdr, false, &success); + if (success) { + return as_enum; + } + } + } + } + + if (prop.has_side_effects) { + throw_compiler_error(in, "Value {} cannot be used as a constant - it has side effects.", + in.print()); + } else { + if (prop.value.is_int()) { + return prop.value.as_int(); + } else { + throw_compiler_error( + in, "Value {} cannot be used as a constant integer - it has the wrong type.", in.print()); + } + } +} + +ValOrConstInt Compiler::get_constant_integer_or_variable(const goos::Object& in, Env* env) { + auto prop = try_constant_propagation(in, env); + + if (prop.value.is_pair()) { + auto head = prop.value.as_pair()->car; + if (head.is_symbol()) { + auto head_sym = head.as_symbol(); + auto enum_type = m_ts.try_enum_lookup(head_sym->name); + if (enum_type) { + bool success; + u64 as_enum = + enum_lookup(prop.value, enum_type, prop.value.as_pair()->cdr, false, &success); + if (success) { + return ValOrConstInt(as_enum); + } + } + } + } + + if (prop.has_side_effects) { + return ValOrConstInt(compile_no_const_prop(prop.value, env)); + } else { + if (prop.value.is_int()) { + return ValOrConstInt(prop.value.as_int()); + } else { + return ValOrConstInt(compile_no_const_prop(prop.value, env)); + } + } +} + +ValOrConstFloat Compiler::get_constant_float_or_variable(const goos::Object& in, Env* env) { + auto prop = try_constant_propagation(in, env); + if (prop.has_side_effects) { + return ValOrConstFloat(compile_no_const_prop(prop.value, env)); + } else { + if (prop.value.is_float()) { + return ValOrConstFloat(prop.value.as_float()); + } else { + return ValOrConstFloat(compile_no_const_prop(prop.value, env)); + } + } +} \ No newline at end of file diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index d3cb1fdc7..313d5f5a3 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -76,7 +76,7 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec auto& sym = args.unnamed.at(0); auto& typespec = args.unnamed.at(1); - auto new_type = parse_typespec(typespec); + auto new_type = parse_typespec(typespec, env); auto existing_type = m_symbol_types.find(symbol_string(sym)); if (existing_type != m_symbol_types.end() && existing_type->second != new_type) { diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp index bdf1d77e0..70691a21c 100644 --- a/goalc/compiler/compilation/Function.cpp +++ b/goalc/compiler/compilation/Function.cpp @@ -71,7 +71,7 @@ Val* Compiler::compile_local_vars(const goos::Object& form, const goos::Object& auto param_args = get_va(o, o); va_check(o, param_args, {goos::ObjectType::SYMBOL, {}}, {}); auto name = symbol_string(param_args.unnamed.at(0)); - auto type = parse_typespec(param_args.unnamed.at(1)); + auto type = parse_typespec(param_args.unnamed.at(1), env); if (fe->params.find(name) != fe->params.end()) { throw_compiler_error(form, "Cannot declare a local named {}, this already exists.", name); @@ -129,7 +129,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest GoalArg parm; parm.name = symbol_string(param_args.unnamed.at(0)); - parm.type = parse_typespec(param_args.unnamed.at(1)); + parm.type = parse_typespec(param_args.unnamed.at(1), env); lambda.params.push_back(parm); lambda_ts.add_arg(parm.type); @@ -687,7 +687,7 @@ Val* Compiler::compile_declare(const goos::Object& form, const goos::Object& res throw_compiler_error( form, "Declare asm-func must provide the function's return type as an argument."); } - fe->asm_func_return_type = parse_typespec(rrest->as_pair()->car); + fe->asm_func_return_type = parse_typespec(rrest->as_pair()->car, env); if (!rrest->as_pair()->cdr.is_empty_list()) { throw_compiler_error(first, "Invalid asm-func declare"); } diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index 13cb7e569..4d6717755 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -35,11 +35,9 @@ Val* Compiler::compile_goos_macro(const goos::Object& o, auto mac_env = mac_env_obj.as_env_ptr(); mac_env->parent_env = m_goos.global_environment.as_env_ptr(); m_goos.set_args_in_env(o, args, macro->args, mac_env); - m_goos.goal_to_goos.enclosing_method_type = env->function_env()->method_of_type_name; auto goos_result = m_goos.eval_list_return_last(macro->body, macro->body, mac_env); // make the macro expanded form point to the source where the macro was used for error messages. // m_goos.reader.db.inherit_info(o, goos_result); - m_goos.goal_to_goos.reset(); auto compile_env_for_macro = env->function_env()->alloc_env(env, name.as_symbol(), macro->body, o); @@ -267,7 +265,7 @@ bool Compiler::expand_macro_once(const goos::Object& src, goos::Object* out, Env auto goos_result = m_goos.eval_list_return_last(macro->body, macro->body, mac_env); // make the macro expanded form point to the source where the macro was used for error messages. - // m_goos.reader.db.inherit_info(src, goos_result); + m_goos.reader.db.inherit_info(src, goos_result); *out = goos_result; return true; diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index 810520072..d4ca7cc8a 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -530,14 +530,15 @@ Val* Compiler::compile_shl(const goos::Object& form, const goos::Object& rest, E auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(form, env); - int64_t constant_sa = -1; - if (try_getting_constant_integer(args.unnamed.at(1), &constant_sa, env)) { - if (constant_sa < 0 || constant_sa > 64) { + + auto sa = get_constant_integer_or_variable(args.unnamed.at(1), env); + if (sa.is_constant()) { + if (sa.constant < 0 || sa.constant > 64) { throw_compiler_error(form, "Cannot shift by more than 64, or by a negative amount."); } - return compile_fixed_shift(form, first, constant_sa, env, IntegerMathKind::SHL_64); + return compile_fixed_shift(form, first, sa.constant, env, IntegerMathKind::SHL_64); } else { - auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(form, env); + auto second = sa.val->to_gpr(form, env); return compile_variable_shift(form, first, second, env, IntegerMathKind::SHLV_64); } } @@ -546,14 +547,15 @@ Val* Compiler::compile_shr(const goos::Object& form, const goos::Object& rest, E auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(form, env); - int64_t constant_sa = -1; - if (try_getting_constant_integer(args.unnamed.at(1), &constant_sa, env)) { - if (constant_sa < 0 || constant_sa > 64) { + + auto sa = get_constant_integer_or_variable(args.unnamed.at(1), env); + if (sa.is_constant()) { + if (sa.constant < 0 || sa.constant > 64) { throw_compiler_error(form, "Cannot shift by more than 64, or by a negative amount."); } - return compile_fixed_shift(form, first, constant_sa, env, IntegerMathKind::SHR_64); + return compile_fixed_shift(form, first, sa.constant, env, IntegerMathKind::SHR_64); } else { - auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(form, env); + auto second = sa.val->to_gpr(form, env); return compile_variable_shift(form, first, second, env, IntegerMathKind::SHRV_64); } } @@ -562,14 +564,15 @@ Val* Compiler::compile_sar(const goos::Object& form, const goos::Object& rest, E auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(form, env); - int64_t constant_sa = -1; - if (try_getting_constant_integer(args.unnamed.at(1), &constant_sa, env)) { - if (constant_sa < 0 || constant_sa > 64) { + + auto sa = get_constant_integer_or_variable(args.unnamed.at(1), env); + if (sa.is_constant()) { + if (sa.constant < 0 || sa.constant > 64) { throw_compiler_error(form, "Cannot shift by more than 64, or by a negative amount."); } - return compile_fixed_shift(form, first, constant_sa, env, IntegerMathKind::SAR_64); + return compile_fixed_shift(form, first, sa.constant, env, IntegerMathKind::SAR_64); } else { - auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(form, env); + auto second = sa.val->to_gpr(form, env); return compile_variable_shift(form, first, second, env, IntegerMathKind::SARV_64); } } diff --git a/goalc/compiler/compilation/Static.cpp b/goalc/compiler/compilation/Static.cpp index 9ac4f3042..b90633a7a 100644 --- a/goalc/compiler/compilation/Static.cpp +++ b/goalc/compiler/compilation/Static.cpp @@ -84,7 +84,7 @@ void Compiler::compile_static_structure_inline(const goos::Object& form, "Array field must be defined with (new 'static ['array, 'inline-array] type-name ...)"); } - auto array_content_type = parse_typespec(new_form.at(3)); + auto array_content_type = parse_typespec(new_form.at(3), env); if (is_inline) { if (field_info.field.type() != array_content_type) { @@ -96,11 +96,7 @@ void Compiler::compile_static_structure_inline(const goos::Object& form, m_ts.typecheck_and_throw(field_info.field.type(), array_content_type, "Array content type"); } - s64 elt_array_len; - if (!try_getting_constant_integer(new_form.at(4), &elt_array_len, env)) { - throw_compiler_error(field_value, "Array field size is invalid, got {}", - new_form.at(4).print()); - } + s64 elt_array_len = get_constant_integer_or_error(new_form.at(4), env); if (elt_array_len != field_info.field.array_size()) { throw_compiler_error(field_value, "Array field had an expected size of {} but got {}", @@ -171,7 +167,7 @@ void Compiler::compile_static_structure_inline(const goos::Object& form, "Inline field must be defined with (new 'static 'type-name ...)"); } - auto inlined_type = parse_typespec(unquote(new_form.at(2))); + auto inlined_type = parse_typespec(unquote(new_form.at(2)), env); if (inlined_type != field_info.type) { throw_compiler_error(field_value, "Cannot store a {} in an inline {}", inlined_type.print(), field_info.type.print()); @@ -321,7 +317,8 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, // dynamic_defs list below. The second pass will combine the constant and dynamic defs to build // the final value. struct DynamicDef { - goos::Object definition; + // goos::Object definition; + RegVal* value = nullptr; int field_offset, field_size; std::string field_name; // for error message TypeSpec expected_type; @@ -351,9 +348,8 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, if (is_integer(field_info.result_type) || field_info.result_type.base_type() == "pointer") { // first, try as a constant - s64 value = 0; - bool got_constant = false; - got_constant = try_getting_constant_integer(field_value, &value, env); + auto compiled_field_val = get_constant_integer_or_variable(field_value, env); + bool got_constant = compiled_field_val.is_constant(); if (!got_constant && is_bitfield(field_info.result_type) && !allow_dynamic_construction) { auto static_result = compile_static(field_value, env); if (static_result.is_constant_data()) { @@ -362,7 +358,9 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, typecheck(field_value, field_info.result_type, static_result.typespec(), "Type of static constant"); got_constant = true; - value = constant_data.value_64(); + compiled_field_val.val = nullptr; + compiled_field_val.constant = constant_data.value_64(); + // TODO: handle this in the constant propagation stuff } } } @@ -370,7 +368,7 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, // failed to get as constant, add to dynamic or error. if (allow_dynamic_construction) { DynamicDef dyn; - dyn.definition = field_value; + dyn.value = compiled_field_val.val->to_gpr(field_value, env); dyn.field_offset = field_offset; dyn.field_size = field_size; dyn.field_name = field_name_def; @@ -383,11 +381,11 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, } else { throw_compiler_error(form, "Field {} is an integer, but the value given couldn't be " - "converted to an integer at compile time.", - field_name_def); + "converted to an integer at compile time: {}", + field_name_def, field_value.print()); } } else { - u64 unsigned_value = value; + u64 unsigned_value = compiled_field_val.constant; u64 or_value = unsigned_value; ASSERT(field_size <= 64); // shift us all the way left to clear upper bits. @@ -415,12 +413,13 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, "bytes. This is probably not what you wanted to do."); } - float value = 0.f; - if (!try_getting_constant_float(field_value, &value, env)) { + // float value = 0.f; + auto float_value_or_const = get_constant_float_or_variable(field_value, env); + if (float_value_or_const.is_variable()) { // failed to get as constant, add to dynamic or error. if (allow_dynamic_construction) { DynamicDef dyn; - dyn.definition = field_value; + dyn.value = float_value_or_const.val->to_gpr(field_value, env); dyn.field_offset = field_offset; dyn.field_size = field_size; dyn.field_name = field_name_def; @@ -432,20 +431,21 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, "be converted to a float at compile time.", field_name_def); } - } - u64 float_value = float_as_u32(value); - bool start_lo = field_offset < 64; - bool end_lo = (field_offset + field_size) <= 64; - ASSERT(start_lo == end_lo); - if (end_lo) { - constant_integer_part.lo |= (float_value << field_offset); } else { - constant_integer_part.hi |= (float_value << (field_offset - 64)); + u64 float_value = float_as_u32(float_value_or_const.constant); + bool start_lo = field_offset < 64; + bool end_lo = (field_offset + field_size) <= 64; + ASSERT(start_lo == end_lo); + if (end_lo) { + constant_integer_part.lo |= (float_value << field_offset); + } else { + constant_integer_part.hi |= (float_value << (field_offset - 64)); + } } } else if (field_info.result_type == TypeSpec("symbol")) { if (allow_dynamic_construction) { DynamicDef dyn; - dyn.definition = field_value; + dyn.value = compile_error_guard(field_value, env)->to_gpr(field_value, env); dyn.field_offset = field_offset; dyn.field_size = field_size; dyn.field_name = field_name_def; @@ -459,7 +459,7 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, } else if (m_ts.tc(TypeSpec("structure"), field_info.result_type)) { if (allow_dynamic_construction) { DynamicDef dyn; - dyn.definition = field_value; + dyn.value = compile_error_guard(field_value, env)->to_gpr(field_value, env); dyn.field_offset = field_offset; dyn.field_size = field_size; dyn.field_name = field_name_def; @@ -499,7 +499,7 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, auto xmm_temp = fe->make_ireg(TypeSpec("object"), RegClass::INT_128); for (auto& def : dynamic_defs) { - auto field_val_in = compile_error_guard(def.definition, env)->to_gpr(def.definition, env); + auto field_val_in = def.value; auto field_val = env->make_gpr(field_val_in->type()); env->emit_ir(form, field_val, field_val_in); if (!m_ts.tc(def.expected_type, field_val->type())) { @@ -541,7 +541,7 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form, } else { RegVal* integer_reg = integer->to_gpr(form, env); for (auto& def : dynamic_defs) { - auto field_val_in = compile_error_guard(def.definition, env)->to_gpr(def.definition, env); + auto field_val_in = def.value; auto field_val = env->make_gpr(field_val_in->type()); env->emit_ir(form, field_val, field_val_in); if (!m_ts.tc(def.expected_type, field_val->type())) { @@ -735,7 +735,7 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env } else if (unquote(args.at(1)).as_symbol()->name == "inline-array") { return fill_static_inline_array(form, rest, env, segment); } else { - auto ts = parse_typespec(unquote(args.at(1))); + auto ts = parse_typespec(unquote(args.at(1)), env); if (ts == TypeSpec("string")) { // (new 'static 'string) if (rest.is_pair() && rest.as_pair()->cdr.is_empty_list() && @@ -762,25 +762,21 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env } else if (first.is_symbol() && first.as_symbol()->name == "the-as") { auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); - auto type = parse_typespec(args.unnamed.at(0)); + auto type = parse_typespec(args.unnamed.at(0), env); if (type == TypeSpec("float")) { - s64 value; - if (try_getting_constant_integer(args.unnamed.at(1), &value, env)) { - if (integer_fits(value, 4, false)) { - return StaticResult::make_constant_data(value, TypeSpec("float")); - } + s64 value = get_constant_integer_or_error(args.unnamed.at(1), env); + if (integer_fits(value, 4, false)) { + return StaticResult::make_constant_data(value, TypeSpec("float")); } } } else if (first.is_symbol() && first.as_symbol()->name == "the") { auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); - auto type = parse_typespec(args.unnamed.at(0)); + auto type = parse_typespec(args.unnamed.at(0), env); if (type == TypeSpec("binteger")) { - s64 value; - if (try_getting_constant_integer(args.unnamed.at(1), &value, env)) { - if (integer_fits(value, 4, true)) { - return StaticResult::make_constant_data(value << 3, TypeSpec("binteger")); - } + s64 value = get_constant_integer_or_error(args.unnamed.at(1), env); + if (integer_fits(value, 4, true)) { + return StaticResult::make_constant_data(value << 3, TypeSpec("binteger")); } } } else if (first.is_symbol("type-ref")) { @@ -823,10 +819,8 @@ StaticResult Compiler::compile_static(const goos::Object& form_before_macro, Env return StaticResult::make_func_ref(lambda->func, lambda->type()); } else { // maybe an enum - s64 int_out; - if (try_getting_constant_integer(form, &int_out, env)) { - return StaticResult::make_constant_data(int_out, TypeSpec("int")); - } + s64 int_out = get_constant_integer_or_error(form, env); + return StaticResult::make_constant_data(int_out, TypeSpec("int")); } } @@ -884,11 +878,8 @@ StaticResult Compiler::fill_static_array(const goos::Object& form, if (args.size() < 4) { throw_compiler_error(form, "new static array must have type and min-size arguments"); } - auto content_type = parse_typespec(args.at(2)); - s64 min_size; - if (!try_getting_constant_integer(args.at(3), &min_size, env)) { - throw_compiler_error(form, "The length {} is not valid.", args.at(3).print()); - } + auto content_type = parse_typespec(args.at(2), env); + s64 min_size = get_constant_integer_or_error(args.at(3), env); s32 length = std::max(min_size, s64(args.size() - 4)); // todo - generalize this array stuff if we ever need other types of static arrays. auto pointer_type = m_ts.make_pointer_typespec(content_type); @@ -929,21 +920,16 @@ StaticResult Compiler::fill_static_boxed_array(const goos::Object& form, if (!args.has_named("type")) { throw_compiler_error(form, "boxed array must have type"); } - auto content_type = parse_typespec(args.get_named("type")); + auto content_type = parse_typespec(args.get_named("type"), env); if (!args.has_named("length")) { throw_compiler_error(form, "boxed array must have length"); } - s64 length; - if (!try_getting_constant_integer(args.get_named("length"), &length, env)) { - throw_compiler_error(form, "boxed array has invalid length"); - } + s64 length = get_constant_integer_or_error(args.get_named("length"), env); s64 allocated_length; if (args.has_named("allocated-length")) { - if (!try_getting_constant_integer(args.get_named("allocated-length"), &allocated_length, env)) { - throw_compiler_error(form, "boxed array has invalid allocated-length"); - } + allocated_length = get_constant_integer_or_error(args.get_named("allocated-length"), env); } else { allocated_length = length; } @@ -1032,7 +1018,7 @@ void Compiler::fill_static_inline_array_inline(const goos::Object& form, elt_def, "Inline array element must be defined with (new 'static 'type-name ...)"); } - auto inlined_type = parse_typespec(unquote(new_form.at(2))); + auto inlined_type = parse_typespec(unquote(new_form.at(2)), env); if (inlined_type != content_type) { throw_compiler_error(elt_def, "Cannot store a {} in an inline array of {}", inlined_type.print(), content_type.print()); @@ -1056,11 +1042,8 @@ StaticResult Compiler::fill_static_inline_array(const goos::Object& form, if (args.size() < 4) { throw_compiler_error(form, "new static boxed array must have type and min-size arguments"); } - auto content_type = parse_typespec(args.at(2)); - s64 min_size; - if (!try_getting_constant_integer(args.at(3), &min_size, env)) { - throw_compiler_error(form, "The length {} is not valid.", args.at(3).print()); - } + auto content_type = parse_typespec(args.at(2), env); + s64 min_size = get_constant_integer_or_error(args.at(3), env); s32 length = std::max(min_size, s64(args.size() - 4)); auto inline_array_type = m_ts.make_inline_array_typespec(content_type); diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index ff9125643..d10cde8a0 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -463,7 +463,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _ GoalArg parm; parm.name = symbol_string(param_args.unnamed.at(0)); - parm.type = parse_typespec(param_args.unnamed.at(1)); + parm.type = parse_typespec(param_args.unnamed.at(1), env); // before substituting _type_ lambda_ts.add_arg(parm.type); @@ -754,10 +754,14 @@ Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest int64_t constant_index_value; RegVal* index_value = nullptr; + auto idx_val = get_constant_integer_or_variable(field_obj, env); + bool has_constant_idx = idx_val.is_constant(); + if (has_constant_idx) { + constant_index_value = idx_val.constant; + } - bool has_constant_idx = try_getting_constant_integer(field_obj, &constant_index_value, env); if (!has_constant_idx) { - index_value = compile_error_guard(field_obj, env)->to_gpr(form, env); + index_value = idx_val.val->to_gpr(form, env); if (!is_integer(index_value->type())) { throw_compiler_error(form, "Cannot use -> with field {}.", field_obj.print()); } @@ -890,7 +894,7 @@ Val* Compiler::compile_addr_of(const goos::Object& form, const goos::Object& res Val* Compiler::compile_the_as(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); - auto desired_ts = parse_typespec(args.unnamed.at(0)); + auto desired_ts = parse_typespec(args.unnamed.at(0), env); auto base = compile_error_guard(args.unnamed.at(1), env); auto result = env->function_env()->alloc_val(desired_ts, base); if (base->settable()) { @@ -907,7 +911,7 @@ Val* Compiler::compile_the_as(const goos::Object& form, const goos::Object& rest Val* Compiler::compile_the(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); va_check(form, args, {{}, {}}, {}); - auto desired_ts = parse_typespec(args.unnamed.at(0)); + auto desired_ts = parse_typespec(args.unnamed.at(0), env); auto base = compile_error_guard(args.unnamed.at(1), env); if (is_number(base->type())) { @@ -960,7 +964,7 @@ Val* Compiler::compile_heap_new(const goos::Object& form, bool making_boxed_array = unquote(type).as_symbol()->name == "boxed-array"; TypeSpec main_type; if (!making_boxed_array) { - main_type = parse_typespec(unquote(type)); + main_type = parse_typespec(unquote(type), env); } if (main_type == TypeSpec("inline-array") || main_type == TypeSpec("array")) { @@ -971,8 +975,8 @@ Val* Compiler::compile_heap_new(const goos::Object& form, auto count_obj = pair_car(*rest); rest = &pair_cdr(*rest); // try to get the size as a compile time constant. - int64_t constant_count = 0; - bool is_constant_size = try_getting_constant_integer(count_obj, &constant_count, env); + auto cv = get_constant_integer_or_variable(count_obj, env); + bool is_constant_size = cv.is_constant(); if (!rest->is_empty_list()) { // got extra arguments @@ -991,12 +995,12 @@ Val* Compiler::compile_heap_new(const goos::Object& form, args.push_back(compile_get_sym_obj(allocation, env)->to_reg(form, env)); if (is_constant_size) { - auto array_size = constant_count * info.stride; + auto array_size = cv.constant * info.stride; args.push_back(compile_integer(array_size, env)->to_reg(form, env)); } else { auto array_size = compile_integer(info.stride, env)->to_reg(form, env); env->emit_ir(form, IntegerMathKind::IMUL_32, array_size, - compile_error_guard(count_obj, env)->to_gpr(form, env)); + cv.val->to_gpr(form, env)); args.push_back(array_size); } @@ -1058,7 +1062,7 @@ Val* Compiler::compile_static_new(const goos::Object& form, auto result = fe->alloc_val(sr.reference(), sr.typespec()); return result; } else { - auto type_of_object = parse_typespec(unquote(type)); + auto type_of_object = parse_typespec(unquote(type), env); if (is_structure(type_of_object)) { return compile_new_static_structure_or_basic(form, type_of_object, *rest, env, env->function_env()->segment_for_static_data()); @@ -1079,7 +1083,7 @@ Val* Compiler::compile_stack_new(const goos::Object& form, Env* env, bool call_constructor, bool use_singleton) { - auto type_of_object = parse_typespec(unquote(type)); + auto type_of_object = parse_typespec(unquote(type), env); auto fe = env->function_env(); auto st_type_info = dynamic_cast(m_ts.lookup_type(type_of_object)); if (st_type_info && st_type_info->is_always_stack_singleton()) { @@ -1103,11 +1107,7 @@ Val* Compiler::compile_stack_new(const goos::Object& form, auto count_obj = pair_car(*rest); rest = &pair_cdr(*rest); // try to get the size as a compile time constant. - int64_t constant_count = 0; - bool is_constant_size = try_getting_constant_integer(count_obj, &constant_count, env); - if (!is_constant_size) { - throw_compiler_error(form, "Cannot create a dynamically sized stack array"); - } + int64_t constant_count = get_constant_integer_or_error(count_obj, env); if (constant_count <= 0) { throw_compiler_error(form, "Cannot create a stack array with size {}", constant_count); @@ -1133,8 +1133,9 @@ Val* Compiler::compile_stack_new(const goos::Object& form, return addr; } - int stride = - align(type_info->get_size_in_memory(), type_info->get_inline_array_stride_alignment()); + int stride = is_inline ? align(type_info->get_size_in_memory(), + type_info->get_inline_array_stride_alignment()) + : 4; ASSERT(stride == info.stride); int size_in_bytes = info.stride * constant_count; @@ -1433,6 +1434,12 @@ Val* Compiler::compile_size_of(const goos::Object& form, const goos::Object& res return compile_integer(get_size_for_size_of(form, rest), env); } +Compiler::ConstPropResult Compiler::const_prop_size_of(const goos::Object& form, + const goos::Object& rest, + Env* /*env*/) { + return {goos::Object::make_integer(get_size_for_size_of(form, rest)), false}; +} + Val* Compiler::compile_psize_of(const goos::Object& form, const goos::Object& rest, Env* env) { return compile_integer((get_size_for_size_of(form, rest) + 0xf) & ~0xf, env); } diff --git a/goalc/debugger/DebugInfo.cpp b/goalc/debugger/DebugInfo.cpp index 47db9a4bd..52d087027 100644 --- a/goalc/debugger/DebugInfo.cpp +++ b/goalc/debugger/DebugInfo.cpp @@ -8,7 +8,7 @@ std::string FunctionDebugInfo::disassemble_debug_info(bool* had_failure, const goos::Reader* reader) { std::string result = fmt::format("[{}]\n", name); result += disassemble_x86_function(generated_code.data(), generated_code.size(), reader, 0x10000, - 0x10000, instructions, function.get(), had_failure); + 0x10000, instructions, function.get(), had_failure, true); return result; } diff --git a/goalc/debugger/Debugger.cpp b/goalc/debugger/Debugger.cpp index b4194c4b6..897d93de8 100644 --- a/goalc/debugger/Debugger.cpp +++ b/goalc/debugger/Debugger.cpp @@ -396,7 +396,8 @@ Disassembly Debugger::disassemble_at_rip(const InstructionPointerInfo& info) { result.text += disassemble_x86_function( function_mem.data(), function_mem.size(), m_reader, m_debug_context.base + info.map_entry->start_addr + func_info->offset_in_seg, - rip + rip_offset, func_info->instructions, func_info->function.get(), &result.failed); + rip + rip_offset, func_info->instructions, func_info->function.get(), &result.failed, + false); } } else { result.failed = true; diff --git a/goalc/debugger/disassemble.cpp b/goalc/debugger/disassemble.cpp index ae3b39dae..090fb8018 100644 --- a/goalc/debugger/disassemble.cpp +++ b/goalc/debugger/disassemble.cpp @@ -81,7 +81,8 @@ std::string disassemble_x86_function(u8* data, u64 highlight_addr, const std::vector& x86_instructions, const FunctionEnv* fenv, - bool* had_failure) { + bool* had_failure, + bool print_whole_function) { std::string result; ZydisDecoder decoder; ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); @@ -207,8 +208,8 @@ std::string disassemble_x86_function(u8* data, } for (auto& line : lines) { - if (line.first >= rip_src_idx - FORM_DUMP_SIZE_REV && - line.first < rip_src_idx + FORM_DUMP_SIZE_FWD) { + if (print_whole_function || (line.first >= rip_src_idx - FORM_DUMP_SIZE_REV && + line.first < rip_src_idx + FORM_DUMP_SIZE_FWD)) { result.append(line.second); } } diff --git a/goalc/debugger/disassemble.h b/goalc/debugger/disassemble.h index ebbffbbf9..526b47e1e 100644 --- a/goalc/debugger/disassemble.h +++ b/goalc/debugger/disassemble.h @@ -34,4 +34,5 @@ std::string disassemble_x86_function(u8* data, u64 highlight_addr, const std::vector& x86_instructions, const FunctionEnv* fenv, - bool* had_failure); \ No newline at end of file + bool* had_failure, + bool print_whole_function); \ No newline at end of file diff --git a/test/test_goos.cpp b/test/test_goos.cpp index 15d53c349..746427ddd 100644 --- a/test/test_goos.cpp +++ b/test/test_goos.cpp @@ -404,17 +404,6 @@ TEST(GoosBuiltins, Null) { } } -TEST(GoosBuiltins, CurrentMethodType) { - Interpreter i; - i.goal_to_goos.enclosing_method_type = "test-type"; - EXPECT_EQ(e(i, "(current-method-type)"), "test-type"); - - i.disable_printfs(); - for (auto x : {"(current-method-type 1)"}) { - EXPECT_ANY_THROW(e(i, x)); - } -} - TEST(GoosBuiltins, Type) { Interpreter i; EXPECT_EQ(e(i, "(type? 'empty-list '())"), "#t");