2020-09-06 12:45:31 -04:00
|
|
|
#include "goalc/compiler/Compiler.h"
|
2020-09-06 16:58:25 -04:00
|
|
|
#include "goalc/compiler/IR.h"
|
2020-11-24 20:48:38 -05:00
|
|
|
#include "common/goos/ParseHelpers.h"
|
2021-07-04 16:54:07 -04:00
|
|
|
#include "common/type_system/deftype.h"
|
2020-09-06 12:45:31 -04:00
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Parse arguments into a goos::Arguments format.
|
|
|
|
*/
|
2020-09-06 12:45:31 -04:00
|
|
|
goos::Arguments Compiler::get_va(const goos::Object& form, const goos::Object& rest) {
|
|
|
|
goos::Arguments args;
|
|
|
|
|
2020-11-24 20:48:38 -05:00
|
|
|
std::string err;
|
|
|
|
if (!goos::get_va(rest, &err, &args)) {
|
2020-12-01 21:39:46 -05:00
|
|
|
throw_compiler_error(form, err);
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
|
|
|
return args;
|
|
|
|
}
|
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Check arguments in a goos::Arguments format (named and unnamed) and throw a compiler error if it
|
|
|
|
* fails.
|
|
|
|
*/
|
2020-09-06 12:45:31 -04:00
|
|
|
void Compiler::va_check(
|
|
|
|
const goos::Object& form,
|
|
|
|
const goos::Arguments& args,
|
2020-11-20 20:17:37 -05:00
|
|
|
const std::vector<std::optional<goos::ObjectType>>& unnamed,
|
|
|
|
const std::unordered_map<std::string, std::pair<bool, std::optional<goos::ObjectType>>>&
|
|
|
|
named) {
|
2020-11-24 20:48:38 -05:00
|
|
|
std::string err;
|
|
|
|
if (!goos::va_check(args, unnamed, named, &err)) {
|
2020-12-01 21:39:46 -05:00
|
|
|
throw_compiler_error(form, err);
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Iterate through elements of a goos list and apply the given function. Throw compiler error if the
|
|
|
|
* list is invalid.
|
|
|
|
*/
|
2020-09-06 16:58:25 -04:00
|
|
|
void Compiler::for_each_in_list(const goos::Object& list,
|
|
|
|
const std::function<void(const goos::Object&)>& f) {
|
2020-09-06 12:45:31 -04:00
|
|
|
const goos::Object* iter = &list;
|
2020-09-06 16:58:25 -04:00
|
|
|
while (iter->is_pair()) {
|
2020-09-06 12:45:31 -04:00
|
|
|
auto lap = iter->as_pair();
|
|
|
|
f(lap->car);
|
|
|
|
iter = &lap->cdr;
|
|
|
|
}
|
|
|
|
|
2020-09-06 16:58:25 -04:00
|
|
|
if (!iter->is_empty_list()) {
|
2020-12-01 21:39:46 -05:00
|
|
|
throw_compiler_error(list, "Invalid list: {}", list.print());
|
2020-09-06 12:45:31 -04:00
|
|
|
}
|
2020-09-07 13:28:16 -04:00
|
|
|
}
|
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Convert a goos::Object that's a string to a std::string. Must be a string.
|
|
|
|
*/
|
2020-09-07 13:28:16 -04:00
|
|
|
std::string Compiler::as_string(const goos::Object& o) {
|
|
|
|
return o.as_string()->data;
|
|
|
|
}
|
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Convert a goos::Object that's a symbol to a std::string. Must be a string.
|
|
|
|
*/
|
2020-09-07 13:28:16 -04:00
|
|
|
std::string Compiler::symbol_string(const goos::Object& o) {
|
|
|
|
return o.as_symbol()->name;
|
2020-09-07 19:17:48 -04:00
|
|
|
}
|
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Convert a single quoted symbol into a std::string. Like 'hi -> "hi". Error if not a quoted
|
|
|
|
* symbol.
|
|
|
|
*/
|
2020-09-19 13:22:14 -04:00
|
|
|
std::string Compiler::quoted_sym_as_string(const goos::Object& o) {
|
|
|
|
auto args = get_va(o, o);
|
|
|
|
va_check(o, args, {{goos::ObjectType::SYMBOL}, {goos::ObjectType::SYMBOL}}, {});
|
|
|
|
if (symbol_string(args.unnamed.at(0)) != "quote") {
|
2020-12-01 21:39:46 -05:00
|
|
|
throw_compiler_error(o, "Invalid quoted symbol: {}.", o.print());
|
2020-09-19 13:22:14 -04:00
|
|
|
}
|
|
|
|
return symbol_string(args.unnamed.at(1));
|
|
|
|
}
|
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Get a thing that's quoted. Error if the thing isn't quoted.
|
|
|
|
*/
|
2020-12-06 15:42:26 -05:00
|
|
|
goos::Object Compiler::unquote(const goos::Object& o) {
|
|
|
|
auto args = get_va(o, o);
|
|
|
|
va_check(o, args, {{goos::ObjectType::SYMBOL}, {}}, {});
|
|
|
|
if (symbol_string(args.unnamed.at(0)) != "quote") {
|
|
|
|
throw_compiler_error(o, "Invalid quoted symbol: {}.", o.print());
|
|
|
|
}
|
|
|
|
return args.unnamed.at(1);
|
|
|
|
}
|
|
|
|
|
2020-12-31 22:15:17 -05:00
|
|
|
/*!
|
|
|
|
* Determine if o is a quoted symbol like 'test.
|
|
|
|
*/
|
2020-10-16 17:08:26 -04:00
|
|
|
bool Compiler::is_quoted_sym(const goos::Object& o) {
|
|
|
|
if (o.is_pair()) {
|
|
|
|
auto car = pair_car(o);
|
|
|
|
auto cdr = pair_cdr(o);
|
|
|
|
if (car.is_symbol() && car.as_symbol()->name == "quote") {
|
|
|
|
if (cdr.is_pair()) {
|
|
|
|
auto thing = pair_car(cdr);
|
|
|
|
if (thing.is_symbol()) {
|
|
|
|
if (pair_cdr(cdr).is_empty_list()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-07 19:17:48 -04:00
|
|
|
const goos::Object& Compiler::pair_car(const goos::Object& o) {
|
|
|
|
return o.as_pair()->car;
|
|
|
|
}
|
|
|
|
|
|
|
|
const goos::Object& Compiler::pair_cdr(const goos::Object& o) {
|
|
|
|
return o.as_pair()->cdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Compiler::expect_empty_list(const goos::Object& o) {
|
|
|
|
if (!o.is_empty_list()) {
|
2020-12-01 21:39:46 -05:00
|
|
|
throw_compiler_error(o, "expected to be an empty list");
|
2020-09-07 19:17:48 -04:00
|
|
|
}
|
2020-09-12 13:11:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
TypeSpec Compiler::parse_typespec(const goos::Object& src) {
|
2021-07-04 16:54:07 -04:00
|
|
|
return ::parse_typespec(&m_ts, src);
|
2020-09-12 13:11:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Compiler::is_local_symbol(const goos::Object& obj, Env* env) {
|
|
|
|
// check in the symbol macro env.
|
2021-08-24 22:15:26 -04:00
|
|
|
auto mlet_env = env->symbol_macro_env();
|
2020-09-12 13:11:42 -04:00
|
|
|
while (mlet_env) {
|
|
|
|
if (mlet_env->macros.find(obj.as_symbol()) != mlet_env->macros.end()) {
|
|
|
|
return true;
|
|
|
|
}
|
2021-08-24 22:15:26 -04:00
|
|
|
mlet_env = mlet_env->parent()->symbol_macro_env();
|
2020-09-12 13:11:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// check lexical
|
|
|
|
if (env->lexical_lookup(obj)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check global constants
|
|
|
|
if (m_global_constants.find(obj.as_symbol()) != m_global_constants.end()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-30 15:33:51 -05:00
|
|
|
emitter::HWRegKind Compiler::get_preferred_reg_kind(const TypeSpec& ts) {
|
|
|
|
switch (m_ts.lookup_type(ts)->get_preferred_reg_class()) {
|
|
|
|
case RegClass::GPR_64:
|
|
|
|
return emitter::HWRegKind::GPR;
|
|
|
|
case RegClass::FLOAT:
|
|
|
|
return emitter::HWRegKind::XMM;
|
2020-09-12 13:11:42 -04:00
|
|
|
default:
|
2020-09-13 21:10:43 -04:00
|
|
|
throw std::runtime_error("Unknown preferred register kind");
|
2020-09-12 13:11:42 -04:00
|
|
|
}
|
2020-09-13 17:34:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Compiler::is_none(Val* in) {
|
|
|
|
return dynamic_cast<None*>(in);
|
2020-09-19 16:50:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Compiler::is_basic(const TypeSpec& ts) {
|
2021-03-03 20:52:25 -05:00
|
|
|
return m_ts.tc(m_ts.make_typespec("basic"), ts);
|
2020-10-14 13:42:14 -04:00
|
|
|
}
|
|
|
|
|
2020-10-16 17:08:26 -04:00
|
|
|
bool Compiler::is_structure(const TypeSpec& ts) {
|
2021-03-03 20:52:25 -05:00
|
|
|
return m_ts.tc(m_ts.make_typespec("structure"), ts);
|
2020-10-16 17:08:26 -04:00
|
|
|
}
|
|
|
|
|
2020-11-29 18:01:30 -05:00
|
|
|
bool Compiler::is_bitfield(const TypeSpec& ts) {
|
|
|
|
return m_ts.is_bitfield_type(ts.base_type());
|
|
|
|
}
|
|
|
|
|
2020-12-19 15:21:29 -05:00
|
|
|
bool Compiler::is_pair(const TypeSpec& ts) {
|
2021-03-03 20:52:25 -05:00
|
|
|
return m_ts.tc(m_ts.make_typespec("pair"), ts);
|
2020-12-19 15:21:29 -05:00
|
|
|
}
|
|
|
|
|
2020-10-14 13:42:14 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-12-05 17:09:46 -05:00
|
|
|
if (in.is_pair()) {
|
|
|
|
auto head = in.as_pair()->car;
|
|
|
|
if (head.is_symbol()) {
|
|
|
|
auto head_sym = head.as_symbol();
|
2021-04-22 19:08:58 -04:00
|
|
|
auto enum_type = m_ts.try_enum_lookup(head_sym->name);
|
|
|
|
if (enum_type) {
|
2020-12-05 17:09:46 -05:00
|
|
|
bool success;
|
2021-04-22 19:08:58 -04:00
|
|
|
u64 as_enum = enum_lookup(in, enum_type, in.as_pair()->cdr, false, &success);
|
2020-12-05 17:09:46 -05:00
|
|
|
if (success) {
|
|
|
|
*out = as_enum;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2021-04-18 19:51:15 -04:00
|
|
|
|
|
|
|
if (head_sym->name == "size-of") {
|
|
|
|
*out = get_size_for_size_of(in, in.as_pair()->cdr);
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-05 17:09:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-25 18:02:03 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-04-11 19:21:49 -04:00
|
|
|
bool Compiler::try_getting_constant_float(const goos::Object& in, float* out, Env* env) {
|
2020-10-25 18:02:03 -04:00
|
|
|
(void)env;
|
|
|
|
if (in.is_float()) {
|
|
|
|
*out = in.as_float();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-16 17:08:26 -04:00
|
|
|
// todo, try more things like constants before giving up.
|
2020-10-14 13:42:14 -04:00
|
|
|
return false;
|
2020-12-04 12:57:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Compiler::get_true_or_false(const goos::Object& form, const goos::Object& boolean) {
|
|
|
|
// todo try other things.
|
|
|
|
if (boolean.is_symbol()) {
|
|
|
|
if (boolean.as_symbol()->name == "#t") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (boolean.as_symbol()->name == "#f") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw_compiler_error(form, "The value {} cannot be used as a boolean.", boolean.print());
|
|
|
|
return false;
|
2020-12-19 15:21:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<goos::Object> Compiler::get_list_as_vector(const goos::Object& o,
|
|
|
|
goos::Object* rest_out,
|
|
|
|
int max_length) {
|
|
|
|
std::vector<goos::Object> result;
|
|
|
|
|
|
|
|
auto* cur = &o;
|
|
|
|
int n = 0;
|
|
|
|
while (true) {
|
|
|
|
if (max_length >= 0 && n >= max_length) {
|
|
|
|
if (rest_out) {
|
|
|
|
*rest_out = *cur;
|
2021-02-20 11:42:46 -05:00
|
|
|
} else {
|
|
|
|
throw std::runtime_error("get_list_as_vector would discard arguments");
|
2020-12-19 15:21:29 -05:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur->is_pair()) {
|
|
|
|
result.push_back(cur->as_pair()->car);
|
|
|
|
cur = &cur->as_pair()->cdr;
|
|
|
|
n++;
|
|
|
|
} else if (cur->is_empty_list()) {
|
|
|
|
if (rest_out) {
|
2021-10-20 19:49:32 -04:00
|
|
|
*rest_out = goos::Object::make_empty_list();
|
2020-12-19 15:21:29 -05:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
2020-12-31 22:15:17 -05:00
|
|
|
}
|
|
|
|
|
2021-08-26 20:33:00 -04:00
|
|
|
void Compiler::compile_constant_product(const goos::Object& form,
|
|
|
|
RegVal* dest,
|
|
|
|
RegVal* src,
|
|
|
|
int stride,
|
|
|
|
Env* env) {
|
2020-12-31 22:15:17 -05:00
|
|
|
// todo - support imul with an imm.
|
|
|
|
assert(stride);
|
|
|
|
|
|
|
|
bool is_power_of_two = (stride & (stride - 1)) == 0;
|
|
|
|
if (stride == 1) {
|
2021-08-26 20:33:00 -04:00
|
|
|
env->emit_ir<IR_RegSet>(form, dest, src);
|
2020-12-31 22:15:17 -05:00
|
|
|
} else if (is_power_of_two) {
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
|
|
if (stride == (1 << i)) {
|
2021-08-26 20:33:00 -04:00
|
|
|
env->emit_ir<IR_RegSet>(form, dest, src);
|
|
|
|
env->emit_ir<IR_IntegerMath>(form, IntegerMathKind::SHL_64, dest, i);
|
2020-12-31 22:15:17 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(false);
|
|
|
|
} else {
|
|
|
|
// get the multiplier
|
2021-08-26 20:33:00 -04:00
|
|
|
env->emit_ir<IR_LoadConstant64>(form, dest, stride);
|
|
|
|
env->emit_ir<IR_IntegerMath>(form, IntegerMathKind::IMUL_32, dest, src);
|
2020-12-31 22:15:17 -05:00
|
|
|
}
|
2021-08-31 22:12:30 -04:00
|
|
|
}
|