mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-23 06:09:57 +00:00
[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:
parent
2caf75a11c
commit
1db96c72ab
@ -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<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,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
|
@ -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<EnvironmentObject>& env);
|
||||
Object eval_current_method_type(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
Object eval_format(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 ...)`
|
||||
- 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.
|
@ -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);
|
||||
|
@ -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))
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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<std::string> 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 <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");
|
||||
if (!str.empty() && str.back() == '\n') {
|
||||
fmt::print(fmt::emphasis::bold, str, std::forward<Args>(args)...);
|
||||
@ -399,7 +405,7 @@ class Compiler {
|
||||
}
|
||||
|
||||
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");
|
||||
if (!str.empty() && str.back() == '\n') {
|
||||
fmt::print(fmt::emphasis::bold, str, std::forward<Args>(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
|
||||
|
@ -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()) {
|
||||
|
@ -293,3 +293,18 @@ class BitFieldVal : public Val {
|
||||
bool m_sign_extend = 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>;
|
@ -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.",
|
||||
|
@ -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);
|
||||
|
319
goalc/compiler/compilation/ConstantPropagation.cpp
Normal file
319
goalc/compiler/compilation/ConstantPropagation.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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<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);
|
||||
// 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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<IR_RegSet>(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<IR_RegSet>(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);
|
||||
|
@ -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<AliasVal>(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<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);
|
||||
}
|
||||
|
||||
@ -1058,7 +1062,7 @@ Val* Compiler::compile_static_new(const goos::Object& form,
|
||||
auto result = fe->alloc_val<StaticVal>(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<StructureType*>(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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -81,7 +81,8 @@ std::string disassemble_x86_function(u8* data,
|
||||
u64 highlight_addr,
|
||||
const std::vector<InstructionInfo>& 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);
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,5 @@ std::string disassemble_x86_function(u8* data,
|
||||
u64 highlight_addr,
|
||||
const std::vector<InstructionInfo>& x86_instructions,
|
||||
const FunctionEnv* fenv,
|
||||
bool* had_failure);
|
||||
bool* had_failure,
|
||||
bool print_whole_function);
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user