mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-26 16:00:52 +00:00
Try to speed up the build (#106)
* first attempt * attempt 2 * windows and formatting fix
This commit is contained in:
parent
985549f27c
commit
a45d180f2c
@ -5,5 +5,5 @@ ELSE()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
ENDIF()
|
||||
|
||||
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp PrettyPrinter.cpp)
|
||||
add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp PrettyPrinter.cpp)
|
||||
target_link_libraries(goos common_util fmt)
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <utility>
|
||||
#include "Interpreter.h"
|
||||
#include <third-party/fmt/core.h>
|
||||
|
||||
namespace goos {
|
||||
Interpreter::Interpreter() {
|
||||
@ -923,4 +924,633 @@ Object Interpreter::eval_while(const Object& form,
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Exit GOOS. Accepts and ignores all arguments;
|
||||
*/
|
||||
Object Interpreter::eval_exit(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)args;
|
||||
(void)env;
|
||||
want_exit = true;
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Begin form
|
||||
*/
|
||||
Object Interpreter::eval_begin(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
if (!args.named.empty()) {
|
||||
throw_eval_error(form, "begin form cannot have keyword arguments");
|
||||
}
|
||||
|
||||
if (args.unnamed.empty()) {
|
||||
return EmptyListObject::make_new();
|
||||
} else {
|
||||
return args.unnamed.back();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Read form, which runs the Reader on a string.
|
||||
*/
|
||||
Object Interpreter::eval_read(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
|
||||
try {
|
||||
return reader.read_from_string(args.unnamed.at(0).as_string()->data);
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("reader error inside of read:\n") + e.what());
|
||||
}
|
||||
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Open and run the Reader on a text file.
|
||||
*/
|
||||
Object Interpreter::eval_read_file(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
|
||||
try {
|
||||
return reader.read_from_file({args.unnamed.at(0).as_string()->data});
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("reader error inside of read-file:\n") + e.what());
|
||||
}
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Combines read-file and eval to load in a file.
|
||||
*/
|
||||
Object Interpreter::eval_load_file(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
|
||||
Object o;
|
||||
try {
|
||||
o = reader.read_from_file({args.unnamed.at(0).as_string()->data});
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("reader error inside of load-file:\n") + e.what());
|
||||
}
|
||||
|
||||
try {
|
||||
return eval_with_rewind(o, global_environment.as_env());
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("eval error inside of load-file:\n") + e.what());
|
||||
}
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Print the form to stdout, including a newline.
|
||||
* Returns ()
|
||||
*/
|
||||
Object Interpreter::eval_print(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}}, {});
|
||||
|
||||
if (!disable_printing) {
|
||||
printf("%s\n", args.unnamed.at(0).print().c_str());
|
||||
}
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Print the inspection of a form to stdout, including a newline.
|
||||
* Returns ()
|
||||
*/
|
||||
Object Interpreter::eval_inspect(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}}, {});
|
||||
|
||||
if (!disable_printing) {
|
||||
printf("%s\n", args.unnamed.at(0).inspect().c_str());
|
||||
}
|
||||
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Fancy equality check (using Object::operator==)
|
||||
*/
|
||||
Object Interpreter::eval_equals(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable,
|
||||
args.unnamed[0] == args.unnamed[1] ? "#t" : "#f");
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a number to an integer
|
||||
*/
|
||||
IntType Interpreter::number_to_integer(const Object& obj) {
|
||||
switch (obj.type) {
|
||||
case ObjectType::INTEGER:
|
||||
return obj.integer_obj.value;
|
||||
case ObjectType::FLOAT:
|
||||
return (int64_t)obj.float_obj.value;
|
||||
default:
|
||||
throw_eval_error(obj, "object cannot be interpreted as a number!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a number to floating point
|
||||
*/
|
||||
FloatType Interpreter::number_to_float(const Object& obj) {
|
||||
switch (obj.type) {
|
||||
case ObjectType::INTEGER:
|
||||
return obj.integer_obj.value;
|
||||
case ObjectType::FLOAT:
|
||||
return obj.float_obj.value;
|
||||
default:
|
||||
throw_eval_error(obj, "object cannot be interpreted as a number!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert number to template type.
|
||||
*/
|
||||
template <>
|
||||
FloatType Interpreter::number(const Object& obj) {
|
||||
return number_to_float(obj);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert number to template type.
|
||||
*/
|
||||
template <>
|
||||
IntType Interpreter::number(const Object& obj) {
|
||||
return number_to_integer(obj);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of addition.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_plus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result = 0;
|
||||
for (const auto& arg : args.unnamed) {
|
||||
result += number<T>(arg);
|
||||
}
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Addition
|
||||
*/
|
||||
Object Interpreter::eval_plus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
if (!args.named.empty() || args.unnamed.empty()) {
|
||||
throw_eval_error(form, "+ must receive at least one unnamed argument!");
|
||||
}
|
||||
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_plus<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_plus<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "+ must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of multiplication.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_times(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result = 1;
|
||||
for (const auto& arg : args.unnamed) {
|
||||
result *= number<T>(arg);
|
||||
}
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Multiplication
|
||||
*/
|
||||
Object Interpreter::eval_times(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
if (!args.named.empty() || args.unnamed.empty()) {
|
||||
throw_eval_error(form, "* must receive at least one unnamed argument!");
|
||||
}
|
||||
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_times<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_times<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "* must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of subtraction.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_minus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result;
|
||||
if (args.unnamed.size() > 1) {
|
||||
result = number<T>(args.unnamed[0]);
|
||||
for (uint32_t i = 1; i < args.unnamed.size(); i++) {
|
||||
result -= number<T>(args.unnamed[i]);
|
||||
}
|
||||
} else {
|
||||
result = -number<T>(args.unnamed[0]);
|
||||
}
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Subtraction
|
||||
*/
|
||||
Object Interpreter::eval_minus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
if (!args.named.empty() || args.unnamed.empty()) {
|
||||
throw_eval_error(form, "- must receive at least one unnamed argument!");
|
||||
}
|
||||
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_minus<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_minus<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "- must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of division.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_divide(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result = number<T>(args.unnamed[0]) / number<T>(args.unnamed[1]);
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Division
|
||||
*/
|
||||
Object Interpreter::eval_divide(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_divide<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_divide<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "/ must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compare numbers for equality
|
||||
*/
|
||||
Object Interpreter::eval_numequals(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
if (!args.named.empty() || args.unnamed.size() < 2) {
|
||||
throw_eval_error(form, "= must receive at least two unnamed arguments!");
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER: {
|
||||
int64_t ref = number_to_integer(args.unnamed.front());
|
||||
for (uint32_t i = 1; i < args.unnamed.size(); i++) {
|
||||
if (ref != number_to_integer(args.unnamed[i])) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case ObjectType::FLOAT: {
|
||||
double ref = number_to_float(args.unnamed.front());
|
||||
for (uint32_t i = 1; i < args.unnamed.size(); i++) {
|
||||
if (ref != number_to_float(args.unnamed[i])) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "+ must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
return SymbolObject::make_new(reader.symbolTable, result ? "#t" : "#f");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_lt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a < b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_lt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_lt<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_lt<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "< must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_gt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a > b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_gt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_gt<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_gt<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "> must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_leq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a <= b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_leq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_leq<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_leq<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "<= must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_geq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a >= b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_geq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_geq<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_geq<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, ">= must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
Object Interpreter::eval_eval(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}}, {});
|
||||
return eval(args.unnamed[0], env);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_car(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR}, {});
|
||||
return args.unnamed[0].as_pair()->car;
|
||||
}
|
||||
|
||||
Object Interpreter::eval_set_car(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR, {}}, {});
|
||||
args.unnamed[0].as_pair()->car = args.unnamed[1];
|
||||
return args.unnamed[0];
|
||||
}
|
||||
|
||||
Object Interpreter::eval_set_cdr(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR, {}}, {});
|
||||
args.unnamed[0].as_pair()->cdr = args.unnamed[1];
|
||||
return args.unnamed[0];
|
||||
}
|
||||
|
||||
Object Interpreter::eval_cdr(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR}, {});
|
||||
return args.unnamed[0].as_pair()->cdr;
|
||||
}
|
||||
|
||||
Object Interpreter::eval_gensym(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable, "gensym" + std::to_string(gensym_id++));
|
||||
}
|
||||
|
||||
Object Interpreter::eval_cons(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
return PairObject::make_new(args.unnamed[0], args.unnamed[1]);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_null(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable, args.unnamed[0].is_empty_list() ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_type(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{ObjectType::SYMBOL}, {}}, {});
|
||||
|
||||
auto kv = string_to_type.find(args.unnamed[0].as_symbol()->name);
|
||||
if (kv == string_to_type.end()) {
|
||||
throw_eval_error(form, "invalid type given to type?");
|
||||
}
|
||||
|
||||
if (args.unnamed[1].type == kv->second) {
|
||||
return SymbolObject::make_new(reader.symbolTable, "#t");
|
||||
} else {
|
||||
return SymbolObject::make_new(reader.symbolTable, "#f");
|
||||
}
|
||||
}
|
||||
|
||||
Object Interpreter::eval_current_method_type(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable, goal_to_goos.enclosing_method_type);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_format(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
if (args.unnamed.size() < 2) {
|
||||
throw_eval_error(form, "format must get at least two arguments");
|
||||
}
|
||||
|
||||
auto dest = args.unnamed.at(0);
|
||||
auto format_str = args.unnamed.at(1);
|
||||
if (!format_str.is_string()) {
|
||||
throw_eval_error(form, "format string must be a string");
|
||||
}
|
||||
|
||||
// Note: this might be relying on internal implementation details of libfmt to work properly
|
||||
// and isn't a great solution.
|
||||
std::vector<fmt::basic_format_arg<fmt::format_context>> args2;
|
||||
std::vector<std::string> strings;
|
||||
for (size_t i = 2; i < args.unnamed.size(); i++) {
|
||||
if (args.unnamed.at(i).is_string()) {
|
||||
strings.push_back(args.unnamed.at(i).as_string()->data);
|
||||
} else {
|
||||
strings.push_back(args.unnamed.at(i).print());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& x : strings) {
|
||||
args2.push_back(fmt::detail::make_arg<fmt::format_context>(x));
|
||||
}
|
||||
|
||||
auto formatted =
|
||||
fmt::vformat(format_str.as_string()->data,
|
||||
fmt::format_args(args2.data(), static_cast<unsigned>(args2.size())));
|
||||
|
||||
if (truthy(dest)) {
|
||||
printf("%s", formatted.c_str());
|
||||
}
|
||||
|
||||
return StringObject::make_new(formatted);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_error(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
throw_eval_error(form, "Error: " + args.unnamed.at(0).as_string()->data);
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
} // namespace goos
|
||||
|
@ -1,640 +0,0 @@
|
||||
/*!
|
||||
* @file InterpreterEval.cpp
|
||||
* Implementation of built-in GOOS functions.
|
||||
*/
|
||||
|
||||
#include <third-party/fmt/format.h>
|
||||
#include "Interpreter.h"
|
||||
|
||||
namespace goos {
|
||||
|
||||
/*!
|
||||
* Exit GOOS. Accepts and ignores all arguments;
|
||||
*/
|
||||
Object Interpreter::eval_exit(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)args;
|
||||
(void)env;
|
||||
want_exit = true;
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Begin form
|
||||
*/
|
||||
Object Interpreter::eval_begin(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
if (!args.named.empty()) {
|
||||
throw_eval_error(form, "begin form cannot have keyword arguments");
|
||||
}
|
||||
|
||||
if (args.unnamed.empty()) {
|
||||
return EmptyListObject::make_new();
|
||||
} else {
|
||||
return args.unnamed.back();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Read form, which runs the Reader on a string.
|
||||
*/
|
||||
Object Interpreter::eval_read(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
|
||||
try {
|
||||
return reader.read_from_string(args.unnamed.at(0).as_string()->data);
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("reader error inside of read:\n") + e.what());
|
||||
}
|
||||
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Open and run the Reader on a text file.
|
||||
*/
|
||||
Object Interpreter::eval_read_file(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
|
||||
try {
|
||||
return reader.read_from_file({args.unnamed.at(0).as_string()->data});
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("reader error inside of read-file:\n") + e.what());
|
||||
}
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Combines read-file and eval to load in a file.
|
||||
*/
|
||||
Object Interpreter::eval_load_file(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
|
||||
Object o;
|
||||
try {
|
||||
o = reader.read_from_file({args.unnamed.at(0).as_string()->data});
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("reader error inside of load-file:\n") + e.what());
|
||||
}
|
||||
|
||||
try {
|
||||
return eval_with_rewind(o, global_environment.as_env());
|
||||
} catch (std::runtime_error& e) {
|
||||
throw_eval_error(form, std::string("eval error inside of load-file:\n") + e.what());
|
||||
}
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Print the form to stdout, including a newline.
|
||||
* Returns ()
|
||||
*/
|
||||
Object Interpreter::eval_print(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}}, {});
|
||||
|
||||
if (!disable_printing) {
|
||||
printf("%s\n", args.unnamed.at(0).print().c_str());
|
||||
}
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Print the inspection of a form to stdout, including a newline.
|
||||
* Returns ()
|
||||
*/
|
||||
Object Interpreter::eval_inspect(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}}, {});
|
||||
|
||||
if (!disable_printing) {
|
||||
printf("%s\n", args.unnamed.at(0).inspect().c_str());
|
||||
}
|
||||
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Fancy equality check (using Object::operator==)
|
||||
*/
|
||||
Object Interpreter::eval_equals(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable,
|
||||
args.unnamed[0] == args.unnamed[1] ? "#t" : "#f");
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a number to an integer
|
||||
*/
|
||||
IntType Interpreter::number_to_integer(const Object& obj) {
|
||||
switch (obj.type) {
|
||||
case ObjectType::INTEGER:
|
||||
return obj.integer_obj.value;
|
||||
case ObjectType::FLOAT:
|
||||
return (int64_t)obj.float_obj.value;
|
||||
default:
|
||||
throw_eval_error(obj, "object cannot be interpreted as a number!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert a number to floating point
|
||||
*/
|
||||
FloatType Interpreter::number_to_float(const Object& obj) {
|
||||
switch (obj.type) {
|
||||
case ObjectType::INTEGER:
|
||||
return obj.integer_obj.value;
|
||||
case ObjectType::FLOAT:
|
||||
return obj.float_obj.value;
|
||||
default:
|
||||
throw_eval_error(obj, "object cannot be interpreted as a number!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert number to template type.
|
||||
*/
|
||||
template <>
|
||||
FloatType Interpreter::number(const Object& obj) {
|
||||
return number_to_float(obj);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert number to template type.
|
||||
*/
|
||||
template <>
|
||||
IntType Interpreter::number(const Object& obj) {
|
||||
return number_to_integer(obj);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of addition.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_plus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result = 0;
|
||||
for (const auto& arg : args.unnamed) {
|
||||
result += number<T>(arg);
|
||||
}
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Addition
|
||||
*/
|
||||
Object Interpreter::eval_plus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
if (!args.named.empty() || args.unnamed.empty()) {
|
||||
throw_eval_error(form, "+ must receive at least one unnamed argument!");
|
||||
}
|
||||
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_plus<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_plus<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "+ must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of multiplication.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_times(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result = 1;
|
||||
for (const auto& arg : args.unnamed) {
|
||||
result *= number<T>(arg);
|
||||
}
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Multiplication
|
||||
*/
|
||||
Object Interpreter::eval_times(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
if (!args.named.empty() || args.unnamed.empty()) {
|
||||
throw_eval_error(form, "* must receive at least one unnamed argument!");
|
||||
}
|
||||
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_times<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_times<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "* must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of subtraction.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_minus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result;
|
||||
if (args.unnamed.size() > 1) {
|
||||
result = number<T>(args.unnamed[0]);
|
||||
for (uint32_t i = 1; i < args.unnamed.size(); i++) {
|
||||
result -= number<T>(args.unnamed[i]);
|
||||
}
|
||||
} else {
|
||||
result = -number<T>(args.unnamed[0]);
|
||||
}
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Subtraction
|
||||
*/
|
||||
Object Interpreter::eval_minus(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
if (!args.named.empty() || args.unnamed.empty()) {
|
||||
throw_eval_error(form, "- must receive at least one unnamed argument!");
|
||||
}
|
||||
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_minus<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_minus<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "- must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Template implementation of division.
|
||||
*/
|
||||
template <typename T>
|
||||
Object Interpreter::num_divide(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
(void)form;
|
||||
T result = number<T>(args.unnamed[0]) / number<T>(args.unnamed[1]);
|
||||
return Object::make_number<T>(result);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Division
|
||||
*/
|
||||
Object Interpreter::eval_divide(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_divide<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_divide<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "/ must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compare numbers for equality
|
||||
*/
|
||||
Object Interpreter::eval_numequals(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
if (!args.named.empty() || args.unnamed.size() < 2) {
|
||||
throw_eval_error(form, "= must receive at least two unnamed arguments!");
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER: {
|
||||
int64_t ref = number_to_integer(args.unnamed.front());
|
||||
for (uint32_t i = 1; i < args.unnamed.size(); i++) {
|
||||
if (ref != number_to_integer(args.unnamed[i])) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case ObjectType::FLOAT: {
|
||||
double ref = number_to_float(args.unnamed.front());
|
||||
for (uint32_t i = 1; i < args.unnamed.size(); i++) {
|
||||
if (ref != number_to_float(args.unnamed[i])) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "+ must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
|
||||
return SymbolObject::make_new(reader.symbolTable, result ? "#t" : "#f");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_lt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a < b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_lt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_lt<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_lt<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "< must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_gt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a > b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_gt(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_gt<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_gt<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "> must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_leq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a <= b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_leq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_leq<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_leq<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, "<= must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Object Interpreter::num_geq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)form;
|
||||
(void)env;
|
||||
T a = number<T>(args.unnamed[0]);
|
||||
T b = number<T>(args.unnamed[1]);
|
||||
return SymbolObject::make_new(reader.symbolTable, (a >= b) ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_geq(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
switch (args.unnamed.front().type) {
|
||||
case ObjectType::INTEGER:
|
||||
return num_geq<int64_t>(form, args, env);
|
||||
|
||||
case ObjectType::FLOAT:
|
||||
return num_geq<double>(form, args, env);
|
||||
|
||||
default:
|
||||
throw_eval_error(form, ">= must have a numeric argument");
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
}
|
||||
|
||||
Object Interpreter::eval_eval(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
vararg_check(form, args, {{}}, {});
|
||||
return eval(args.unnamed[0], env);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_car(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR}, {});
|
||||
return args.unnamed[0].as_pair()->car;
|
||||
}
|
||||
|
||||
Object Interpreter::eval_set_car(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR, {}}, {});
|
||||
args.unnamed[0].as_pair()->car = args.unnamed[1];
|
||||
return args.unnamed[0];
|
||||
}
|
||||
|
||||
Object Interpreter::eval_set_cdr(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR, {}}, {});
|
||||
args.unnamed[0].as_pair()->cdr = args.unnamed[1];
|
||||
return args.unnamed[0];
|
||||
}
|
||||
|
||||
Object Interpreter::eval_cdr(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::PAIR}, {});
|
||||
return args.unnamed[0].as_pair()->cdr;
|
||||
}
|
||||
|
||||
Object Interpreter::eval_gensym(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable, "gensym" + std::to_string(gensym_id++));
|
||||
}
|
||||
|
||||
Object Interpreter::eval_cons(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}, {}}, {});
|
||||
return PairObject::make_new(args.unnamed[0], args.unnamed[1]);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_null(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{}}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable, args.unnamed[0].is_empty_list() ? "#t" : "#f");
|
||||
}
|
||||
|
||||
Object Interpreter::eval_type(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {{ObjectType::SYMBOL}, {}}, {});
|
||||
|
||||
auto kv = string_to_type.find(args.unnamed[0].as_symbol()->name);
|
||||
if (kv == string_to_type.end()) {
|
||||
throw_eval_error(form, "invalid type given to type?");
|
||||
}
|
||||
|
||||
if (args.unnamed[1].type == kv->second) {
|
||||
return SymbolObject::make_new(reader.symbolTable, "#t");
|
||||
} else {
|
||||
return SymbolObject::make_new(reader.symbolTable, "#f");
|
||||
}
|
||||
}
|
||||
|
||||
Object Interpreter::eval_current_method_type(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {}, {});
|
||||
return SymbolObject::make_new(reader.symbolTable, goal_to_goos.enclosing_method_type);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_format(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
if (args.unnamed.size() < 2) {
|
||||
throw_eval_error(form, "format must get at least two arguments");
|
||||
}
|
||||
|
||||
auto dest = args.unnamed.at(0);
|
||||
auto format_str = args.unnamed.at(1);
|
||||
if (!format_str.is_string()) {
|
||||
throw_eval_error(form, "format string must be a string");
|
||||
}
|
||||
|
||||
// Note: this might be relying on internal implementation details of libfmt to work properly
|
||||
// and isn't a great solution.
|
||||
std::vector<fmt::basic_format_arg<fmt::format_context>> args2;
|
||||
std::vector<std::string> strings;
|
||||
for (size_t i = 2; i < args.unnamed.size(); i++) {
|
||||
if (args.unnamed.at(i).is_string()) {
|
||||
strings.push_back(args.unnamed.at(i).as_string()->data);
|
||||
} else {
|
||||
strings.push_back(args.unnamed.at(i).print());
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& x : strings) {
|
||||
args2.push_back(fmt::detail::make_arg<fmt::format_context>(x));
|
||||
}
|
||||
|
||||
auto formatted =
|
||||
fmt::vformat(format_str.as_string()->data,
|
||||
fmt::format_args(args2.data(), static_cast<unsigned>(args2.size())));
|
||||
|
||||
if (truthy(dest)) {
|
||||
printf("%s", formatted.c_str());
|
||||
}
|
||||
|
||||
return StringObject::make_new(formatted);
|
||||
}
|
||||
|
||||
Object Interpreter::eval_error(const Object& form,
|
||||
Arguments& args,
|
||||
const std::shared_ptr<EnvironmentObject>& env) {
|
||||
(void)env;
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
throw_eval_error(form, "Error: " + args.unnamed.at(0).as_string()->data);
|
||||
return EmptyListObject::make_new();
|
||||
}
|
||||
} // namespace goos
|
@ -1,4 +1,5 @@
|
||||
#include <cassert>
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "decompiler/Disasm/InstructionMatching.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "CfgVtx.h"
|
||||
|
@ -6,7 +6,10 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
namespace goos {
|
||||
class Object;
|
||||
}
|
||||
|
||||
/*!
|
||||
* In v, find an item equal to old, and replace it with replace.
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "TypeInspector.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
|
||||
namespace {
|
||||
std::vector<Register> gpr_backups = {make_gpr(Reg::GP), make_gpr(Reg::S5), make_gpr(Reg::S4),
|
||||
|
@ -5,13 +5,17 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include "decompiler/Disasm/Instruction.h"
|
||||
#include "BasicBlocks.h"
|
||||
#include "CfgVtx.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
#include "common/type_system/TypeSpec.h"
|
||||
|
||||
class DecompilerTypeSystem;
|
||||
// Map of what type is in each register.
|
||||
using TypeMap = std::unordered_map<Register, TypeSpec, Register::hash>;
|
||||
class IR;
|
||||
|
||||
struct FunctionName {
|
||||
enum class FunctionKind {
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "Function.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "decompiler/Disasm/InstructionMatching.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
|
||||
namespace {
|
||||
/*!
|
||||
|
@ -3,9 +3,10 @@
|
||||
#include "TypeInspector.h"
|
||||
#include "Function.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "common/type_system/deftype.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
|
||||
namespace {
|
||||
struct FieldPrint {
|
||||
|
@ -6,11 +6,12 @@
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include "common/type_system/Type.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
class Function;
|
||||
class DecompilerTypeSystem;
|
||||
class LinkedObjectFile;
|
||||
class Field;
|
||||
|
||||
struct TypeInspectorResult {
|
||||
bool success = false;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/Function/BasicBlocks.h"
|
||||
#include "decompiler/Disasm/InstructionMatching.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
#include "third-party/fmt/format.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include <unordered_set>
|
||||
#include "common/util/MatchParam.h"
|
||||
#include "CfgBuilder.h"
|
||||
#include "decompiler/Function/CfgVtx.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "decompiler/Disasm/InstructionMatching.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "IR.h"
|
||||
#include "decompiler/ObjectFile/LinkedObjectFile.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
std::vector<std::shared_ptr<IR>> IR::get_all_ir(LinkedObjectFile& file) const {
|
||||
(void)file;
|
||||
|
@ -3,13 +3,18 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "decompiler/Disasm/Register.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "common/type_system/TypeSpec.h"
|
||||
|
||||
class LinkedObjectFile;
|
||||
class DecompilerTypeSystem;
|
||||
|
||||
namespace goos {
|
||||
class Object;
|
||||
}
|
||||
|
||||
// Map of what type is in each register.
|
||||
using TypeMap = std::unordered_map<Register, TypeSpec, Register::hash>;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <decompiler/Disasm/InstructionMatching.h>
|
||||
#include "IR.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
bool IR::get_type_of_expr(const TypeMap& reg_types,
|
||||
DecompilerTypeSystem& dts,
|
||||
|
@ -7,12 +7,14 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <numeric>
|
||||
#include "third-party/fmt/format.h"
|
||||
#include "decompiler/IR/IR.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "LinkedObjectFile.h"
|
||||
#include "decompiler/Disasm/InstructionDecode.h"
|
||||
#include "decompiler/config.h"
|
||||
#include "third-party/json.hpp"
|
||||
#include "third-party/spdlog/include/spdlog/spdlog.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
|
||||
/*!
|
||||
* Set the number of segments in this object file.
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <unordered_set>
|
||||
#include "LinkedWord.h"
|
||||
#include "decompiler/Function/Function.h"
|
||||
#include "common/goos/PrettyPrinter.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
/*!
|
||||
* A label to a location in this object file.
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <vector>
|
||||
#include "LinkedObjectFile.h"
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
/*!
|
||||
* A "record" which can be used to identify an object file.
|
||||
|
@ -131,4 +131,19 @@ bool DecompilerTypeSystem::lookup_flags(const std::string& type, u64* dest) cons
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DecompilerTypeSystem::add_symbol(const std::string& name, const TypeSpec& type_spec) {
|
||||
add_symbol(name);
|
||||
auto skv = symbol_types.find(name);
|
||||
if (skv == symbol_types.end() || skv->second == type_spec) {
|
||||
symbol_types[name] = type_spec;
|
||||
} else {
|
||||
if (ts.typecheck(type_spec, skv->second, "", false, false)) {
|
||||
} else {
|
||||
spdlog::warn("Attempting to redefine type of symbol {} from {} to {}\n", name,
|
||||
skv->second.print(), type_spec.print());
|
||||
throw std::runtime_error("Type redefinition");
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
#define JAK_DECOMPILERTYPESYSTEM_H
|
||||
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
class DecompilerTypeSystem {
|
||||
public:
|
||||
@ -25,20 +24,7 @@ class DecompilerTypeSystem {
|
||||
add_symbol(name, TypeSpec(base_type));
|
||||
}
|
||||
|
||||
void add_symbol(const std::string& name, const TypeSpec& type_spec) {
|
||||
add_symbol(name);
|
||||
auto skv = symbol_types.find(name);
|
||||
if (skv == symbol_types.end() || skv->second == type_spec) {
|
||||
symbol_types[name] = type_spec;
|
||||
} else {
|
||||
if (ts.typecheck(type_spec, skv->second, "", false, false)) {
|
||||
} else {
|
||||
fmt::print("Attempting to redefine type of symbol {} from {} to {}\n", name,
|
||||
skv->second.print(), type_spec.print());
|
||||
throw std::runtime_error("Type redefinition");
|
||||
}
|
||||
}
|
||||
}
|
||||
void add_symbol(const std::string& name, const TypeSpec& type_spec);
|
||||
|
||||
void parse_type_defs(const std::vector<std::string>& file_path);
|
||||
|
||||
|
@ -28,6 +28,7 @@ add_library(compiler
|
||||
regalloc/IRegister.cpp
|
||||
regalloc/Allocator.cpp
|
||||
regalloc/allocate.cpp
|
||||
regalloc/allocate_common.cpp
|
||||
compiler/Compiler.cpp)
|
||||
|
||||
add_executable(goalc main.cpp)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "common/link_types.h"
|
||||
#include "IR.h"
|
||||
#include "goalc/regalloc/allocate.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#ifndef JAK_COMPILER_H
|
||||
#define JAK_COMPILER_H
|
||||
|
||||
#include <functional>
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include "Env.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <stdexcept>
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "Env.h"
|
||||
#include "IR.h"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include "IR.h"
|
||||
|
||||
#include <utility>
|
||||
#include "IR.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
using namespace emitter;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "Val.h"
|
||||
#include "Env.h"
|
||||
#include "IR.h"
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include "goalc/regalloc/IRegister.h"
|
||||
#include "Lambda.h"
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "goalc/logger/Logger.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace {
|
||||
/*!
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
using namespace goos;
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
namespace {
|
||||
bool integer_fits(s64 in, int size, bool is_signed) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/type_system/deftype.h"
|
||||
|
||||
namespace {
|
||||
|
@ -9,7 +9,6 @@
|
||||
#define JAK_REGISTER_H
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "Allocator.h"
|
||||
#include "LiveInfo.h"
|
||||
|
||||
/*!
|
||||
* Find basic blocks and add block link info.
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include <unordered_map>
|
||||
#include "IRegister.h"
|
||||
#include "allocate.h"
|
||||
#include "LiveInfo.h"
|
||||
#include "StackOp.h"
|
||||
|
||||
struct RegAllocBasicBlock {
|
||||
std::vector<int> instr_idx;
|
||||
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_ASSIGNMENT_H
|
||||
#define JAK_ASSIGNMENT_H
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "goalc/emitter/Register.h"
|
||||
|
||||
/*!
|
||||
* The assignment of an IRegister to a real Register.
|
||||
* For a single IR Instruction.
|
||||
*/
|
||||
struct Assignment {
|
||||
enum class Kind { STACK, REGISTER, UNASSIGNED } kind = Kind::UNASSIGNED;
|
||||
emitter::Register reg = -1; //! where the IRegister is now
|
||||
int stack_slot = -1; //! index of the slot, if we are ever spilled
|
||||
bool spilled = false; //! are we ever spilled
|
||||
|
||||
std::string to_string() const {
|
||||
std::string result;
|
||||
if (spilled) {
|
||||
result += "*";
|
||||
}
|
||||
switch (kind) {
|
||||
case Kind::STACK:
|
||||
result += fmt::format("s[{:2d}]", stack_slot);
|
||||
break;
|
||||
case Kind::REGISTER:
|
||||
result += emitter::gRegInfo.get_info(reg).name;
|
||||
break;
|
||||
case Kind::UNASSIGNED:
|
||||
result += "unassigned";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool occupies_same_reg(const Assignment& other) const { return other.reg == reg && (reg != -1); }
|
||||
|
||||
bool occupies_reg(emitter::Register other_reg) const { return reg == other_reg && (reg != -1); }
|
||||
|
||||
bool is_assigned() const { return kind != Kind::UNASSIGNED; }
|
||||
};
|
||||
|
||||
#endif // JAK_ASSIGNMENT_H
|
@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* @file StackOp.h
|
||||
* An operation that's added to an Instruction so that it loads/stores things from the stack if
|
||||
* needed for spilling.
|
||||
*/
|
||||
|
||||
#ifndef JAK_STACKOP_H
|
||||
#define JAK_STACKOP_H
|
||||
|
||||
#include <vector>
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "goalc/emitter/Register.h"
|
||||
|
||||
struct StackOp {
|
||||
struct Op {
|
||||
int slot = -1;
|
||||
emitter::Register reg;
|
||||
bool load = false; // load from reg before instruction?
|
||||
bool store = false; // store into reg after instruction?
|
||||
};
|
||||
|
||||
std::vector<Op> ops;
|
||||
|
||||
std::string print() const {
|
||||
std::string result;
|
||||
bool added = false;
|
||||
for (const auto& op : ops) {
|
||||
if (op.load) {
|
||||
result += fmt::format("{} <- [{:2d}], ", emitter::gRegInfo.get_info(op.reg).name, op.slot);
|
||||
added = true;
|
||||
}
|
||||
if (op.store) {
|
||||
result += fmt::format("{} -> [{:2d}], ", emitter::gRegInfo.get_info(op.reg).name, op.slot);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (added) {
|
||||
result.pop_back();
|
||||
result.pop_back();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // JAK_STACKOP_H
|
@ -15,9 +15,7 @@
|
||||
#include <vector>
|
||||
#include "goalc/emitter/Register.h"
|
||||
#include "IRegister.h"
|
||||
#include "StackOp.h"
|
||||
#include "Assignment.h"
|
||||
#include "LiveInfo.h"
|
||||
#include "allocate_common.h"
|
||||
|
||||
/*!
|
||||
* Information about an instruction needed for register allocation.
|
||||
|
54
goalc/regalloc/allocate_common.cpp
Normal file
54
goalc/regalloc/allocate_common.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "allocate_common.h"
|
||||
|
||||
std::string StackOp::print() const {
|
||||
std::string result;
|
||||
bool added = false;
|
||||
for (const auto& op : ops) {
|
||||
if (op.load) {
|
||||
result += fmt::format("{} <- [{:2d}], ", emitter::gRegInfo.get_info(op.reg).name, op.slot);
|
||||
added = true;
|
||||
}
|
||||
if (op.store) {
|
||||
result += fmt::format("{} -> [{:2d}], ", emitter::gRegInfo.get_info(op.reg).name, op.slot);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (added) {
|
||||
result.pop_back();
|
||||
result.pop_back();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Assignment::to_string() const {
|
||||
std::string result;
|
||||
if (spilled) {
|
||||
result += "*";
|
||||
}
|
||||
switch (kind) {
|
||||
case Kind::STACK:
|
||||
result += fmt::format("s[{:2d}]", stack_slot);
|
||||
break;
|
||||
case Kind::REGISTER:
|
||||
result += emitter::gRegInfo.get_info(reg).name;
|
||||
break;
|
||||
case Kind::UNASSIGNED:
|
||||
result += "unassigned";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string LiveInfo::print_assignment() {
|
||||
std::string result = "Assignment for var " + std::to_string(var) + "\n";
|
||||
for (uint32_t i = 0; i < assignment.size(); i++) {
|
||||
result += fmt::format("i[{:3d}] {}\n", i + min, assignment.at(i).to_string());
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,13 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef JAK_LIVEINFO_H
|
||||
#define JAK_LIVEINFO_H
|
||||
|
||||
#include <cassert>
|
||||
#ifndef JAK_ALLOCATE_COMMON_H
|
||||
#define JAK_ALLOCATE_COMMON_H
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include "Assignment.h"
|
||||
#include "goalc/emitter/Register.h"
|
||||
|
||||
/*!
|
||||
* An operation that's added to an Instruction so that it loads/stores things from the stack if
|
||||
* needed for spilling.
|
||||
*/
|
||||
struct StackOp {
|
||||
struct Op {
|
||||
int slot = -1;
|
||||
emitter::Register reg;
|
||||
bool load = false; // load from reg before instruction?
|
||||
bool store = false; // store into reg after instruction?
|
||||
};
|
||||
|
||||
std::vector<Op> ops;
|
||||
|
||||
std::string print() const;
|
||||
};
|
||||
|
||||
/*!
|
||||
* The assignment of an IRegister to a real Register.
|
||||
* For a single IR Instruction.
|
||||
*/
|
||||
struct Assignment {
|
||||
enum class Kind { STACK, REGISTER, UNASSIGNED } kind = Kind::UNASSIGNED;
|
||||
emitter::Register reg = -1; //! where the IRegister is now
|
||||
int stack_slot = -1; //! index of the slot, if we are ever spilled
|
||||
bool spilled = false; //! are we ever spilled
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
bool occupies_same_reg(const Assignment& other) const { return other.reg == reg && (reg != -1); }
|
||||
|
||||
bool occupies_reg(emitter::Register other_reg) const { return reg == other_reg && (reg != -1); }
|
||||
|
||||
bool is_assigned() const { return kind != Kind::UNASSIGNED; }
|
||||
};
|
||||
|
||||
// with this on, gaps in usage of registers allow other variables to steal registers.
|
||||
// this reduces stack spills/moves, but may make register allocation slower.
|
||||
@ -175,13 +206,6 @@ struct LiveInfo {
|
||||
return assignment.at(id - min);
|
||||
}
|
||||
|
||||
std::string print_assignment() {
|
||||
std::string result = "Assignment for var " + std::to_string(var) + "\n";
|
||||
for (uint32_t i = 0; i < assignment.size(); i++) {
|
||||
result += fmt::format("i[{:3d}] {}\n", i + min, assignment.at(i).to_string());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string print_assignment();
|
||||
};
|
||||
|
||||
#endif // JAK_LIVEINFO_H
|
||||
#endif // JAK_ALLOCATE_COMMON_H
|
59
scripts/analyze_build_time.py
Normal file
59
scripts/analyze_build_time.py
Normal file
@ -0,0 +1,59 @@
|
||||
# this script is used to analyze the output of the build when running
|
||||
# export CXX="/usr/bin/time -p g++"; cmake ..
|
||||
# you must run this on a fresh build.
|
||||
|
||||
# This only works on linux and relies on the output of cmake looking a certain way
|
||||
|
||||
import argparse
|
||||
import re
|
||||
|
||||
def get_time(line):
|
||||
return float(line.split()[-1])
|
||||
|
||||
def parse_file(lines):
|
||||
scanning_pattern = re.compile("Scanning dependencies of target \\w+\\n")
|
||||
building_cxx_pattern = re.compile("\\[....\\] Building CXX object .+\\n")
|
||||
current_target = "UNKNOWN"
|
||||
all_builds = []
|
||||
time_by_target = dict()
|
||||
count_by_target = dict()
|
||||
total_real_time = 0.0
|
||||
for i in range(len(lines)):
|
||||
if(scanning_pattern.match(lines[i])):
|
||||
current_target = lines[i][:-1].split()[-1]
|
||||
print("current_target is {}".format(current_target))
|
||||
if current_target not in time_by_target:
|
||||
time_by_target[current_target] = 0.0
|
||||
count_by_target[current_target] = 0
|
||||
elif(building_cxx_pattern.match(lines[i])):
|
||||
obj = lines[i][:-3].split('/')[-1]
|
||||
real_time = get_time(lines[i+1])
|
||||
user_time = get_time(lines[i+2])
|
||||
sys_time = get_time(lines[i+3])
|
||||
i += 3
|
||||
print(" building cxx is {}: {}, {}, {}".format(obj, real_time, user_time, sys_time))
|
||||
all_builds.append((obj, real_time, user_time, sys_time))
|
||||
total_real_time += real_time
|
||||
time_by_target[current_target] += real_time
|
||||
count_by_target[current_target] += 1
|
||||
|
||||
print("Total build time: {}".format(total_real_time))
|
||||
for k, v in count_by_target.items():
|
||||
print("{}, {}".format(k, v))
|
||||
|
||||
print("-----------------------------------")
|
||||
for k, v in time_by_target.items():
|
||||
print("{}, {}".format(k, v))
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(dest = 'input', help = 'Input text file.')
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.input, "r") as f:
|
||||
parse_file(f.readlines())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -12,12 +12,8 @@ add_executable(goalc-test
|
||||
all_jak1_symbols.cpp
|
||||
test_type_system.cpp
|
||||
test_CodeTester.cpp
|
||||
test_emitter_slow.cpp
|
||||
test_emitter_loads_and_store.cpp
|
||||
test_emitter_xmm32.cpp
|
||||
test_emitter_integer_math.cpp
|
||||
test_emitter.cpp
|
||||
test_common_util.cpp
|
||||
test_deftype.cpp
|
||||
test_pretty_print.cpp
|
||||
${GOALC_TEST_FRAMEWORK_SOURCES}
|
||||
${GOALC_TEST_CASES})
|
||||
|
@ -1,22 +1,7 @@
|
||||
# TODO - probably a more cmakey way to do this
|
||||
|
||||
set(GOALC_TEST_CASES
|
||||
"goalc/test_arithmetic.cpp"
|
||||
"goalc/test_compiler.cpp"
|
||||
"goalc/test_control_statements.cpp"
|
||||
"goalc/test_collections.cpp"
|
||||
"goalc/test_float.cpp"
|
||||
"goalc/test_functions.cpp"
|
||||
"goalc/test_library.cpp"
|
||||
"goalc/test_logic.cpp"
|
||||
"goalc/test_loop_recur.cpp"
|
||||
"goalc/test_macros.cpp"
|
||||
"goalc/test_methods.cpp"
|
||||
"goalc/test_pointers.cpp"
|
||||
"goalc/test_strings.cpp"
|
||||
"goalc/test_symbols.cpp"
|
||||
"goalc/test_variables.cpp"
|
||||
"goalc/test_with_game.cpp"
|
||||
"goalc/all_goalc_tests.cpp"
|
||||
)
|
||||
|
||||
set(GOALC_TEST_FRAMEWORK_SOURCES
|
||||
|
16
test/goalc/all_goalc_tests.cpp
Normal file
16
test/goalc/all_goalc_tests.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "test_arithmetic.cpp"
|
||||
#include "test_collections.cpp"
|
||||
#include "test_compiler.cpp"
|
||||
#include "test_control_statements.cpp"
|
||||
#include "test_float.cpp"
|
||||
#include "test_functions.cpp"
|
||||
#include "test_library.cpp"
|
||||
#include "test_logic.cpp"
|
||||
#include "test_loop_recur.cpp"
|
||||
#include "test_macros.cpp"
|
||||
#include "test_methods.cpp"
|
||||
#include "test_pointers.cpp"
|
||||
#include "test_strings.cpp"
|
||||
#include "test_symbols.cpp"
|
||||
#include "test_variables.cpp"
|
||||
#include "test_with_game.cpp"
|
@ -1,5 +1,6 @@
|
||||
|
||||
#include "test_runner.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "third-party/json.hpp"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include <test/goalc/framework/test_runner.h>
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
@ -1,48 +0,0 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include "common/type_system/deftype.h"
|
||||
#include "common/goos/Reader.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
TEST(Deftype, deftype) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
std::string input =
|
||||
"(deftype my-type (basic) ((f1 int64) (f2 string) (f3 int8) (f4 type :inline)))";
|
||||
goos::Reader reader;
|
||||
auto in = reader.read_from_string(input).as_pair()->cdr.as_pair()->car.as_pair()->cdr;
|
||||
auto result = parse_deftype(in, &ts);
|
||||
|
||||
auto& f = dynamic_cast<StructureType*>(ts.lookup_type(result.type))->fields();
|
||||
EXPECT_EQ(f.size(), 5);
|
||||
|
||||
auto& tf = f.at(0);
|
||||
EXPECT_EQ(tf.name(), "type");
|
||||
EXPECT_EQ(tf.offset(), 0);
|
||||
EXPECT_EQ(tf.type().print(), "type");
|
||||
EXPECT_EQ(tf.is_inline(), false);
|
||||
|
||||
auto& f1 = f.at(1);
|
||||
EXPECT_EQ(f1.name(), "f1");
|
||||
EXPECT_EQ(f1.offset(), 8);
|
||||
EXPECT_EQ(f1.type().print(), "int64");
|
||||
EXPECT_EQ(f1.is_inline(), false);
|
||||
|
||||
auto& f2 = f.at(2);
|
||||
EXPECT_EQ(f2.name(), "f2");
|
||||
EXPECT_EQ(f2.offset(), 16);
|
||||
EXPECT_EQ(f2.type().print(), "string");
|
||||
EXPECT_EQ(f2.is_inline(), false);
|
||||
|
||||
auto& f3 = f.at(3);
|
||||
EXPECT_EQ(f3.name(), "f3");
|
||||
EXPECT_EQ(f3.offset(), 20);
|
||||
EXPECT_EQ(f3.type().print(), "int8");
|
||||
EXPECT_EQ(f3.is_inline(), false);
|
||||
|
||||
auto& f4 = f.at(4);
|
||||
EXPECT_EQ(f4.name(), "f4");
|
||||
EXPECT_EQ(f4.offset(), 32);
|
||||
EXPECT_EQ(f4.type().print(), "type");
|
||||
EXPECT_EQ(f4.is_inline(), true);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,628 +0,0 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
|
||||
using namespace emitter;
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_imm8s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val + imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::add_gpr64_imm8s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::add_gpr64_imm8s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 c4 0c");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_imm32s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val + imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::add_gpr64_imm32s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::add_gpr64_imm32s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 c4 0c 00 00 00");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_imm8s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val - imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::sub_gpr64_imm8s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::sub_gpr64_imm8s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 ec 0c");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_imm32s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val - imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::sub_gpr64_imm32s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::sub_gpr64_imm32s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 ec 0c 00 00 00");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 + v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::add_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 - v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::sub_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, mul_gpr32_gpr32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s32> vals = {
|
||||
0, 1, -2, -20, 123123, INT32_MIN, INT32_MAX, INT32_MIN + 1, INT32_MAX - 1};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
// this is kind of weird behavior, but it's what the PS2 CPU does, I think.
|
||||
// the lower 32-bits of the result are sign extended, even if this sign doesn't match
|
||||
// the sign of the real product. This is true for both signed and unsigned multiply.
|
||||
auto expected = ((s64(v1) * s64(v2)) << 32) >> 32;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (s64)v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, (s64)v2));
|
||||
tester.emit(IGen::imul_gpr32_gpr32(i, j));
|
||||
tester.emit(IGen::movsx_r64_r32(RAX, i)); // weird PS2 sign extend.
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
if (result != expected) {
|
||||
fmt::print("fail {} x {}: {}\n", v1, v2, tester.dump_to_hex_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, or_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 | v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::or_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, and_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 & v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::and_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, xor_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 ^ v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::xor_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, not_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
auto expected = ~v1;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::not_gpr64(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shl_gpr64_cl) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP || i == RCX) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v << sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
|
||||
tester.emit(IGen::shl_gpr64_cl(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shr_gpr64_cl) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
|
||||
INT64_MAX, 117, 32, u64(-348473), 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP || i == RCX) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
|
||||
tester.emit(IGen::shr_gpr64_cl(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sar_gpr64_cl) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP || i == RCX) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
|
||||
tester.emit(IGen::sar_gpr64_cl(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shl_gpr64_u8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v << sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::shl_gpr64_u8(i, sa));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shr_gpr64_u8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
|
||||
INT64_MAX, 117, 32, u64(-348473), 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::shr_gpr64_u8(i, sa));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sar_gpr64_u8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::sar_gpr64_u8(i, sa));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, jumps) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<int> reads;
|
||||
|
||||
auto x = IGen::jmp_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::je_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jne_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jle_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jge_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jl_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jg_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jbe_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jae_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jb_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::ja_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
for (auto off : reads) {
|
||||
EXPECT_EQ(0, tester.read<s32>(off));
|
||||
}
|
||||
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true),
|
||||
"E9000000000F84000000000F85000000000F8E000000000F8D000000000F8C000000000F8F000000000F86"
|
||||
"000000000F83000000000F82000000000F8700000000");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, null) {
|
||||
auto instr = IGen::null();
|
||||
EXPECT_EQ(0, instr.emit(nullptr));
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*!
|
||||
* @file test_emitter_slow.cpp
|
||||
* Tests for the emitter which take over 1 second. (Checking 10,000's of functions).
|
||||
*
|
||||
* It may make sense to exclude these tests when developing to save time.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
//
|
||||
using namespace emitter;
|
||||
|
||||
TEST(EmitterSlow, xmm32_move) {
|
||||
std::vector<u32> u32_constants = {0, INT32_MAX, UINT32_MAX, 17};
|
||||
|
||||
// test moving between xmms (32-bit) and gprs.
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
for (auto constant : u32_constants) {
|
||||
for (int r1 = 0; r1 < 16; r1++) {
|
||||
if (r1 == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int r2 = 0; r2 < 16; r2++) {
|
||||
if (r2 == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int r3 = 0; r3 < 16; r3++) {
|
||||
for (int r4 = 0; r4 < 16; r4++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// move constant to gpr
|
||||
tester.emit(IGen::mov_gpr64_u32(r1, constant));
|
||||
// move gpr to xmm
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + r3, r1));
|
||||
// move xmm to xmm
|
||||
tester.emit(IGen::mov_xmm32_xmm32(XMM0 + r4, XMM0 + r3));
|
||||
// move xmm to gpr
|
||||
tester.emit(IGen::movd_gpr32_xmm32(r2, XMM0 + r4));
|
||||
// return!
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, r2));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,659 +0,0 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
using namespace emitter;
|
||||
|
||||
TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(XMM3, RAX, RBX));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 1c 03");
|
||||
|
||||
int iter = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < 16; k++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// fill k with junk
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(XMM0 + k, i, j));
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 3 * sizeof(float), 0, 0), 3.45f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 2 * sizeof(float), 0, 0), 1.23f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 4 * sizeof(float), 0, 0), 5.67f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 5 * sizeof(float), 0, 0), 0);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM3, RAX, RBX, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 5c 03 ff");
|
||||
|
||||
auto instr = IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM3, RBX, RSI, -3);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(s8(buff[instr.offset_of_disp()]), -3);
|
||||
|
||||
int iter = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < 16; k++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// fill k with junk
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM0 + k, i, j, -3));
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 3 * sizeof(float) + 3, 0, 0), 3.45f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 2 * sizeof(float) + 3, 0, 0), 1.23f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 4 * sizeof(float) + 3, 0, 0), 5.67f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 5 * sizeof(float) + 3, 0, 0), 0);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM3, RAX, RBX, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 9c 03 ff ff ff ff");
|
||||
|
||||
auto instr = IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM3, RBX, RSI, -1234);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(*(s32*)(buff + instr.offset_of_disp()), -1234);
|
||||
|
||||
int iter = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < 16; k++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// fill k with junk
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN;
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM0 + k, i, j, offset));
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 3 * sizeof(float) - offset, 0, 0), 3.45f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 2 * sizeof(float) - offset, 0, 0), 1.23f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 4 * sizeof(float) - offset, 0, 0), 5.67f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 5 * sizeof(float) - offset, 0, 0), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
float as_float(T x) {
|
||||
float result;
|
||||
memcpy(&result, &x, sizeof(float));
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 as_u32(float x) {
|
||||
u32 result;
|
||||
memcpy(&result, &x, 4);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(RAX, RBX, XMM7));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 3c 03");
|
||||
|
||||
int iter = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int k = 0; k < 16; k++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value
|
||||
|
||||
// pop value into addr1 GPR
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
// move to XMM
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop addrs
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
tester.emit(IGen::pop_gpr64(j));
|
||||
|
||||
// store
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(i, j, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
tester.execute((u64)memory, 12, as_u32(1.234f), 0);
|
||||
EXPECT_EQ(memory[2], 1.23f);
|
||||
EXPECT_EQ(memory[3], 1.234f);
|
||||
EXPECT_EQ(memory[4], 5.67f);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(RAX, RBX, XMM3, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 5c 03 ff");
|
||||
|
||||
auto instr = IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(RBX, RSI, XMM3, -3);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(s8(buff[instr.offset_of_disp()]), -3);
|
||||
|
||||
int iter = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < 16; k++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value
|
||||
|
||||
// pop value into addr1 GPR
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
// move to XMM
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop addrs
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
tester.emit(IGen::pop_gpr64(j));
|
||||
|
||||
s64 offset = (iter & 1) ? INT8_MAX : INT8_MIN;
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(i, j, XMM0 + k, offset));
|
||||
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
tester.execute((u64)memory, 12 - offset, as_u32(1.234f), 0);
|
||||
EXPECT_EQ(memory[2], 1.23f);
|
||||
EXPECT_EQ(memory[3], 1.234f);
|
||||
EXPECT_EQ(memory[4], 5.67f);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(RAX, RBX, XMM3, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 9c 03 ff ff ff ff");
|
||||
|
||||
auto instr = IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(RBX, RSI, XMM3, -1234);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(*(s32*)(buff + instr.offset_of_disp()), -1234);
|
||||
|
||||
int iter = 0;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (int k = 0; k < 16; k++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value
|
||||
|
||||
// pop value into addr1 GPR
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
// move to XMM
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop addrs
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
tester.emit(IGen::pop_gpr64(j));
|
||||
|
||||
s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN;
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(i, j, XMM0 + k, offset));
|
||||
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
tester.execute((u64)memory, 12 - offset, as_u32(1.234f), 0);
|
||||
EXPECT_EQ(memory[2], 1.23f);
|
||||
EXPECT_EQ(memory[3], 1.234f);
|
||||
EXPECT_EQ(memory[4], 5.67f);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, static_load_xmm32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
auto loc_of_load = tester.size();
|
||||
auto load_instr = IGen::static_load_xmm32(XMM0 + i, INT32_MAX);
|
||||
|
||||
tester.emit(load_instr);
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto loc_of_float = tester.emit_data(float(1.2345f));
|
||||
|
||||
// patch offset
|
||||
tester.write<s32>(loc_of_float - loc_of_load - load_instr.length(),
|
||||
loc_of_load + load_instr.offset_of_disp());
|
||||
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, 1.2345f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, static_store_xmm32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
auto loc_of_store = tester.size();
|
||||
auto store_instr = IGen::static_store_xmm32(XMM0 + i, INT32_MAX);
|
||||
|
||||
tester.emit(store_instr);
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto loc_of_float = tester.emit_data(float(1.2345f));
|
||||
|
||||
tester.write<s32>(loc_of_float - loc_of_store - store_instr.length(),
|
||||
loc_of_store + store_instr.offset_of_disp());
|
||||
tester.execute(as_u32(-44.567f), 0, 0, 0);
|
||||
EXPECT_EQ(-44.567f, tester.read<float>(loc_of_float));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, ucomiss) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::cmp_flt_flt(XMM13, XMM14));
|
||||
EXPECT_EQ("45 0f 2e ee", tester.dump_to_hex_string());
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, mul) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = f * g;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::mulss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, div) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = g / f;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::divss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, add) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = g + f;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::addss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, sub) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = g - f;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::subss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, float_to_int) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f,
|
||||
7.545f, 0.1f, 0.9f, -0.1f, -0.9f};
|
||||
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP) {
|
||||
continue;
|
||||
}
|
||||
s32 expected = g;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
tester.emit(IGen::float_to_int32(j, XMM0 + i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s32>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, int_to_float) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MAX, -3457343, 7, INT32_MIN};
|
||||
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP) {
|
||||
continue;
|
||||
}
|
||||
float expected = g;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(j, g));
|
||||
tester.emit(IGen::int32_to_float(XMM0 + i, j));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "common/type_system/TypeSystem.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "common/goos/Reader.h"
|
||||
#include "common/type_system/deftype.h"
|
||||
|
||||
TEST(TypeSystem, Construction) {
|
||||
// test that we can add all builtin types without any type errors
|
||||
@ -391,4 +392,47 @@ TEST(TypeSystem, DecompLookupsMethod) {
|
||||
EXPECT_EQ(result.deref_path.at(1).index, 2);
|
||||
}
|
||||
|
||||
TEST(Deftype, deftype) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
std::string input =
|
||||
"(deftype my-type (basic) ((f1 int64) (f2 string) (f3 int8) (f4 type :inline)))";
|
||||
goos::Reader reader;
|
||||
auto in = reader.read_from_string(input).as_pair()->cdr.as_pair()->car.as_pair()->cdr;
|
||||
auto result = parse_deftype(in, &ts);
|
||||
|
||||
auto& f = dynamic_cast<StructureType*>(ts.lookup_type(result.type))->fields();
|
||||
EXPECT_EQ(f.size(), 5);
|
||||
|
||||
auto& tf = f.at(0);
|
||||
EXPECT_EQ(tf.name(), "type");
|
||||
EXPECT_EQ(tf.offset(), 0);
|
||||
EXPECT_EQ(tf.type().print(), "type");
|
||||
EXPECT_EQ(tf.is_inline(), false);
|
||||
|
||||
auto& f1 = f.at(1);
|
||||
EXPECT_EQ(f1.name(), "f1");
|
||||
EXPECT_EQ(f1.offset(), 8);
|
||||
EXPECT_EQ(f1.type().print(), "int64");
|
||||
EXPECT_EQ(f1.is_inline(), false);
|
||||
|
||||
auto& f2 = f.at(2);
|
||||
EXPECT_EQ(f2.name(), "f2");
|
||||
EXPECT_EQ(f2.offset(), 16);
|
||||
EXPECT_EQ(f2.type().print(), "string");
|
||||
EXPECT_EQ(f2.is_inline(), false);
|
||||
|
||||
auto& f3 = f.at(3);
|
||||
EXPECT_EQ(f3.name(), "f3");
|
||||
EXPECT_EQ(f3.offset(), 20);
|
||||
EXPECT_EQ(f3.type().print(), "int8");
|
||||
EXPECT_EQ(f3.is_inline(), false);
|
||||
|
||||
auto& f4 = f.at(4);
|
||||
EXPECT_EQ(f4.name(), "f4");
|
||||
EXPECT_EQ(f4.offset(), 32);
|
||||
EXPECT_EQ(f4.type().print(), "type");
|
||||
EXPECT_EQ(f4.is_inline(), true);
|
||||
}
|
||||
|
||||
// TODO - a big test to make sure all the builtin types are what we expect.
|
||||
|
Loading…
Reference in New Issue
Block a user