Try to speed up the build (#106)

* first attempt

* attempt 2

* windows and formatting fix
This commit is contained in:
water111 2020-10-29 21:27:52 -04:00 committed by GitHub
parent 985549f27c
commit a45d180f2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 2227 additions and 2222 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,5 @@
#include <cassert>
#include "common/goos/PrettyPrinter.h"
#include "decompiler/Disasm/InstructionMatching.h"
#include "decompiler/ObjectFile/LinkedObjectFile.h"
#include "CfgVtx.h"

View File

@ -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.

View File

@ -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),

View File

@ -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 {

View File

@ -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 {
/*!

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View 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>;

View File

@ -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,

View File

@ -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.

View 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.

View 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.

View 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");
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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>

View File

@ -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"

View File

@ -1,4 +1,5 @@
#include <stdexcept>
#include "third-party/fmt/core.h"
#include "Env.h"
#include "IR.h"

View File

@ -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;

View File

@ -1,3 +1,4 @@
#include "third-party/fmt/core.h"
#include "Val.h"
#include "Env.h"
#include "IR.h"

View File

@ -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"

View File

@ -5,6 +5,7 @@
#include "goalc/compiler/Compiler.h"
#include "goalc/logger/Logger.h"
#include "third-party/fmt/core.h"
namespace {
/*!

View File

@ -1,4 +1,5 @@
#include "goalc/compiler/Compiler.h"
#include "third-party/fmt/core.h"
using namespace goos;

View File

@ -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) {

View File

@ -1,4 +1,5 @@
#include "goalc/compiler/Compiler.h"
#include "third-party/fmt/core.h"
#include "common/type_system/deftype.h"
namespace {

View File

@ -9,7 +9,6 @@
#define JAK_REGISTER_H
#include <cassert>
#include <functional>
#include <array>
#include <vector>
#include <string>

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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.

View 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;
}

View File

@ -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

View 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()

View File

@ -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})

View File

@ -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

View 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"

View File

@ -1,5 +1,6 @@
#include "test_runner.h"
#include "third-party/fmt/core.h"
#include <string>

View File

@ -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>

View File

@ -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

View File

@ -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));
}

View File

@ -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();
}
}
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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.