diff --git a/common/goos/CMakeLists.txt b/common/goos/CMakeLists.txt index cc8c4fe041..10aef75320 100644 --- a/common/goos/CMakeLists.txt +++ b/common/goos/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/common/goos/Interpreter.cpp b/common/goos/Interpreter.cpp index 8833619530..6502a683cd 100644 --- a/common/goos/Interpreter.cpp +++ b/common/goos/Interpreter.cpp @@ -6,6 +6,7 @@ #include #include "Interpreter.h" +#include 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& 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& 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& 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& 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& 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& 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& 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& 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 +Object Interpreter::num_plus(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)env; + (void)form; + T result = 0; + for (const auto& arg : args.unnamed) { + result += number(arg); + } + return Object::make_number(result); +} + +/*! + * Addition + */ +Object Interpreter::eval_plus(const Object& form, + Arguments& args, + const std::shared_ptr& 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(form, args, env); + + case ObjectType::FLOAT: + return num_plus(form, args, env); + + default: + throw_eval_error(form, "+ must have a numeric argument"); + return EmptyListObject::make_new(); + } +} + +/*! + * Template implementation of multiplication. + */ +template +Object Interpreter::num_times(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)env; + (void)form; + T result = 1; + for (const auto& arg : args.unnamed) { + result *= number(arg); + } + return Object::make_number(result); +} + +/*! + * Multiplication + */ +Object Interpreter::eval_times(const Object& form, + Arguments& args, + const std::shared_ptr& 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(form, args, env); + + case ObjectType::FLOAT: + return num_times(form, args, env); + + default: + throw_eval_error(form, "* must have a numeric argument"); + return EmptyListObject::make_new(); + } +} + +/*! + * Template implementation of subtraction. + */ +template +Object Interpreter::num_minus(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)env; + (void)form; + T result; + if (args.unnamed.size() > 1) { + result = number(args.unnamed[0]); + for (uint32_t i = 1; i < args.unnamed.size(); i++) { + result -= number(args.unnamed[i]); + } + } else { + result = -number(args.unnamed[0]); + } + return Object::make_number(result); +} + +/*! + * Subtraction + */ +Object Interpreter::eval_minus(const Object& form, + Arguments& args, + const std::shared_ptr& 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(form, args, env); + + case ObjectType::FLOAT: + return num_minus(form, args, env); + + default: + throw_eval_error(form, "- must have a numeric argument"); + return EmptyListObject::make_new(); + } +} + +/*! + * Template implementation of division. + */ +template +Object Interpreter::num_divide(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)env; + (void)form; + T result = number(args.unnamed[0]) / number(args.unnamed[1]); + return Object::make_number(result); +} + +/*! + * Division + */ +Object Interpreter::eval_divide(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + vararg_check(form, args, {{}, {}}, {}); + switch (args.unnamed.front().type) { + case ObjectType::INTEGER: + return num_divide(form, args, env); + + case ObjectType::FLOAT: + return num_divide(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& 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 +Object Interpreter::num_lt(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)form; + (void)env; + T a = number(args.unnamed[0]); + T b = number(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& env) { + vararg_check(form, args, {{}, {}}, {}); + switch (args.unnamed.front().type) { + case ObjectType::INTEGER: + return num_lt(form, args, env); + + case ObjectType::FLOAT: + return num_lt(form, args, env); + + default: + throw_eval_error(form, "< must have a numeric argument"); + return EmptyListObject::make_new(); + } +} + +template +Object Interpreter::num_gt(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)form; + (void)env; + T a = number(args.unnamed[0]); + T b = number(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& env) { + vararg_check(form, args, {{}, {}}, {}); + switch (args.unnamed.front().type) { + case ObjectType::INTEGER: + return num_gt(form, args, env); + + case ObjectType::FLOAT: + return num_gt(form, args, env); + + default: + throw_eval_error(form, "> must have a numeric argument"); + return EmptyListObject::make_new(); + } +} + +template +Object Interpreter::num_leq(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)form; + (void)env; + T a = number(args.unnamed[0]); + T b = number(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& env) { + vararg_check(form, args, {{}, {}}, {}); + switch (args.unnamed.front().type) { + case ObjectType::INTEGER: + return num_leq(form, args, env); + + case ObjectType::FLOAT: + return num_leq(form, args, env); + + default: + throw_eval_error(form, "<= must have a numeric argument"); + return EmptyListObject::make_new(); + } +} + +template +Object Interpreter::num_geq(const Object& form, + Arguments& args, + const std::shared_ptr& env) { + (void)form; + (void)env; + T a = number(args.unnamed[0]); + T b = number(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& env) { + vararg_check(form, args, {{}, {}}, {}); + switch (args.unnamed.front().type) { + case ObjectType::INTEGER: + return num_geq(form, args, env); + + case ObjectType::FLOAT: + return num_geq(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& env) { + vararg_check(form, args, {{}}, {}); + return eval(args.unnamed[0], env); +} + +Object Interpreter::eval_car(const Object& form, + Arguments& args, + const std::shared_ptr& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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> args2; + std::vector 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(x)); + } + + auto formatted = + fmt::vformat(format_str.as_string()->data, + fmt::format_args(args2.data(), static_cast(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& 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 diff --git a/common/goos/InterpreterEval.cpp b/common/goos/InterpreterEval.cpp deleted file mode 100644 index 9bbbaf0016..0000000000 --- a/common/goos/InterpreterEval.cpp +++ /dev/null @@ -1,640 +0,0 @@ -/*! - * @file InterpreterEval.cpp - * Implementation of built-in GOOS functions. - */ - -#include -#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& 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& 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& 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& 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& 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& 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& 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& 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 -Object Interpreter::num_plus(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)env; - (void)form; - T result = 0; - for (const auto& arg : args.unnamed) { - result += number(arg); - } - return Object::make_number(result); -} - -/*! - * Addition - */ -Object Interpreter::eval_plus(const Object& form, - Arguments& args, - const std::shared_ptr& 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(form, args, env); - - case ObjectType::FLOAT: - return num_plus(form, args, env); - - default: - throw_eval_error(form, "+ must have a numeric argument"); - return EmptyListObject::make_new(); - } -} - -/*! - * Template implementation of multiplication. - */ -template -Object Interpreter::num_times(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)env; - (void)form; - T result = 1; - for (const auto& arg : args.unnamed) { - result *= number(arg); - } - return Object::make_number(result); -} - -/*! - * Multiplication - */ -Object Interpreter::eval_times(const Object& form, - Arguments& args, - const std::shared_ptr& 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(form, args, env); - - case ObjectType::FLOAT: - return num_times(form, args, env); - - default: - throw_eval_error(form, "* must have a numeric argument"); - return EmptyListObject::make_new(); - } -} - -/*! - * Template implementation of subtraction. - */ -template -Object Interpreter::num_minus(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)env; - (void)form; - T result; - if (args.unnamed.size() > 1) { - result = number(args.unnamed[0]); - for (uint32_t i = 1; i < args.unnamed.size(); i++) { - result -= number(args.unnamed[i]); - } - } else { - result = -number(args.unnamed[0]); - } - return Object::make_number(result); -} - -/*! - * Subtraction - */ -Object Interpreter::eval_minus(const Object& form, - Arguments& args, - const std::shared_ptr& 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(form, args, env); - - case ObjectType::FLOAT: - return num_minus(form, args, env); - - default: - throw_eval_error(form, "- must have a numeric argument"); - return EmptyListObject::make_new(); - } -} - -/*! - * Template implementation of division. - */ -template -Object Interpreter::num_divide(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)env; - (void)form; - T result = number(args.unnamed[0]) / number(args.unnamed[1]); - return Object::make_number(result); -} - -/*! - * Division - */ -Object Interpreter::eval_divide(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - vararg_check(form, args, {{}, {}}, {}); - switch (args.unnamed.front().type) { - case ObjectType::INTEGER: - return num_divide(form, args, env); - - case ObjectType::FLOAT: - return num_divide(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& 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 -Object Interpreter::num_lt(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)form; - (void)env; - T a = number(args.unnamed[0]); - T b = number(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& env) { - vararg_check(form, args, {{}, {}}, {}); - switch (args.unnamed.front().type) { - case ObjectType::INTEGER: - return num_lt(form, args, env); - - case ObjectType::FLOAT: - return num_lt(form, args, env); - - default: - throw_eval_error(form, "< must have a numeric argument"); - return EmptyListObject::make_new(); - } -} - -template -Object Interpreter::num_gt(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)form; - (void)env; - T a = number(args.unnamed[0]); - T b = number(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& env) { - vararg_check(form, args, {{}, {}}, {}); - switch (args.unnamed.front().type) { - case ObjectType::INTEGER: - return num_gt(form, args, env); - - case ObjectType::FLOAT: - return num_gt(form, args, env); - - default: - throw_eval_error(form, "> must have a numeric argument"); - return EmptyListObject::make_new(); - } -} - -template -Object Interpreter::num_leq(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)form; - (void)env; - T a = number(args.unnamed[0]); - T b = number(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& env) { - vararg_check(form, args, {{}, {}}, {}); - switch (args.unnamed.front().type) { - case ObjectType::INTEGER: - return num_leq(form, args, env); - - case ObjectType::FLOAT: - return num_leq(form, args, env); - - default: - throw_eval_error(form, "<= must have a numeric argument"); - return EmptyListObject::make_new(); - } -} - -template -Object Interpreter::num_geq(const Object& form, - Arguments& args, - const std::shared_ptr& env) { - (void)form; - (void)env; - T a = number(args.unnamed[0]); - T b = number(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& env) { - vararg_check(form, args, {{}, {}}, {}); - switch (args.unnamed.front().type) { - case ObjectType::INTEGER: - return num_geq(form, args, env); - - case ObjectType::FLOAT: - return num_geq(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& env) { - vararg_check(form, args, {{}}, {}); - return eval(args.unnamed[0], env); -} - -Object Interpreter::eval_car(const Object& form, - Arguments& args, - const std::shared_ptr& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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> args2; - std::vector 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(x)); - } - - auto formatted = - fmt::vformat(format_str.as_string()->data, - fmt::format_args(args2.data(), static_cast(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& 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 diff --git a/decompiler/Function/CfgVtx.cpp b/decompiler/Function/CfgVtx.cpp index 09accf7291..67ece7540b 100644 --- a/decompiler/Function/CfgVtx.cpp +++ b/decompiler/Function/CfgVtx.cpp @@ -1,4 +1,5 @@ #include +#include "common/goos/PrettyPrinter.h" #include "decompiler/Disasm/InstructionMatching.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" #include "CfgVtx.h" diff --git a/decompiler/Function/CfgVtx.h b/decompiler/Function/CfgVtx.h index 66fbd218c8..4ef1e7181c 100644 --- a/decompiler/Function/CfgVtx.h +++ b/decompiler/Function/CfgVtx.h @@ -6,7 +6,10 @@ #include #include #include -#include "common/goos/PrettyPrinter.h" + +namespace goos { +class Object; +} /*! * In v, find an item equal to old, and replace it with replace. diff --git a/decompiler/Function/Function.cpp b/decompiler/Function/Function.cpp index e09989c8cf..2a788134d7 100644 --- a/decompiler/Function/Function.cpp +++ b/decompiler/Function/Function.cpp @@ -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 gpr_backups = {make_gpr(Reg::GP), make_gpr(Reg::S5), make_gpr(Reg::S4), diff --git a/decompiler/Function/Function.h b/decompiler/Function/Function.h index cfccd4a8ea..cdcb782622 100644 --- a/decompiler/Function/Function.h +++ b/decompiler/Function/Function.h @@ -5,13 +5,17 @@ #include #include +#include +#include #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; +class IR; struct FunctionName { enum class FunctionKind { diff --git a/decompiler/Function/TypeAnalysis.cpp b/decompiler/Function/TypeAnalysis.cpp index 38b7e43008..927ffecca2 100644 --- a/decompiler/Function/TypeAnalysis.cpp +++ b/decompiler/Function/TypeAnalysis.cpp @@ -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 { /*! diff --git a/decompiler/Function/TypeInspector.cpp b/decompiler/Function/TypeInspector.cpp index 93f375c311..f2a8ba4f18 100644 --- a/decompiler/Function/TypeInspector.cpp +++ b/decompiler/Function/TypeInspector.cpp @@ -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 { diff --git a/decompiler/Function/TypeInspector.h b/decompiler/Function/TypeInspector.h index a70c142249..4381687ec6 100644 --- a/decompiler/Function/TypeInspector.h +++ b/decompiler/Function/TypeInspector.h @@ -6,11 +6,12 @@ */ #include -#include "common/type_system/Type.h" +#include "common/common_types.h" class Function; class DecompilerTypeSystem; class LinkedObjectFile; +class Field; struct TypeInspectorResult { bool success = false; diff --git a/decompiler/IR/BasicOpBuilder.cpp b/decompiler/IR/BasicOpBuilder.cpp index 9a5a15d872..d39131083f 100644 --- a/decompiler/IR/BasicOpBuilder.cpp +++ b/decompiler/IR/BasicOpBuilder.cpp @@ -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 { diff --git a/decompiler/IR/CfgBuilder.cpp b/decompiler/IR/CfgBuilder.cpp index 71d8b6a7ed..d928800420 100644 --- a/decompiler/IR/CfgBuilder.cpp +++ b/decompiler/IR/CfgBuilder.cpp @@ -1,10 +1,11 @@ -#include "third-party/fmt/format.h" +#include "third-party/fmt/core.h" #include #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 { diff --git a/decompiler/IR/IR.cpp b/decompiler/IR/IR.cpp index 7985c288e9..2fbb404e2e 100644 --- a/decompiler/IR/IR.cpp +++ b/decompiler/IR/IR.cpp @@ -1,5 +1,6 @@ #include "IR.h" #include "decompiler/ObjectFile/LinkedObjectFile.h" +#include "common/goos/PrettyPrinter.h" std::vector> IR::get_all_ir(LinkedObjectFile& file) const { (void)file; diff --git a/decompiler/IR/IR.h b/decompiler/IR/IR.h index 604e3434f4..2e7bec8c61 100644 --- a/decompiler/IR/IR.h +++ b/decompiler/IR/IR.h @@ -3,13 +3,18 @@ #include #include +#include +#include #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; diff --git a/decompiler/IR/IR_TypeAnalysis.cpp b/decompiler/IR/IR_TypeAnalysis.cpp index e5d5640f83..8741a23911 100644 --- a/decompiler/IR/IR_TypeAnalysis.cpp +++ b/decompiler/IR/IR_TypeAnalysis.cpp @@ -1,6 +1,7 @@ #include #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, diff --git a/decompiler/ObjectFile/LinkedObjectFile.cpp b/decompiler/ObjectFile/LinkedObjectFile.cpp index f3ecb71407..6f38d0422d 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.cpp +++ b/decompiler/ObjectFile/LinkedObjectFile.cpp @@ -7,12 +7,14 @@ #include #include #include -#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. diff --git a/decompiler/ObjectFile/LinkedObjectFile.h b/decompiler/ObjectFile/LinkedObjectFile.h index d2bff079fd..6f4fbc01c1 100644 --- a/decompiler/ObjectFile/LinkedObjectFile.h +++ b/decompiler/ObjectFile/LinkedObjectFile.h @@ -15,7 +15,7 @@ #include #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. diff --git a/decompiler/ObjectFile/ObjectFileDB.h b/decompiler/ObjectFile/ObjectFileDB.h index 962935ce4e..cfb44bdf83 100644 --- a/decompiler/ObjectFile/ObjectFileDB.h +++ b/decompiler/ObjectFile/ObjectFileDB.h @@ -16,6 +16,7 @@ #include #include "LinkedObjectFile.h" #include "decompiler/util/DecompilerTypeSystem.h" +#include "common/common_types.h" /*! * A "record" which can be used to identify an object file. diff --git a/decompiler/util/DecompilerTypeSystem.cpp b/decompiler/util/DecompilerTypeSystem.cpp index 706e6a820e..4743c6b85f 100644 --- a/decompiler/util/DecompilerTypeSystem.cpp +++ b/decompiler/util/DecompilerTypeSystem.cpp @@ -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"); + } + } } \ No newline at end of file diff --git a/decompiler/util/DecompilerTypeSystem.h b/decompiler/util/DecompilerTypeSystem.h index 8d72d17418..6253914644 100644 --- a/decompiler/util/DecompilerTypeSystem.h +++ b/decompiler/util/DecompilerTypeSystem.h @@ -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& file_path); diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 06a0e85dab..43d1d12cfa 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -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) diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 66eeb00f3d..1ad7aa34a4 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.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 #include diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 049c2d99b1..f8fb923ab7 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -3,6 +3,7 @@ #ifndef JAK_COMPILER_H #define JAK_COMPILER_H +#include #include "common/type_system/TypeSystem.h" #include "Env.h" #include "goalc/listener/Listener.h" diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index d6974dcfe3..a7e06fff4d 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -1,4 +1,5 @@ #include +#include "third-party/fmt/core.h" #include "Env.h" #include "IR.h" diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index dc4656df7f..4f2fbd3e72 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -1,7 +1,7 @@ -#include "IR.h" - #include +#include "IR.h" #include "goalc/emitter/IGen.h" +#include "third-party/fmt/core.h" using namespace emitter; diff --git a/goalc/compiler/Val.cpp b/goalc/compiler/Val.cpp index 6dab69ffa2..ed69297fe3 100644 --- a/goalc/compiler/Val.cpp +++ b/goalc/compiler/Val.cpp @@ -1,3 +1,4 @@ +#include "third-party/fmt/core.h" #include "Val.h" #include "Env.h" #include "IR.h" diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index 48ad8adec0..2ca4dd174e 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -11,7 +11,6 @@ #include #include #include -#include "third-party/fmt/core.h" #include "common/type_system/TypeSystem.h" #include "goalc/regalloc/IRegister.h" #include "Lambda.h" diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp index 19be2ee0c2..7e9b13531c 100644 --- a/goalc/compiler/compilation/Function.cpp +++ b/goalc/compiler/compilation/Function.cpp @@ -5,6 +5,7 @@ #include "goalc/compiler/Compiler.h" #include "goalc/logger/Logger.h" +#include "third-party/fmt/core.h" namespace { /*! diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index aaeb4220a4..b83af2c704 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -1,4 +1,5 @@ #include "goalc/compiler/Compiler.h" +#include "third-party/fmt/core.h" using namespace goos; diff --git a/goalc/compiler/compilation/Static.cpp b/goalc/compiler/compilation/Static.cpp index a8ea2dfbb4..12ec68a3a3 100644 --- a/goalc/compiler/compilation/Static.cpp +++ b/goalc/compiler/compilation/Static.cpp @@ -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) { diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index 478d07d411..091e71d1af 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -1,4 +1,5 @@ #include "goalc/compiler/Compiler.h" +#include "third-party/fmt/core.h" #include "common/type_system/deftype.h" namespace { diff --git a/goalc/emitter/Register.h b/goalc/emitter/Register.h index 94c83cd5ab..b31ea01942 100644 --- a/goalc/emitter/Register.h +++ b/goalc/emitter/Register.h @@ -9,7 +9,6 @@ #define JAK_REGISTER_H #include -#include #include #include #include diff --git a/goalc/regalloc/Allocator.cpp b/goalc/regalloc/Allocator.cpp index 8dfd43ddb9..41d52a8b3d 100644 --- a/goalc/regalloc/Allocator.cpp +++ b/goalc/regalloc/Allocator.cpp @@ -4,8 +4,8 @@ */ #include +#include "third-party/fmt/core.h" #include "Allocator.h" -#include "LiveInfo.h" /*! * Find basic blocks and add block link info. diff --git a/goalc/regalloc/Allocator.h b/goalc/regalloc/Allocator.h index 5e750da0f3..b1540de24c 100644 --- a/goalc/regalloc/Allocator.h +++ b/goalc/regalloc/Allocator.h @@ -8,8 +8,6 @@ #include #include "IRegister.h" #include "allocate.h" -#include "LiveInfo.h" -#include "StackOp.h" struct RegAllocBasicBlock { std::vector instr_idx; diff --git a/goalc/regalloc/Assignment.h b/goalc/regalloc/Assignment.h deleted file mode 100644 index eecd8821e9..0000000000 --- a/goalc/regalloc/Assignment.h +++ /dev/null @@ -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 diff --git a/goalc/regalloc/StackOp.h b/goalc/regalloc/StackOp.h deleted file mode 100644 index 8a0d363e14..0000000000 --- a/goalc/regalloc/StackOp.h +++ /dev/null @@ -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 -#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 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 diff --git a/goalc/regalloc/allocate.h b/goalc/regalloc/allocate.h index 63698467c9..0ddc038fcc 100644 --- a/goalc/regalloc/allocate.h +++ b/goalc/regalloc/allocate.h @@ -15,9 +15,7 @@ #include #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. diff --git a/goalc/regalloc/allocate_common.cpp b/goalc/regalloc/allocate_common.cpp new file mode 100644 index 0000000000..abc39bc46a --- /dev/null +++ b/goalc/regalloc/allocate_common.cpp @@ -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; +} \ No newline at end of file diff --git a/goalc/regalloc/LiveInfo.h b/goalc/regalloc/allocate_common.h similarity index 78% rename from goalc/regalloc/LiveInfo.h rename to goalc/regalloc/allocate_common.h index 07095fa595..8a2dfc7f06 100644 --- a/goalc/regalloc/LiveInfo.h +++ b/goalc/regalloc/allocate_common.h @@ -1,13 +1,44 @@ -#pragma once - -#ifndef JAK_LIVEINFO_H -#define JAK_LIVEINFO_H - -#include +#ifndef JAK_ALLOCATE_COMMON_H +#define JAK_ALLOCATE_COMMON_H #include -#include #include -#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 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 diff --git a/scripts/analyze_build_time.py b/scripts/analyze_build_time.py new file mode 100644 index 0000000000..5e14689e6d --- /dev/null +++ b/scripts/analyze_build_time.py @@ -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() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a61bdc2f00..91c6d9a9b5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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}) diff --git a/test/goalc/CMakeLists.txt b/test/goalc/CMakeLists.txt index e5e374b193..70b071c2cf 100644 --- a/test/goalc/CMakeLists.txt +++ b/test/goalc/CMakeLists.txt @@ -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 diff --git a/test/goalc/all_goalc_tests.cpp b/test/goalc/all_goalc_tests.cpp new file mode 100644 index 0000000000..bcf93470e8 --- /dev/null +++ b/test/goalc/all_goalc_tests.cpp @@ -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" diff --git a/test/goalc/framework/test_runner.cpp b/test/goalc/framework/test_runner.cpp index c428a03ca1..b09333931a 100644 --- a/test/goalc/framework/test_runner.cpp +++ b/test/goalc/framework/test_runner.cpp @@ -1,5 +1,6 @@ #include "test_runner.h" +#include "third-party/fmt/core.h" #include diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index 085f67bbed..f1aa763e83 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -10,6 +10,7 @@ #include "third-party/json.hpp" #include "common/util/FileUtil.h" #include +#include "third-party/fmt/core.h" #include #include diff --git a/test/test_deftype.cpp b/test/test_deftype.cpp deleted file mode 100644 index 3bea903e54..0000000000 --- a/test/test_deftype.cpp +++ /dev/null @@ -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(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); -} \ No newline at end of file diff --git a/test/test_emitter_loads_and_store.cpp b/test/test_emitter.cpp similarity index 64% rename from test/test_emitter_loads_and_store.cpp rename to test/test_emitter.cpp index 02a9261800..4140d00832 100644 --- a/test/test_emitter_loads_and_store.cpp +++ b/test/test_emitter.cpp @@ -1,15 +1,628 @@ -/*! - * @file test_emitter_loads_and_stores.cpp - * Tests for the emitter which are fast (checking 100's of functions) - */ - #include "gtest/gtest.h" #include "goalc/emitter/CodeTester.h" #include "goalc/emitter/IGen.h" -#include "third-party/fmt/core.h" -// + using namespace emitter; +TEST(EmitterIntegerMath, add_gpr64_imm8s) { + CodeTester tester; + tester.init_code_buffer(256); + + std::vector vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; + std::vector 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(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 vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; + std::vector 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(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 vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; + std::vector 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(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 vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; + std::vector 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(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 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterIntegerMath, sub_gpr64_gpr64) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterIntegerMath, mul_gpr32_gpr32) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterIntegerMath, or_gpr64_gpr64) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterIntegerMath, and_gpr64_gpr64) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterIntegerMath, xor_gpr64_gpr64) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterIntegerMath, not_gpr64) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } +} + +TEST(EmitterIntegerMath, shl_gpr64_cl) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, shr_gpr64_cl) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN), + INT64_MAX, 117, 32, u64(-348473), 83747382}; + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, sar_gpr64_cl) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, shl_gpr64_u8) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, shr_gpr64_u8) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN), + INT64_MAX, 117, 32, u64(-348473), 83747382}; + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, sar_gpr64_u8) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, jumps) { + CodeTester tester; + tester.init_code_buffer(256); + + std::vector 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(off)); + } + + EXPECT_EQ(tester.dump_to_hex_string(true), + "E9000000000F84000000000F85000000000F8E000000000F8D000000000F8C000000000F8F000000000F86" + "000000000F83000000000F82000000000F8700000000"); +} + +TEST(EmitterIntegerMath, null) { + auto instr = IGen::null(); + EXPECT_EQ(0, instr.emit(nullptr)); +} + TEST(EmitterLoadsAndStores, load_constant_64_and_move_gpr_gpr_64) { std::vector u64_constants = {0, UINT64_MAX, INT64_MAX, 7, 12}; @@ -1598,12 +2211,6 @@ TEST(EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64) { EXPECT_EQ(memory[2], 3); EXPECT_EQ(memory[3], 7); EXPECT_EQ(memory[4], 1); - - if (memory[3] != 7) { - fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), - tester.reg_name(k)); - printf("%s\n", tester.dump_to_hex_string().c_str()); - } iter++; } } @@ -1667,11 +2274,6 @@ TEST(EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64_plus_s8) { EXPECT_EQ(memory[3], 7); EXPECT_EQ(memory[4], 1); - if (memory[3] != 7) { - fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), - tester.reg_name(k)); - printf("%s\n", tester.dump_to_hex_string().c_str()); - } iter++; } } @@ -1735,11 +2337,6 @@ TEST(EmitterLoadsAndStores, store8_gpr64_gpr64_plus_gpr64_plus_s32) { EXPECT_EQ(memory[3], 7); EXPECT_EQ(memory[4], 1); - if (memory[3] != 7) { - fmt::print("test {}, {}, {}\n", tester.reg_name(i), tester.reg_name(j), - tester.reg_name(k)); - printf("%s\n", tester.dump_to_hex_string().c_str()); - } iter++; } } @@ -2510,4 +3107,698 @@ TEST(EmitterLoadsAndStores, static_addr) { auto result = tester.execute(); EXPECT_EQ(result, (u64)(tester.data()) + 1); } -} \ No newline at end of file +} + +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((u64)memory, 3 * sizeof(float), 0, 0), 3.45f); + EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float), 0, 0), 1.23f); + EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float), 0, 0), 5.67f); + EXPECT_EQ(tester.execute_ret((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((u64)memory, 3 * sizeof(float) + 3, 0, 0), 3.45f); + EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float) + 3, 0, 0), 1.23f); + EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float) + 3, 0, 0), 5.67f); + EXPECT_EQ(tester.execute_ret((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((u64)memory, 3 * sizeof(float) - offset, 0, 0), 3.45f); + EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float) - offset, 0, 0), 1.23f); + EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float) - offset, 0, 0), 5.67f); + EXPECT_EQ(tester.execute_ret((u64)memory, 5 * sizeof(float) - offset, 0, 0), 0); + iter++; + } + } + } +} + +namespace { +template +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(loc_of_float - loc_of_load - load_instr.length(), + loc_of_load + load_instr.offset_of_disp()); + + auto result = tester.execute_ret(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(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(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 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, div) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, add) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, sub) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, float_to_int) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterXmm32, int_to_float) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector 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(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterSlow, xmm32_move) { + std::vector 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(); + } + } + } + } + } +} diff --git a/test/test_emitter_integer_math.cpp b/test/test_emitter_integer_math.cpp deleted file mode 100644 index 1eb8dd926a..0000000000 --- a/test/test_emitter_integer_math.cpp +++ /dev/null @@ -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 vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; - std::vector 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(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 vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; - std::vector 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(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 vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; - std::vector 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(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 vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX}; - std::vector 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(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 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterIntegerMath, sub_gpr64_gpr64) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterIntegerMath, mul_gpr32_gpr32) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector 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(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 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterIntegerMath, and_gpr64_gpr64) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterIntegerMath, xor_gpr64_gpr64) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterIntegerMath, not_gpr64) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } -} - -TEST(EmitterIntegerMath, shl_gpr64_cl) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, - INT64_MAX, 117, 32, -348473, 83747382}; - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} - -TEST(EmitterIntegerMath, shr_gpr64_cl) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN), - INT64_MAX, 117, 32, u64(-348473), 83747382}; - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} - -TEST(EmitterIntegerMath, sar_gpr64_cl) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, - INT64_MAX, 117, 32, -348473, 83747382}; - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} - -TEST(EmitterIntegerMath, shl_gpr64_u8) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, - INT64_MAX, 117, 32, -348473, 83747382}; - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} - -TEST(EmitterIntegerMath, shr_gpr64_u8) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN), - INT64_MAX, 117, 32, u64(-348473), 83747382}; - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} - -TEST(EmitterIntegerMath, sar_gpr64_u8) { - CodeTester tester; - tester.init_code_buffer(256); - std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, - INT64_MAX, 117, 32, -348473, 83747382}; - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} - -TEST(EmitterIntegerMath, jumps) { - CodeTester tester; - tester.init_code_buffer(256); - - std::vector 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(off)); - } - - EXPECT_EQ(tester.dump_to_hex_string(true), - "E9000000000F84000000000F85000000000F8E000000000F8D000000000F8C000000000F8F000000000F86" - "000000000F83000000000F82000000000F8700000000"); -} - -TEST(EmitterIntegerMath, null) { - auto instr = IGen::null(); - EXPECT_EQ(0, instr.emit(nullptr)); -} \ No newline at end of file diff --git a/test/test_emitter_slow.cpp b/test/test_emitter_slow.cpp deleted file mode 100644 index ebe298745d..0000000000 --- a/test/test_emitter_slow.cpp +++ /dev/null @@ -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_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(); - } - } - } - } - } -} diff --git a/test/test_emitter_xmm32.cpp b/test/test_emitter_xmm32.cpp deleted file mode 100644 index 97b1651214..0000000000 --- a/test/test_emitter_xmm32.cpp +++ /dev/null @@ -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((u64)memory, 3 * sizeof(float), 0, 0), 3.45f); - EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float), 0, 0), 1.23f); - EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float), 0, 0), 5.67f); - EXPECT_EQ(tester.execute_ret((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((u64)memory, 3 * sizeof(float) + 3, 0, 0), 3.45f); - EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float) + 3, 0, 0), 1.23f); - EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float) + 3, 0, 0), 5.67f); - EXPECT_EQ(tester.execute_ret((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((u64)memory, 3 * sizeof(float) - offset, 0, 0), 3.45f); - EXPECT_EQ(tester.execute_ret((u64)memory, 2 * sizeof(float) - offset, 0, 0), 1.23f); - EXPECT_EQ(tester.execute_ret((u64)memory, 4 * sizeof(float) - offset, 0, 0), 5.67f); - EXPECT_EQ(tester.execute_ret((u64)memory, 5 * sizeof(float) - offset, 0, 0), 0); - iter++; - } - } - } -} - -namespace { -template -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(loc_of_float - loc_of_load - load_instr.length(), - loc_of_load + load_instr.offset_of_disp()); - - auto result = tester.execute_ret(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(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(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 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterXmm32, div) { - CodeTester tester; - tester.init_code_buffer(512); - - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterXmm32, add) { - CodeTester tester; - tester.init_code_buffer(512); - - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterXmm32, sub) { - CodeTester tester; - tester.init_code_buffer(512); - - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } - } -} - -TEST(EmitterXmm32, float_to_int) { - CodeTester tester; - tester.init_code_buffer(512); - - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} - -TEST(EmitterXmm32, int_to_float) { - CodeTester tester; - tester.init_code_buffer(512); - - std::vector 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(0, 0, 0, 0); - EXPECT_EQ(result, expected); - } - } - } -} \ No newline at end of file diff --git a/test/test_type_system.cpp b/test/test_type_system.cpp index b38d1daefb..c257004c2d 100644 --- a/test/test_type_system.cpp +++ b/test/test_type_system.cpp @@ -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(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.