mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04: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 {
|
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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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>;
|
|
@ -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.",
|
||||||
|
|
|
@ -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, ¯o_obj)) {
|
if (try_getting_macro_from_goos(head, ¯o_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);
|
||||||
|
|
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& 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) {
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in a new issue