[goalc] macro expansion in integer constants (#1282)

* [goalc] macro expansion in integer constants

* working

* didn't break it yet

* support conditional compilation

* fix up some more small bugs

* fix duplicate evaluation of bitfield definitions

* paranoid
This commit is contained in:
water111 2022-04-07 19:13:22 -04:00 committed by GitHub
parent 2caf75a11c
commit 1db96c72ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 501 additions and 241 deletions

View file

@ -12,8 +12,6 @@
namespace goos { namespace goos {
Interpreter::Interpreter(const std::string& username) { Interpreter::Interpreter(const std::string& username) {
// Interpreter startup: // Interpreter startup:
goal_to_goos.reset();
// create the GOOS global environment // create the GOOS global environment
global_environment = EnvironmentObject::make_new("global"); global_environment = EnvironmentObject::make_new("global");
@ -73,7 +71,6 @@ Interpreter::Interpreter(const std::string& username) {
{">=", &Interpreter::eval_geq}, {">=", &Interpreter::eval_geq},
{"null?", &Interpreter::eval_null}, {"null?", &Interpreter::eval_null},
{"type?", &Interpreter::eval_type}, {"type?", &Interpreter::eval_type},
{"current-method-type", &Interpreter::eval_current_method_type},
{"fmt", &Interpreter::eval_format}, {"fmt", &Interpreter::eval_format},
{"error", &Interpreter::eval_error}, {"error", &Interpreter::eval_error},
{"string-ref", &Interpreter::eval_string_ref}, {"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<EnvironmentObject>& 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, Object Interpreter::eval_format(const Object& form,
Arguments& args, Arguments& args,
const std::shared_ptr<EnvironmentObject>& env) { const std::shared_ptr<EnvironmentObject>& env) {

View file

@ -49,13 +49,6 @@ class Interpreter {
Object global_environment; Object global_environment;
Object goal_env; 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: private:
friend class Goal; friend class Goal;
void load_goos_library(); void load_goos_library();
@ -188,9 +181,6 @@ class Interpreter {
Object eval_type(const Object& form, Object eval_type(const Object& form,
Arguments& args, Arguments& args,
const std::shared_ptr<EnvironmentObject>& env); const std::shared_ptr<EnvironmentObject>& env);
Object eval_current_method_type(const Object& form,
Arguments& args,
const std::shared_ptr<EnvironmentObject>& env);
Object eval_format(const Object& form, Object eval_format(const Object& form,
Arguments& args, Arguments& args,
const std::shared_ptr<EnvironmentObject>& env); const std::shared_ptr<EnvironmentObject>& env);

View file

@ -10,7 +10,7 @@
namespace versions { namespace versions {
// language version (OpenGOAL) // language version (OpenGOAL)
constexpr s32 GOAL_VERSION_MAJOR = 0; constexpr s32 GOAL_VERSION_MAJOR = 0;
constexpr s32 GOAL_VERSION_MINOR = 8; constexpr s32 GOAL_VERSION_MINOR = 9;
constexpr int DECOMPILER_VERSION = 4; constexpr int DECOMPILER_VERSION = 4;

View file

@ -214,4 +214,9 @@
- Asm ops requiring 128-bit inputs will now try harder to convert their inputs when it is appropriate. - 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`. - 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. - 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 ...)` - 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.

View file

@ -617,7 +617,7 @@ void DirectRenderer::render_gif(const u8* data,
} }
if (size != UINT32_MAX) { 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("DirectRenderer size failed in {}\n", name_and_id());
fmt::print("expected: {}, got: {}\n", size, offset); fmt::print("expected: {}, got: {}\n", size, offset);
ASSERT(false); ASSERT(false);

View file

@ -799,8 +799,8 @@
(defmacro object-new (allocation type-to-make &rest sz) (defmacro object-new (allocation type-to-make &rest sz)
(if (null? 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 (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 ,@sz))
) )
) )

View file

@ -15,6 +15,7 @@ add_library(compiler
compiler/compilation/Asm.cpp compiler/compilation/Asm.cpp
compiler/compilation/Atoms.cpp compiler/compilation/Atoms.cpp
compiler/compilation/CompilerControl.cpp compiler/compilation/CompilerControl.cpp
compiler/compilation/ConstantPropagation.cpp
compiler/compilation/Block.cpp compiler/compilation/Block.cpp
compiler/compilation/Macro.cpp compiler/compilation/Macro.cpp
compiler/compilation/Math.cpp compiler/compilation/Math.cpp

View file

@ -8,6 +8,7 @@
#include "goalc/regalloc/Allocator.h" #include "goalc/regalloc/Allocator.h"
#include "goalc/regalloc/Allocator_v2.h" #include "goalc/regalloc/Allocator_v2.h"
#include "third-party/fmt/core.h" #include "third-party/fmt/core.h"
#include "common/goos/PrettyPrinter.h"
using namespace goos; using namespace goos;
@ -184,11 +185,6 @@ Val* Compiler::compile_error_guard(const goos::Object& code, Env* env) {
return compile(code, env); return compile(code, env);
} catch (CompilerException& ce) { } catch (CompilerException& ce) {
if (ce.print_err_stack) { 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; bool term;
auto loc_info = m_goos.reader.db.get_info_for(code, &term); auto loc_info = m_goos.reader.db.get_info_for(code, &term);
if (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(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) { if (term) {
ce.print_err_stack = false; 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) { catch (std::runtime_error& e) {
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Compilation Error! --\n"); fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Compilation Error! --\n");
fmt::print(fmt::emphasis::bold, "{}\n", e.what()); 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; bool term;
auto loc_info = m_goos.reader.db.get_info_for(code, &term); auto loc_info = m_goos.reader.db.get_info_for(code, &term);
if (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(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"); CompilerException ce("Compiler Exception");
if (term) { if (term) {

View file

@ -34,6 +34,8 @@ class Compiler {
const goos::Object& code, const goos::Object& code,
Env* env); Env* env);
Val* compile(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); Val* compile_error_guard(const goos::Object& code, Env* env);
None* get_none() { return m_none.get(); } None* get_none() { return m_none.get(); }
std::vector<std::string> run_test_from_file(const std::string& source_code); std::vector<std::string> run_test_from_file(const std::string& source_code);
@ -212,7 +214,7 @@ class Compiler {
const Val* actual, const Val* actual,
const std::string& error_message = ""); 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); bool is_local_symbol(const goos::Object& obj, Env* env);
emitter::HWRegKind get_preferred_reg_kind(const TypeSpec& ts); emitter::HWRegKind get_preferred_reg_kind(const TypeSpec& ts);
Val* compile_real_function_call(const goos::Object& form, Val* compile_real_function_call(const goos::Object& form,
@ -221,8 +223,10 @@ class Compiler {
Env* env, Env* env,
const std::string& method_type_name = ""); const std::string& method_type_name = "");
bool try_getting_constant_integer(const goos::Object& in, int64_t* out, Env* env); s64 get_constant_integer_or_error(const goos::Object& in, Env* env);
bool try_getting_constant_float(const goos::Object& in, float* out, 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, Val* compile_heap_new(const goos::Object& form,
const std::string& allocation, const std::string& allocation,
const goos::Object& type, const goos::Object& type,
@ -385,7 +389,9 @@ class Compiler {
int get_size_for_size_of(const goos::Object& form, const goos::Object& rest); int get_size_for_size_of(const goos::Object& form, const goos::Object& rest);
template <typename... Args> template <typename... Args>
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"); fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Compilation Error! --\n");
if (!str.empty() && str.back() == '\n') { if (!str.empty() && str.back() == '\n') {
fmt::print(fmt::emphasis::bold, str, std::forward<Args>(args)...); fmt::print(fmt::emphasis::bold, str, std::forward<Args>(args)...);
@ -399,7 +405,7 @@ class Compiler {
} }
template <typename... Args> template <typename... Args>
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"); fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "-- Compilation Error! --\n");
if (!str.empty() && str.back() == '\n') { if (!str.empty() && str.back() == '\n') {
fmt::print(fmt::emphasis::bold, str, std::forward<Args>(args)...); fmt::print(fmt::emphasis::bold, str, std::forward<Args>(args)...);
@ -431,6 +437,13 @@ class Compiler {
Val*& enter_val); Val*& enter_val);
public: 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 // Asm
Val* compile_rlet(const goos::Object& form, const goos::Object& rest, Env* env); 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); 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_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_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_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 // Vector Float Operations
Val* compile_asm_lvf(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_asm_lvf(const goos::Object& form, const goos::Object& rest, Env* env);
@ -541,6 +552,7 @@ class Compiler {
// Block // Block
Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env); 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_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_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); Val* compile_return_from(const goos::Object& form, const goos::Object& rest, Env* env);
@ -604,6 +616,7 @@ class Compiler {
// Macro // Macro
Val* compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env); 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_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_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_defconstant(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_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_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); 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); Val* compile_psize_of(const goos::Object& form, const goos::Object& rest, Env* env);
// State // State

View file

@ -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); 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); 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) { bool Compiler::get_true_or_false(const goos::Object& form, const goos::Object& boolean) {
// todo try other things. // todo try other things.
if (boolean.is_symbol()) { if (boolean.is_symbol()) {

View file

@ -293,3 +293,18 @@ class BitFieldVal : public Val {
bool m_sign_extend = false; bool m_sign_extend = false;
bool m_use_128 = false; bool m_use_128 = false;
}; };
template <typename T>
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<s64>;
using ValOrConstFloat = ValOrConstant<float>;

View file

@ -54,7 +54,7 @@ Val* Compiler::compile_rlet(const goos::Object& form, const goos::Object& rest,
// get the type of the new place // get the type of the new place
TypeSpec ts = m_ts.make_typespec("object"); TypeSpec ts = m_ts.make_typespec("object");
if (def_args.has_named("type")) { 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 // 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 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); auto src = compile_error_guard(args.unnamed.at(1), env)->to_xmm128(form, env);
s64 imm; s64 imm = get_constant_integer_or_error(args.unnamed.at(2), env);
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());
}
if (imm < 0 || imm > 255) { if (imm < 0 || imm > 255) {
throw_compiler_error(form, "Immediate {} is invalid. The value {} is out of range for a uint8.", throw_compiler_error(form, "Immediate {} is invalid. The value {} is out of range for a uint8.",

View file

@ -262,10 +262,7 @@ const std::unordered_map<
{"define-virtual-state-hook", &Compiler::compile_define_virtual_state_hook}, {"define-virtual-state-hook", &Compiler::compile_define_virtual_state_hook},
}; };
/*! Val* Compiler::compile_no_const_prop(const goos::Object& code, Env* env) {
* Highest level compile function
*/
Val* Compiler::compile(const goos::Object& code, Env* env) {
switch (code.type) { switch (code.type) {
case goos::ObjectType::PAIR: case goos::ObjectType::PAIR:
return compile_pair(code, env); return compile_pair(code, env);
@ -285,6 +282,14 @@ Val* Compiler::compile(const goos::Object& code, Env* env) {
return get_none(); 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. * Compile a pair/list.
* Can be a compiler form, function call (possibly inlined), method call, immediate application of a * 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()) { if (head.is_symbol()) {
auto head_sym = head.as_symbol(); auto head_sym = head.as_symbol();
// first try as a goal compiler form // first try as a macro
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
goos::Object macro_obj; goos::Object macro_obj;
if (try_getting_macro_from_goos(head, &macro_obj)) { if (try_getting_macro_from_goos(head, &macro_obj)) {
return compile_goos_macro(code, macro_obj, rest, head, env); 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 // next try as an enum
auto enum_type = m_ts.try_enum_lookup(head_sym->name); auto enum_type = m_ts.try_enum_lookup(head_sym->name);
if (enum_type) { 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. * 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) { Val* Compiler::compile_symbol(const goos::Object& form, Env* env) {
auto name = symbol_string(form); auto name = symbol_string(form);

View file

@ -0,0 +1,319 @@
#include "goalc/compiler/Compiler.h"
/*!
* Main table for compiler forms that can be constant propagated.
*/
const std::unordered_map<std::string,
Compiler::ConstPropResult (Compiler::*)(const goos::Object& form,
const goos::Object& rest,
Env* env)>
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));
}
}
}

View file

@ -76,7 +76,7 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec
auto& sym = args.unnamed.at(0); auto& sym = args.unnamed.at(0);
auto& typespec = args.unnamed.at(1); 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)); auto existing_type = m_symbol_types.find(symbol_string(sym));
if (existing_type != m_symbol_types.end() && existing_type->second != new_type) { if (existing_type != m_symbol_types.end() && existing_type->second != new_type) {

View file

@ -71,7 +71,7 @@ Val* Compiler::compile_local_vars(const goos::Object& form, const goos::Object&
auto param_args = get_va(o, o); auto param_args = get_va(o, o);
va_check(o, param_args, {goos::ObjectType::SYMBOL, {}}, {}); va_check(o, param_args, {goos::ObjectType::SYMBOL, {}}, {});
auto name = symbol_string(param_args.unnamed.at(0)); 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()) { if (fe->params.find(name) != fe->params.end()) {
throw_compiler_error(form, "Cannot declare a local named {}, this already exists.", name); 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; GoalArg parm;
parm.name = symbol_string(param_args.unnamed.at(0)); 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.params.push_back(parm);
lambda_ts.add_arg(parm.type); 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( throw_compiler_error(
form, "Declare asm-func must provide the function's return type as an argument."); 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()) { if (!rrest->as_pair()->cdr.is_empty_list()) {
throw_compiler_error(first, "Invalid asm-func declare"); throw_compiler_error(first, "Invalid asm-func declare");
} }

View file

@ -35,11 +35,9 @@ Val* Compiler::compile_goos_macro(const goos::Object& o,
auto mac_env = mac_env_obj.as_env_ptr(); auto mac_env = mac_env_obj.as_env_ptr();
mac_env->parent_env = m_goos.global_environment.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.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); 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. // 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.reader.db.inherit_info(o, goos_result);
m_goos.goal_to_goos.reset();
auto compile_env_for_macro = auto compile_env_for_macro =
env->function_env()->alloc_env<MacroExpandEnv>(env, name.as_symbol(), macro->body, o); env->function_env()->alloc_env<MacroExpandEnv>(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); 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. // 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; *out = goos_result;
return true; return true;

View file

@ -530,14 +530,15 @@ Val* Compiler::compile_shl(const goos::Object& form, const goos::Object& rest, E
auto args = get_va(form, rest); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); va_check(form, args, {{}, {}}, {});
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(form, env); 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)) { auto sa = get_constant_integer_or_variable(args.unnamed.at(1), env);
if (constant_sa < 0 || constant_sa > 64) { 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."); 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 { } 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); 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); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); va_check(form, args, {{}, {}}, {});
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(form, env); 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)) { auto sa = get_constant_integer_or_variable(args.unnamed.at(1), env);
if (constant_sa < 0 || constant_sa > 64) { 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."); 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 { } 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); 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); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); va_check(form, args, {{}, {}}, {});
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(form, env); 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)) { auto sa = get_constant_integer_or_variable(args.unnamed.at(1), env);
if (constant_sa < 0 || constant_sa > 64) { 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."); 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 { } 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); return compile_variable_shift(form, first, second, env, IntegerMathKind::SARV_64);
} }
} }

View file

@ -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 ...)"); "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 (is_inline) {
if (field_info.field.type() != array_content_type) { 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"); m_ts.typecheck_and_throw(field_info.field.type(), array_content_type, "Array content type");
} }
s64 elt_array_len; s64 elt_array_len = get_constant_integer_or_error(new_form.at(4), env);
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());
}
if (elt_array_len != field_info.field.array_size()) { if (elt_array_len != field_info.field.array_size()) {
throw_compiler_error(field_value, "Array field had an expected size of {} but got {}", 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 ...)"); "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) { if (inlined_type != field_info.type) {
throw_compiler_error(field_value, "Cannot store a {} in an inline {}", throw_compiler_error(field_value, "Cannot store a {} in an inline {}",
inlined_type.print(), field_info.type.print()); 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 // dynamic_defs list below. The second pass will combine the constant and dynamic defs to build
// the final value. // the final value.
struct DynamicDef { struct DynamicDef {
goos::Object definition; // goos::Object definition;
RegVal* value = nullptr;
int field_offset, field_size; int field_offset, field_size;
std::string field_name; // for error message std::string field_name; // for error message
TypeSpec expected_type; 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") { if (is_integer(field_info.result_type) || field_info.result_type.base_type() == "pointer") {
// first, try as a constant // first, try as a constant
s64 value = 0; auto compiled_field_val = get_constant_integer_or_variable(field_value, env);
bool got_constant = false; bool got_constant = compiled_field_val.is_constant();
got_constant = try_getting_constant_integer(field_value, &value, env);
if (!got_constant && is_bitfield(field_info.result_type) && !allow_dynamic_construction) { if (!got_constant && is_bitfield(field_info.result_type) && !allow_dynamic_construction) {
auto static_result = compile_static(field_value, env); auto static_result = compile_static(field_value, env);
if (static_result.is_constant_data()) { 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(), typecheck(field_value, field_info.result_type, static_result.typespec(),
"Type of static constant"); "Type of static constant");
got_constant = true; 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. // failed to get as constant, add to dynamic or error.
if (allow_dynamic_construction) { if (allow_dynamic_construction) {
DynamicDef dyn; DynamicDef dyn;
dyn.definition = field_value; dyn.value = compiled_field_val.val->to_gpr(field_value, env);
dyn.field_offset = field_offset; dyn.field_offset = field_offset;
dyn.field_size = field_size; dyn.field_size = field_size;
dyn.field_name = field_name_def; dyn.field_name = field_name_def;
@ -383,11 +381,11 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form,
} else { } else {
throw_compiler_error(form, throw_compiler_error(form,
"Field {} is an integer, but the value given couldn't be " "Field {} is an integer, but the value given couldn't be "
"converted to an integer at compile time.", "converted to an integer at compile time: {}",
field_name_def); field_name_def, field_value.print());
} }
} else { } else {
u64 unsigned_value = value; u64 unsigned_value = compiled_field_val.constant;
u64 or_value = unsigned_value; u64 or_value = unsigned_value;
ASSERT(field_size <= 64); ASSERT(field_size <= 64);
// shift us all the way left to clear upper bits. // 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."); "bytes. This is probably not what you wanted to do.");
} }
float value = 0.f; // float value = 0.f;
if (!try_getting_constant_float(field_value, &value, env)) { 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. // failed to get as constant, add to dynamic or error.
if (allow_dynamic_construction) { if (allow_dynamic_construction) {
DynamicDef dyn; 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_offset = field_offset;
dyn.field_size = field_size; dyn.field_size = field_size;
dyn.field_name = field_name_def; 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.", "be converted to a float at compile time.",
field_name_def); 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 { } 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")) { } else if (field_info.result_type == TypeSpec("symbol")) {
if (allow_dynamic_construction) { if (allow_dynamic_construction) {
DynamicDef dyn; 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_offset = field_offset;
dyn.field_size = field_size; dyn.field_size = field_size;
dyn.field_name = field_name_def; 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)) { } else if (m_ts.tc(TypeSpec("structure"), field_info.result_type)) {
if (allow_dynamic_construction) { if (allow_dynamic_construction) {
DynamicDef dyn; 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_offset = field_offset;
dyn.field_size = field_size; dyn.field_size = field_size;
dyn.field_name = field_name_def; 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); auto xmm_temp = fe->make_ireg(TypeSpec("object"), RegClass::INT_128);
for (auto& def : dynamic_defs) { 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()); auto field_val = env->make_gpr(field_val_in->type());
env->emit_ir<IR_RegSet>(form, field_val, field_val_in); env->emit_ir<IR_RegSet>(form, field_val, field_val_in);
if (!m_ts.tc(def.expected_type, field_val->type())) { if (!m_ts.tc(def.expected_type, field_val->type())) {
@ -541,7 +541,7 @@ Val* Compiler::compile_bitfield_definition(const goos::Object& form,
} else { } else {
RegVal* integer_reg = integer->to_gpr(form, env); RegVal* integer_reg = integer->to_gpr(form, env);
for (auto& def : dynamic_defs) { 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()); auto field_val = env->make_gpr(field_val_in->type());
env->emit_ir<IR_RegSet>(form, field_val, field_val_in); env->emit_ir<IR_RegSet>(form, field_val, field_val_in);
if (!m_ts.tc(def.expected_type, field_val->type())) { 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") { } else if (unquote(args.at(1)).as_symbol()->name == "inline-array") {
return fill_static_inline_array(form, rest, env, segment); return fill_static_inline_array(form, rest, env, segment);
} else { } else {
auto ts = parse_typespec(unquote(args.at(1))); auto ts = parse_typespec(unquote(args.at(1)), env);
if (ts == TypeSpec("string")) { if (ts == TypeSpec("string")) {
// (new 'static 'string) // (new 'static 'string)
if (rest.is_pair() && rest.as_pair()->cdr.is_empty_list() && 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") { } else if (first.is_symbol() && first.as_symbol()->name == "the-as") {
auto args = get_va(form, rest); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); 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")) { if (type == TypeSpec("float")) {
s64 value; s64 value = get_constant_integer_or_error(args.unnamed.at(1), env);
if (try_getting_constant_integer(args.unnamed.at(1), &value, env)) { if (integer_fits(value, 4, false)) {
if (integer_fits(value, 4, false)) { return StaticResult::make_constant_data(value, TypeSpec("float"));
return StaticResult::make_constant_data(value, TypeSpec("float"));
}
} }
} }
} else if (first.is_symbol() && first.as_symbol()->name == "the") { } else if (first.is_symbol() && first.as_symbol()->name == "the") {
auto args = get_va(form, rest); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); 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")) { if (type == TypeSpec("binteger")) {
s64 value; s64 value = get_constant_integer_or_error(args.unnamed.at(1), env);
if (try_getting_constant_integer(args.unnamed.at(1), &value, env)) { if (integer_fits(value, 4, true)) {
if (integer_fits(value, 4, true)) { return StaticResult::make_constant_data(value << 3, TypeSpec("binteger"));
return StaticResult::make_constant_data(value << 3, TypeSpec("binteger"));
}
} }
} }
} else if (first.is_symbol("type-ref")) { } 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()); return StaticResult::make_func_ref(lambda->func, lambda->type());
} else { } else {
// maybe an enum // maybe an enum
s64 int_out; s64 int_out = get_constant_integer_or_error(form, env);
if (try_getting_constant_integer(form, &int_out, env)) { return StaticResult::make_constant_data(int_out, TypeSpec("int"));
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) { if (args.size() < 4) {
throw_compiler_error(form, "new static array must have type and min-size arguments"); throw_compiler_error(form, "new static array must have type and min-size arguments");
} }
auto content_type = parse_typespec(args.at(2)); auto content_type = parse_typespec(args.at(2), env);
s64 min_size; s64 min_size = get_constant_integer_or_error(args.at(3), env);
if (!try_getting_constant_integer(args.at(3), &min_size, env)) {
throw_compiler_error(form, "The length {} is not valid.", args.at(3).print());
}
s32 length = std::max(min_size, s64(args.size() - 4)); s32 length = std::max(min_size, s64(args.size() - 4));
// todo - generalize this array stuff if we ever need other types of static arrays. // todo - generalize this array stuff if we ever need other types of static arrays.
auto pointer_type = m_ts.make_pointer_typespec(content_type); 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")) { if (!args.has_named("type")) {
throw_compiler_error(form, "boxed array must have 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")) { if (!args.has_named("length")) {
throw_compiler_error(form, "boxed array must have length"); throw_compiler_error(form, "boxed array must have length");
} }
s64 length; s64 length = get_constant_integer_or_error(args.get_named("length"), env);
if (!try_getting_constant_integer(args.get_named("length"), &length, env)) {
throw_compiler_error(form, "boxed array has invalid length");
}
s64 allocated_length; s64 allocated_length;
if (args.has_named("allocated-length")) { if (args.has_named("allocated-length")) {
if (!try_getting_constant_integer(args.get_named("allocated-length"), &allocated_length, env)) { allocated_length = get_constant_integer_or_error(args.get_named("allocated-length"), env);
throw_compiler_error(form, "boxed array has invalid allocated-length");
}
} else { } else {
allocated_length = length; 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 ...)"); 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) { if (inlined_type != content_type) {
throw_compiler_error(elt_def, "Cannot store a {} in an inline array of {}", throw_compiler_error(elt_def, "Cannot store a {} in an inline array of {}",
inlined_type.print(), content_type.print()); inlined_type.print(), content_type.print());
@ -1056,11 +1042,8 @@ StaticResult Compiler::fill_static_inline_array(const goos::Object& form,
if (args.size() < 4) { if (args.size() < 4) {
throw_compiler_error(form, "new static boxed array must have type and min-size arguments"); throw_compiler_error(form, "new static boxed array must have type and min-size arguments");
} }
auto content_type = parse_typespec(args.at(2)); auto content_type = parse_typespec(args.at(2), env);
s64 min_size; s64 min_size = get_constant_integer_or_error(args.at(3), env);
if (!try_getting_constant_integer(args.at(3), &min_size, env)) {
throw_compiler_error(form, "The length {} is not valid.", args.at(3).print());
}
s32 length = std::max(min_size, s64(args.size() - 4)); s32 length = std::max(min_size, s64(args.size() - 4));
auto inline_array_type = m_ts.make_inline_array_typespec(content_type); auto inline_array_type = m_ts.make_inline_array_typespec(content_type);

View file

@ -463,7 +463,7 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
GoalArg parm; GoalArg parm;
parm.name = symbol_string(param_args.unnamed.at(0)); 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_ // before substituting _type_
lambda_ts.add_arg(parm.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; int64_t constant_index_value;
RegVal* index_value = nullptr; 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) { 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())) { if (!is_integer(index_value->type())) {
throw_compiler_error(form, "Cannot use -> with field {}.", field_obj.print()); 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) { Val* Compiler::compile_the_as(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); 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 base = compile_error_guard(args.unnamed.at(1), env);
auto result = env->function_env()->alloc_val<AliasVal>(desired_ts, base); auto result = env->function_env()->alloc_val<AliasVal>(desired_ts, base);
if (base->settable()) { 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) { Val* Compiler::compile_the(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest); auto args = get_va(form, rest);
va_check(form, args, {{}, {}}, {}); 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 base = compile_error_guard(args.unnamed.at(1), env);
if (is_number(base->type())) { 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"; bool making_boxed_array = unquote(type).as_symbol()->name == "boxed-array";
TypeSpec main_type; TypeSpec main_type;
if (!making_boxed_array) { 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")) { 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); auto count_obj = pair_car(*rest);
rest = &pair_cdr(*rest); rest = &pair_cdr(*rest);
// try to get the size as a compile time constant. // try to get the size as a compile time constant.
int64_t constant_count = 0; auto cv = get_constant_integer_or_variable(count_obj, env);
bool is_constant_size = try_getting_constant_integer(count_obj, &constant_count, env); bool is_constant_size = cv.is_constant();
if (!rest->is_empty_list()) { if (!rest->is_empty_list()) {
// got extra arguments // 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)); args.push_back(compile_get_sym_obj(allocation, env)->to_reg(form, env));
if (is_constant_size) { 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)); args.push_back(compile_integer(array_size, env)->to_reg(form, env));
} else { } else {
auto array_size = compile_integer(info.stride, env)->to_reg(form, env); auto array_size = compile_integer(info.stride, env)->to_reg(form, env);
env->emit_ir<IR_IntegerMath>(form, IntegerMathKind::IMUL_32, array_size, env->emit_ir<IR_IntegerMath>(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); args.push_back(array_size);
} }
@ -1058,7 +1062,7 @@ Val* Compiler::compile_static_new(const goos::Object& form,
auto result = fe->alloc_val<StaticVal>(sr.reference(), sr.typespec()); auto result = fe->alloc_val<StaticVal>(sr.reference(), sr.typespec());
return result; return result;
} else { } else {
auto type_of_object = parse_typespec(unquote(type)); auto type_of_object = parse_typespec(unquote(type), env);
if (is_structure(type_of_object)) { if (is_structure(type_of_object)) {
return compile_new_static_structure_or_basic(form, type_of_object, *rest, env, return compile_new_static_structure_or_basic(form, type_of_object, *rest, env,
env->function_env()->segment_for_static_data()); env->function_env()->segment_for_static_data());
@ -1079,7 +1083,7 @@ Val* Compiler::compile_stack_new(const goos::Object& form,
Env* env, Env* env,
bool call_constructor, bool call_constructor,
bool use_singleton) { 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 fe = env->function_env();
auto st_type_info = dynamic_cast<StructureType*>(m_ts.lookup_type(type_of_object)); auto st_type_info = dynamic_cast<StructureType*>(m_ts.lookup_type(type_of_object));
if (st_type_info && st_type_info->is_always_stack_singleton()) { 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); auto count_obj = pair_car(*rest);
rest = &pair_cdr(*rest); rest = &pair_cdr(*rest);
// try to get the size as a compile time constant. // try to get the size as a compile time constant.
int64_t constant_count = 0; int64_t constant_count = get_constant_integer_or_error(count_obj, env);
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");
}
if (constant_count <= 0) { if (constant_count <= 0) {
throw_compiler_error(form, "Cannot create a stack array with size {}", constant_count); 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; return addr;
} }
int stride = int stride = is_inline ? align(type_info->get_size_in_memory(),
align(type_info->get_size_in_memory(), type_info->get_inline_array_stride_alignment()); type_info->get_inline_array_stride_alignment())
: 4;
ASSERT(stride == info.stride); ASSERT(stride == info.stride);
int size_in_bytes = info.stride * constant_count; 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); 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) { 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); return compile_integer((get_size_for_size_of(form, rest) + 0xf) & ~0xf, env);
} }

View file

@ -8,7 +8,7 @@ std::string FunctionDebugInfo::disassemble_debug_info(bool* had_failure,
const goos::Reader* reader) { const goos::Reader* reader) {
std::string result = fmt::format("[{}]\n", name); std::string result = fmt::format("[{}]\n", name);
result += disassemble_x86_function(generated_code.data(), generated_code.size(), reader, 0x10000, 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; return result;
} }

View file

@ -396,7 +396,8 @@ Disassembly Debugger::disassemble_at_rip(const InstructionPointerInfo& info) {
result.text += disassemble_x86_function( result.text += disassemble_x86_function(
function_mem.data(), function_mem.size(), m_reader, function_mem.data(), function_mem.size(), m_reader,
m_debug_context.base + info.map_entry->start_addr + func_info->offset_in_seg, 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 { } else {
result.failed = true; result.failed = true;

View file

@ -81,7 +81,8 @@ std::string disassemble_x86_function(u8* data,
u64 highlight_addr, u64 highlight_addr,
const std::vector<InstructionInfo>& x86_instructions, const std::vector<InstructionInfo>& x86_instructions,
const FunctionEnv* fenv, const FunctionEnv* fenv,
bool* had_failure) { bool* had_failure,
bool print_whole_function) {
std::string result; std::string result;
ZydisDecoder decoder; ZydisDecoder decoder;
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); 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) { for (auto& line : lines) {
if (line.first >= rip_src_idx - FORM_DUMP_SIZE_REV && if (print_whole_function || (line.first >= rip_src_idx - FORM_DUMP_SIZE_REV &&
line.first < rip_src_idx + FORM_DUMP_SIZE_FWD) { line.first < rip_src_idx + FORM_DUMP_SIZE_FWD)) {
result.append(line.second); result.append(line.second);
} }
} }

View file

@ -34,4 +34,5 @@ std::string disassemble_x86_function(u8* data,
u64 highlight_addr, u64 highlight_addr,
const std::vector<InstructionInfo>& x86_instructions, const std::vector<InstructionInfo>& x86_instructions,
const FunctionEnv* fenv, const FunctionEnv* fenv,
bool* had_failure); bool* had_failure,
bool print_whole_function);

View file

@ -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) { TEST(GoosBuiltins, Type) {
Interpreter i; Interpreter i;
EXPECT_EQ(e(i, "(type? 'empty-list '())"), "#t"); EXPECT_EQ(e(i, "(type? 'empty-list '())"), "#t");