[OpenGOAL] Support for 128-bit arguments and return values (WIP) (#510)

* xmm call and return working, needs some careful double checking and tests

* clean up and test
This commit is contained in:
water111
2021-05-20 20:12:49 -04:00
committed by GitHub
parent 62877ed4f3
commit 663b5c7899
21 changed files with 365 additions and 111 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
namespace versions {
// language version (OpenGOAL)
constexpr s32 GOAL_VERSION_MAJOR = 0;
constexpr s32 GOAL_VERSION_MINOR = 7;
constexpr s32 GOAL_VERSION_MINOR = 8;
constexpr int DECOMPILER_VERSION = 4;
+1 -1
View File
@@ -13248,7 +13248,7 @@
(define-extern find-temp-buffer (function int pointer))
(define-extern dgo-load-link (function dgo-header kheap symbol symbol symbol))
(define-extern destroy-mem (function (pointer uint32) (pointer uint32) none))
(define-extern string->sound-name (function string uint128))
(define-extern string->sound-name (function string sound-name))
(define-extern str-play-kick (function none))
(define-extern *dgo-time* uint)
@@ -437,7 +437,7 @@
],
"string->sound-name": [
[[6, 8], "a1", "(pointer uint8)"]
[[2, 18], "a1", "(pointer uint8)"]
],
"ramdisk-load": [
@@ -156,3 +156,6 @@
- Support 128-bit bitfields inside of static structure
- Support 128-bit bitfield constants
- Support dynamic construction of 128-bit bitfield values
## V0.8 New Calling Convention for 128-bit
- 128-bit values may now be used in function arguments and return values.
+1 -17
View File
@@ -266,21 +266,6 @@ struct DgoHeader {
;; DGO LOAD and LINK
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun string->sound-name! ((out (pointer uint128)) (in string))
"This function was added as a temporary workaround for not having
uint128 return values in OpenGOAL yet."
(set! (-> out) (the uint128 0))
(let ((out-ptr (the (pointer uint8) out))
(in-ptr (-> in data)))
(while (and (nonzero? (-> in-ptr))
(< (&- in-ptr (-> in data)) 15))
(set! (-> out-ptr) (-> in-ptr))
(&+! out-ptr 1)
(&+! in-ptr 1)
)
)
)
(define *dgo-time* (the-as uint 0))
(defun dgo-load-begin ((name string) (buffer1 int) (buffer2 int) (current-heap int))
@@ -301,8 +286,7 @@ struct DgoHeader {
(set! (-> cmd b2) (the-as uint buffer2))
(set! (-> cmd bt) (the-as uint current-heap))
;; modified due to OpenGOAL not supporting uint128 return values yet
;;(set! (-> cmd name) (string->sound-name name))
(string->sound-name! (&-> cmd name) name)
(set! (-> cmd name) (string->sound-name name))
;; call now!
(call *load-dgo-rpc* (the-as uint 0) (the-as pointer cmd) (the-as uint 32))
cmd
+2
View File
@@ -354,3 +354,5 @@
(define *sound-bank-1* #f)
(define *sound-bank-2* #f)
(defun-extern string->sound-name string sound-name)
+20
View File
@@ -5,3 +5,23 @@
;; name in dgo: gsound
;; dgos: GAME, ENGINE
(defun string->sound-name ((str string))
(let ((snd-name (new 'stack-no-clear 'qword)))
(set! (-> snd-name quad) (the-as uint128 0))
(let ((out-ptr (the-as (pointer uint8) snd-name))
(in-ptr (-> str data))
)
(while
(and
(nonzero? (-> in-ptr 0))
(< (&- in-ptr (the-as uint (-> str data))) 15)
)
(set! (-> out-ptr 0) (-> in-ptr 0))
(set! out-ptr (&-> out-ptr 1))
(set! in-ptr (&-> in-ptr 1))
)
)
(the-as sound-name (-> snd-name quad))
)
)
+19 -6
View File
@@ -206,11 +206,7 @@
(define format _format)
;; vec4s - this is present in the game as a 128-bit integer with 4 packed floats.
;; 128-bit integers seem to be used almost never in GOAL and I suspect they were not
;; fully implemented in the compiler. Instead, 128-bit integer code used inline assembly.
;; OpenGOAL does not support 128-bit integer types, so this is a bit useless.
;; Note - the actually used vector type stores the vector in memory, not a register.
;; inline assembly code puts the register in vf registers, not integer registers.
;; this isn't used very much.
(deftype vec4s (uint128)
((x float :offset 0)
(y float :offset 32)
@@ -221,7 +217,24 @@
:flag-assert #x900000010
)
;; NOTE: there is a print/inspect for vec4s that is not implemented.
(defmethod inspect vec4s ((obj vec4s))
(format #t "[~8x] ~A~%" obj 'vec4s)
(format #t "~Tx: ~f~%" (-> obj x))
(format #t "~Ty: ~f~%" (-> obj y))
(format #t "~Tz: ~f~%" (-> obj z))
(format #t "~Tw: ~f~%" (-> obj w))
obj
)
(defmethod print vec4s ((obj vec4s))
(format #t "#<vector ~F ~F ~F ~F @ #x~X>"
(-> obj x)
(-> obj y)
(-> obj z)
(-> obj w)
obj)
obj
)
(defmacro print128 (value &key (stream #t))
"Print a 128-bit value"
+1
View File
@@ -1,5 +1,6 @@
add_library(compiler
SHARED
emitter/CallingConvention.cpp
emitter/CodeTester.cpp
emitter/ObjectFileData.cpp
emitter/ObjectGenerator.cpp
+2 -1
View File
@@ -139,7 +139,8 @@ std::unique_ptr<FunctionEnv> Compiler::compile_top_level_function(const std::str
// only move to return register if we actually got a result
if (!dynamic_cast<const None*>(result)) {
fe->emit(std::make_unique<IR_Return>(fe->make_gpr(result->type()), result->to_gpr(fe.get())));
fe->emit(std::make_unique<IR_Return>(fe->make_gpr(result->type()), result->to_gpr(fe.get()),
emitter::gRegInfo.get_gpr_ret_reg()));
}
fe->finish();
+23 -12
View File
@@ -130,8 +130,8 @@ void regset_common(emitter::ObjectGenerator* gen,
///////////
// Return
///////////
IR_Return::IR_Return(const RegVal* return_reg, const RegVal* value)
: m_return_reg(return_reg), m_value(value) {}
IR_Return::IR_Return(const RegVal* return_reg, const RegVal* value, emitter::Register ret_reg)
: m_return_reg(return_reg), m_value(value), m_ret_reg(ret_reg) {}
std::string IR_Return::print() {
return fmt::format("ret {} {}", m_return_reg->print(), m_value->print());
}
@@ -154,7 +154,7 @@ void IR_Return::add_constraints(std::vector<IRegConstraint>* constraints, int my
c.ireg = m_return_reg->ireg();
c.instr_idx = my_id;
c.desired_register = emitter::RAX;
c.desired_register = m_ret_reg;
constraints->push_back(c);
}
@@ -167,7 +167,8 @@ void IR_Return::do_codegen(emitter::ObjectGenerator* gen,
if (val_reg == dest_reg) {
gen->add_instr(IGen::null(), irec);
} else {
gen->add_instr(IGen::mov_gpr64_gpr64(dest_reg, val_reg), irec);
regset_common(gen, allocs, irec, m_return_reg, m_value, true);
// gen->add_instr(IGen::mov_gpr64_gpr64(dest_reg, val_reg), irec);
}
}
@@ -361,8 +362,16 @@ void IR_GotoLabel::resolve(const Label* dest) {
// FunctionCall
/////////////////////
IR_FunctionCall::IR_FunctionCall(const RegVal* func, const RegVal* ret, std::vector<RegVal*> args)
: m_func(func), m_ret(ret), m_args(std::move(args)) {}
IR_FunctionCall::IR_FunctionCall(const RegVal* func,
const RegVal* ret,
std::vector<RegVal*> args,
std::vector<emitter::Register> arg_regs,
std::optional<emitter::Register> ret_reg)
: m_func(func),
m_ret(ret),
m_args(std::move(args)),
m_arg_regs(std::move(arg_regs)),
m_ret_reg(ret_reg) {}
std::string IR_FunctionCall::print() {
std::string result = fmt::format("call {} (ret {}) (args ", m_func->print(), m_ret->print());
@@ -398,15 +407,17 @@ void IR_FunctionCall::add_constraints(std::vector<IRegConstraint>* constraints,
IRegConstraint c;
c.ireg = m_args.at(i)->ireg();
c.instr_idx = my_id;
c.desired_register = emitter::gRegInfo.get_arg_reg(i);
c.desired_register = m_arg_regs.at(i);
constraints->push_back(c);
}
IRegConstraint c;
c.ireg = m_ret->ireg();
c.desired_register = emitter::gRegInfo.get_ret_reg();
c.instr_idx = my_id;
constraints->push_back(c);
if (m_ret_reg) {
IRegConstraint c;
c.ireg = m_ret->ireg();
c.desired_register = *m_ret_reg;
c.instr_idx = my_id;
constraints->push_back(c);
}
}
void IR_FunctionCall::do_codegen(emitter::ObjectGenerator* gen,
+9 -2
View File
@@ -32,7 +32,7 @@ class IR {
class IR_Return : public IR {
public:
IR_Return(const RegVal* return_reg, const RegVal* value);
IR_Return(const RegVal* return_reg, const RegVal* value, emitter::Register ret_reg);
std::string print() override;
RegAllocInstr to_rai() override;
void add_constraints(std::vector<IRegConstraint>* constraints, int my_id) override;
@@ -44,6 +44,7 @@ class IR_Return : public IR {
protected:
const RegVal* m_return_reg = nullptr;
const RegVal* m_value = nullptr;
emitter::Register m_ret_reg;
};
class IR_LoadConstant64 : public IR {
@@ -119,7 +120,11 @@ class IR_RegSet : public IR {
class IR_FunctionCall : public IR {
public:
IR_FunctionCall(const RegVal* func, const RegVal* ret, std::vector<RegVal*> args);
IR_FunctionCall(const RegVal* func,
const RegVal* ret,
std::vector<RegVal*> args,
std::vector<emitter::Register> arg_regs,
std::optional<emitter::Register> ret_reg);
std::string print() override;
RegAllocInstr to_rai() override;
void do_codegen(emitter::ObjectGenerator* gen,
@@ -131,6 +136,8 @@ class IR_FunctionCall : public IR {
const RegVal* m_func = nullptr;
const RegVal* m_ret = nullptr;
std::vector<RegVal*> m_args;
std::vector<emitter::Register> m_arg_regs;
std::optional<emitter::Register> m_ret_reg;
};
class IR_StaticVarAddr : public IR {
+1
View File
@@ -70,6 +70,7 @@ class RegVal : public Val {
RegVal(IRegister ireg, const TypeSpec& ts) : Val(coerce_to_reg_type(ts)), m_ireg(ireg) {}
bool is_register() const override { return true; }
IRegister ireg() const override { return m_ireg; }
void change_class(RegClass new_class) { m_ireg.reg_class = new_class; }
std::string print() const override { return m_ireg.to_string(); };
RegVal* to_reg(Env* fe) override;
RegVal* to_gpr(Env* fe) override;
+68 -14
View File
@@ -4,6 +4,7 @@
*/
#include "goalc/compiler/Compiler.h"
#include "goalc/emitter/CallingConvention.h"
#include "third-party/fmt/core.h"
namespace {
@@ -189,20 +190,27 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
// set up arguments
if (lambda.params.size() > 8) {
throw_compiler_error(form,
"Cannot generate an x86-64 function for a lambda with {} parameters. "
"Cannot generate a real function for a lambda with {} parameters. "
"The current limit is 8.",
lambda.params.size());
}
// set up argument register constraints.
std::vector<RegVal*> args_for_coloring;
std::vector<TypeSpec> arg_types;
for (auto& parm : lambda.params) {
arg_types.push_back(parm.type);
}
auto arg_regs = get_arg_registers(m_ts, arg_types);
for (u32 i = 0; i < lambda.params.size(); i++) {
IRegConstraint constr;
constr.instr_idx = 0; // constraint at function start
auto ireg = new_func_env->make_gpr(lambda.params.at(i).type);
auto ireg = new_func_env->make_ireg(
lambda.params.at(i).type, arg_regs.at(i).is_gpr() ? RegClass::GPR_64 : RegClass::INT_128);
ireg->mark_as_settable();
constr.ireg = ireg->ireg();
constr.desired_register = emitter::gRegInfo.get_arg_reg(i);
constr.desired_register = arg_regs.at(i);
new_func_env->params[lambda.params.at(i).name] = ireg;
new_func_env->constrain(constr);
args_for_coloring.push_back(ireg);
@@ -211,6 +219,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
place->func = new_func_env.get();
// nasty function block env setup
// TODO use calling convention
auto return_reg = new_func_env->make_gpr(get_none()->type());
auto func_block_env = new_func_env->alloc_env<BlockEnv>(new_func_env.get(), "#f");
func_block_env->return_value = return_reg;
@@ -239,9 +248,29 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
lambda_ts.add_arg(new_func_env->asm_func_return_type);
} else if (result && !dynamic_cast<None*>(result)) {
// got a result, so to_gpr it and return it.
auto final_result = result->to_gpr(new_func_env.get());
new_func_env->emit(std::make_unique<IR_Return>(return_reg, final_result));
RegVal* final_result;
emitter::Register ret_hw_reg = emitter::gRegInfo.get_gpr_ret_reg();
if (result->type() != TypeSpec("none") &&
m_ts.lookup_type(result->type())->get_load_size() == 16) {
ret_hw_reg = emitter::gRegInfo.get_xmm_ret_reg();
final_result = result->to_xmm128(new_func_env.get());
return_reg->change_class(RegClass::INT_128);
} else {
final_result = result->to_gpr(new_func_env.get());
}
func_block_env->return_types.push_back(final_result->type());
for (const auto& possible_type : func_block_env->return_types) {
if (possible_type != TypeSpec("none") &&
m_ts.lookup_type(possible_type)->get_load_size() == 16) {
return_reg->change_class(RegClass::INT_128);
break;
}
}
new_func_env->emit(std::make_unique<IR_Return>(return_reg, final_result, ret_hw_reg));
auto return_type = m_ts.lowest_common_ancestor(func_block_env->return_types);
lambda_ts.add_arg(return_type);
} else {
@@ -364,7 +393,6 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en
std::vector<RegVal*> eval_args;
for (uint32_t i = 1; i < args.unnamed.size(); i++) {
auto intermediate = compile_error_guard(args.unnamed.at(i), env);
// todo, are the eval/to_reg'd in batches?
eval_args.push_back(intermediate->to_reg(env));
}
@@ -420,7 +448,14 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en
RegVal* result_reg_if_return_from = nullptr;
if (auto_inline || got_inlined_lambda) {
inlined_block_env = fe->alloc_env<BlockEnv>(inlined_compile_env, "#f");
result_reg_if_return_from = inlined_compile_env->make_gpr(get_none()->type());
RegClass ret_class = RegClass::GPR_64;
if (head->type().last_arg() != TypeSpec("none") &&
m_ts.lookup_type(head->type().last_arg())->get_load_size() == 16) {
ret_class = RegClass::INT_128;
}
result_reg_if_return_from =
inlined_compile_env->make_ireg(head->type().last_arg(), ret_class);
inlined_block_env->return_value = result_reg_if_return_from;
inlined_block_env->end_label = Label(fe);
inlined_compile_env = inlined_block_env;
@@ -447,10 +482,19 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en
// there were return froms used in the function, so we fall back to using the separate
// return gpr.
if (!dynamic_cast<None*>(result)) {
auto final_result = result->to_gpr(inlined_compile_env);
auto final_result = result->to_reg(inlined_compile_env);
inlined_block_env->return_types.push_back(final_result->type());
for (const auto& possible_type : inlined_block_env->return_types) {
if (possible_type != TypeSpec("none") &&
m_ts.lookup_type(possible_type)->get_load_size() == 16) {
result_reg_if_return_from->change_class(RegClass::INT_128);
}
}
inlined_compile_env->emit(
std::make_unique<IR_RegSet>(result_reg_if_return_from, final_result));
inlined_block_env->return_types.push_back(final_result->type());
auto return_type = m_ts.lowest_common_ancestor(inlined_block_env->return_types);
inlined_block_env->return_value->set_type(return_type);
} else {
@@ -526,7 +570,13 @@ Val* Compiler::compile_real_function_call(const goos::Object& form,
return_ts = function->type().last_arg();
}
auto return_reg = env->make_gpr(return_ts);
auto cc = get_function_calling_convention(function->type(), m_ts);
RegClass ret_reg_class = RegClass::GPR_64;
if (cc.return_reg && cc.return_reg->is_xmm()) {
ret_reg_class = RegClass::INT_128;
}
auto return_reg = env->make_ireg(return_ts, ret_reg_class);
// check arg count:
if (function->type().arg_count() && !is_varargs_function(function->type())) {
@@ -552,8 +602,11 @@ Val* Compiler::compile_real_function_call(const goos::Object& form,
// set args (introducing a move here makes coloring more likely to be possible)
std::vector<RegVal*> arg_outs;
for (auto& arg : args) {
arg_outs.push_back(env->make_gpr(arg->type()));
for (int i = 0; i < (int)args.size(); i++) {
const auto& arg = args.at(i);
auto reg = cc.arg_regs.at(i);
arg_outs.push_back(
env->make_ireg(arg->type(), reg.is_xmm() ? RegClass::INT_128 : RegClass::GPR_64));
arg_outs.back()->mark_as_settable();
env->emit(std::make_unique<IR_RegSet>(arg_outs.back(), arg));
}
@@ -561,10 +614,11 @@ Val* Compiler::compile_real_function_call(const goos::Object& form,
// todo, there's probably a more efficient way to do this.
auto temp_function = fe->make_gpr(function->type());
env->emit(std::make_unique<IR_RegSet>(temp_function, function));
env->emit(std::make_unique<IR_FunctionCall>(temp_function, return_reg, arg_outs));
env->emit(std::make_unique<IR_FunctionCall>(temp_function, return_reg, arg_outs, cc.arg_regs,
cc.return_reg));
if (m_settings.emit_move_after_return) {
auto result_reg = env->make_gpr(return_reg->type());
auto result_reg = env->make_ireg(return_reg->type(), ret_reg_class);
env->emit(std::make_unique<IR_RegSet>(result_reg, return_reg));
return result_reg;
} else {
+60 -19
View File
@@ -2,6 +2,7 @@
#include "third-party/fmt/core.h"
#include "common/type_system/defenum.h"
#include "common/type_system/deftype.h"
#include "goalc/emitter/CallingConvention.h"
namespace {
@@ -171,40 +172,41 @@ void Compiler::generate_field_description(const goos::Object& form,
Val* Compiler::generate_inspector_for_structured_type(const goos::Object& form,
Env* env,
StructureType* structured_type) {
StructureType* structure_type) {
// Create a function environment to hold the code for the inspect method. The name is just for
// debugging.
auto method_env = std::make_unique<FunctionEnv>(
env, "autogenerated-inspect-method-of-" + structured_type->get_name());
env, "autogenerated-inspect-method-of-" + structure_type->get_name());
// put the method in the debug segment.
method_env->set_segment(DEBUG_SEGMENT);
// Create a register which will hold the input to the inspect method
auto input = method_env->make_gpr(structured_type->get_name());
auto input = method_env->make_gpr(structure_type->get_name());
// "Constrain" this register to be the register that the function argument is passed in
IRegConstraint constraint;
constraint.instr_idx = 0; // constraint at the start of the function
constraint.ireg = input->ireg(); // constrain this register
constraint.desired_register = emitter::gRegInfo.get_arg_reg(0); // to the first argument
constraint.desired_register = emitter::gRegInfo.get_gpr_arg_reg(0); // to the first argument
method_env->constrain(constraint);
// Inform the compiler that `input`'s value will be written to `rdi` (first arg register)
method_env->emit(std::make_unique<IR_ValueReset>(std::vector<RegVal*>{input}));
RegVal* type_name = nullptr;
if (dynamic_cast<BasicType*>(structured_type)) {
type_name = get_field_of_structure(structured_type, input, "type", method_env.get())
if (dynamic_cast<BasicType*>(structure_type)) {
type_name = get_field_of_structure(structure_type, input, "type", method_env.get())
->to_gpr(method_env.get());
} else {
type_name = compile_get_sym_obj(structured_type->get_name(), method_env.get())
->to_gpr(method_env.get());
type_name =
compile_get_sym_obj(structure_type->get_name(), method_env.get())->to_gpr(method_env.get());
}
compile_format_string(form, method_env.get(), "[~8x] ~A~%", {input, type_name});
for (const Field& f : structured_type->fields()) {
generate_field_description(form, structured_type, method_env.get(), input, f);
for (const Field& f : structure_type->fields()) {
generate_field_description(form, structure_type, method_env.get(), input, f);
}
method_env->emit(std::make_unique<IR_Return>(method_env->make_gpr(input->type()), input));
method_env->emit_ir<IR_Return>(method_env->make_gpr(input->type()), input,
emitter::gRegInfo.get_gpr_ret_reg());
// add this function to the object file
auto fe = get_parent_env_of_type<FunctionEnv>(env);
@@ -214,8 +216,8 @@ Val* Compiler::generate_inspector_for_structured_type(const goos::Object& form,
obj_env_inspect->add_function(std::move(method_env));
// call method-set!
auto type_obj = compile_get_symbol_value(form, structured_type->get_name(), env)->to_gpr(env);
auto id_val = compile_integer(m_ts.lookup_method(structured_type->get_name(), "inspect").id, env)
auto type_obj = compile_get_symbol_value(form, structure_type->get_name(), env)->to_gpr(env);
auto id_val = compile_integer(m_ts.lookup_method(structure_type->get_name(), "inspect").id, env)
->to_gpr(env);
auto method_set_val = compile_get_symbol_value(form, "method-set!", env)->to_gpr(env);
return compile_real_function_call(form, method_set_val, {type_obj, id_val, method->to_gpr(env)},
@@ -225,6 +227,7 @@ Val* Compiler::generate_inspector_for_structured_type(const goos::Object& form,
Val* Compiler::generate_inspector_for_bitfield_type(const goos::Object& form,
Env* env,
BitFieldType* bitfield_type) {
bool bitfield_128 = bitfield_type->get_load_size() == 16;
// Create a function environment to hold the code for the inspect method. The name is just for
// debugging.
auto method_env = std::make_unique<FunctionEnv>(
@@ -238,7 +241,12 @@ Val* Compiler::generate_inspector_for_bitfield_type(const goos::Object& form,
IRegConstraint constraint;
constraint.instr_idx = 0; // constraint at the start of the function
constraint.ireg = input->ireg(); // constrain this register
constraint.desired_register = emitter::gRegInfo.get_arg_reg(0); // to the first argument
if (bitfield_128) {
constraint.desired_register = emitter::gRegInfo.get_xmm_arg_reg(0); // to the first argument
} else {
constraint.desired_register = emitter::gRegInfo.get_gpr_arg_reg(0); // to the first argument
}
method_env->constrain(constraint);
// Inform the compiler that `input`'s value will be written to `rdi` (first arg register)
method_env->emit(std::make_unique<IR_ValueReset>(std::vector<RegVal*>{input}));
@@ -259,7 +267,14 @@ Val* Compiler::generate_inspector_for_bitfield_type(const goos::Object& form,
compile_format_string(form, method_env.get(), str_template, format_args);
}
method_env->emit(std::make_unique<IR_Return>(method_env->make_gpr(input->type()), input));
if (bitfield_128) {
method_env->emit(
std::make_unique<IR_Return>(method_env->make_ireg(input->type(), RegClass::INT_128), input,
emitter::gRegInfo.get_gpr_ret_reg()));
} else {
method_env->emit(std::make_unique<IR_Return>(method_env->make_gpr(input->type()), input,
emitter::gRegInfo.get_gpr_ret_reg()));
}
// add this function to the object file
auto fe = get_parent_env_of_type<FunctionEnv>(env);
@@ -394,13 +409,20 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
throw_compiler_error(form, "Methods cannot have more than 8 arguments");
}
std::vector<RegVal*> args_for_coloring;
std::vector<TypeSpec> arg_types;
for (auto& parm : lambda.params) {
arg_types.push_back(parm.type);
}
auto arg_regs = get_arg_registers(m_ts, arg_types);
for (u32 i = 0; i < lambda.params.size(); i++) {
IRegConstraint constr;
constr.instr_idx = 0; // constraint at function start
auto ireg = new_func_env->make_gpr(lambda.params.at(i).type);
auto ireg = new_func_env->make_ireg(
lambda.params.at(i).type, arg_regs.at(i).is_gpr() ? RegClass::GPR_64 : RegClass::INT_128);
ireg->mark_as_settable();
constr.ireg = ireg->ireg();
constr.desired_register = emitter::gRegInfo.get_arg_reg(i);
constr.desired_register = arg_regs.at(i);
new_func_env->params[lambda.params.at(i).name] = ireg;
new_func_env->constrain(constr);
args_for_coloring.push_back(ireg);
@@ -434,9 +456,28 @@ Val* Compiler::compile_defmethod(const goos::Object& form, const goos::Object& _
// don't add return automatically!
lambda_ts.add_arg(new_func_env->asm_func_return_type);
} else if (result && !dynamic_cast<None*>(result)) {
auto final_result = result->to_gpr(new_func_env.get());
new_func_env->emit(std::make_unique<IR_Return>(return_reg, final_result));
RegVal* final_result;
emitter::Register ret_hw_reg = emitter::gRegInfo.get_gpr_ret_reg();
if (m_ts.lookup_type(result->type())->get_load_size() == 16) {
ret_hw_reg = emitter::gRegInfo.get_xmm_ret_reg();
final_result = result->to_xmm128(new_func_env.get());
return_reg->change_class(RegClass::INT_128);
} else {
final_result = result->to_gpr(new_func_env.get());
}
func_block_env->return_types.push_back(final_result->type());
for (const auto& possible_type : func_block_env->return_types) {
if (possible_type != TypeSpec("none") &&
m_ts.lookup_type(possible_type)->get_load_size() == 16) {
return_reg->change_class(RegClass::INT_128);
break;
}
}
new_func_env->emit(std::make_unique<IR_Return>(return_reg, final_result, ret_hw_reg));
auto return_type = m_ts.lowest_common_ancestor(func_block_env->return_types);
lambda_ts.add_arg(return_type);
} else {
+56
View File
@@ -0,0 +1,56 @@
#include "common/util/assert.h"
#include "CallingConvention.h"
CallingConvention get_function_calling_convention(const TypeSpec& function_type,
const TypeSystem& type_system) {
assert(function_type.base_type() == "function");
assert(function_type.arg_count() > 0);
assert(function_type.arg_count() <= 9);
int gpr_idx = 0;
int xmm_idx = 0;
CallingConvention cc;
if (function_type.arg_count() == 2 && function_type.get_arg(0).print() == "_varargs_") {
for (int i = 0; i < 8; i++) {
cc.arg_regs.push_back(emitter::gRegInfo.get_gpr_arg_reg(gpr_idx++));
}
} else {
for (int i = 0; i < (int)function_type.arg_count() - 1; i++) {
auto info = type_system.lookup_type(function_type.get_arg(i));
if (dynamic_cast<const ValueType*>(info) && info->get_load_size() == 16) {
cc.arg_regs.push_back(emitter::gRegInfo.get_xmm_arg_reg(xmm_idx++));
} else {
cc.arg_regs.push_back(emitter::gRegInfo.get_gpr_arg_reg(gpr_idx++));
}
}
}
if (function_type.last_arg() != TypeSpec("none")) {
if (type_system.lookup_type(function_type.last_arg())->get_load_size() == 16) {
cc.return_reg = emitter::gRegInfo.get_xmm_ret_reg();
} else {
cc.return_reg = emitter::gRegInfo.get_gpr_ret_reg();
}
}
return cc;
}
std::vector<emitter::Register> get_arg_registers(const TypeSystem& type_system,
const std::vector<TypeSpec>& arg_types) {
std::vector<emitter::Register> result;
int gpr_idx = 0;
int xmm_idx = 0;
for (auto& type : arg_types) {
auto info = type_system.lookup_type(type);
if (info->get_load_size() == 16) {
result.push_back(emitter::gRegInfo.get_xmm_arg_reg(xmm_idx++));
} else {
result.push_back(emitter::gRegInfo.get_gpr_arg_reg(gpr_idx++));
}
}
return result;
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <vector>
#include <optional>
#include "common/type_system/TypeSystem.h"
#include "goalc/emitter/Register.h"
struct CallingConvention {
std::vector<emitter::Register> arg_regs;
std::optional<emitter::Register> return_reg;
};
std::vector<emitter::Register> get_arg_registers(const TypeSystem& type_system,
const std::vector<TypeSpec>& arg_types);
CallingConvention get_function_calling_convention(const TypeSpec& function_type,
const TypeSystem& type_system);
+36 -33
View File
@@ -5,42 +5,45 @@ namespace emitter {
RegisterInfo RegisterInfo::make_register_info() {
RegisterInfo info;
info.m_info[RAX] = {-1, false, false, "rax"}; // temp
info.m_info[RCX] = {3, false, false, "rcx"}; // temp
info.m_info[RDX] = {2, false, false, "rdx"}; // temp
info.m_info[RBX] = {-1, true, false, "rbx"}; //
info.m_info[RSP] = {-1, false, true, "rsp"};
info.m_info[RBP] = {-1, true, false, "rbp"};
info.m_info[RSI] = {1, false, false, "rsi"};
info.m_info[RDI] = {0, false, false, "rdi"};
info.m_info[RAX] = {false, false, "rax"}; // return, temp
info.m_info[RCX] = {false, false, "rcx"}; // gpr arg 3, temp
info.m_info[RDX] = {false, false, "rdx"}; // gpr arg 2, temp
info.m_info[RBX] = {true, false, "rbx"}; // saved
info.m_info[RSP] = {false, true, "rsp"}; // stack pointer
info.m_info[RBP] = {true, false, "rbp"}; // saved
info.m_info[RSI] = {false, false, "rsi"}; // gpr arg 1, temp
info.m_info[RDI] = {false, false, "rdi"}; // gpr arg 0, temp
info.m_info[R8] = {4, false, false, "r8"};
info.m_info[R9] = {5, false, false, "r9"};
info.m_info[R10] = {6, true, false, "r10"};
info.m_info[R11] = {7, true, false, "r11"};
info.m_info[R12] = {-1, true, false, "r12"};
info.m_info[R13] = {-1, false, true, "r13"}; // pp?
info.m_info[R14] = {-1, false, true, "r14"}; // st?
info.m_info[R15] = {-1, false, true, "r15"}; // offset.
info.m_info[R8] = {false, false, "r8"}; // gpr arg 4, temp
info.m_info[R9] = {false, false, "r9"}; // gpr arg 5, temp
info.m_info[R10] = {true, false, "r10"}; // gpr arg 6, saved
info.m_info[R11] = {true, false, "r11"}; // gpr arg 7, saved
info.m_info[R12] = {true, false, "r12"}; // saved
info.m_info[R13] = {false, true, "r13"}; // pp
info.m_info[R14] = {false, true, "r14"}; // st
info.m_info[R15] = {false, true, "r15"}; // offset.
info.m_info[XMM0] = {-1, false, false, "xmm0"};
info.m_info[XMM1] = {-1, false, false, "xmm1"};
info.m_info[XMM2] = {-1, false, false, "xmm2"};
info.m_info[XMM3] = {-1, false, false, "xmm3"};
info.m_info[XMM4] = {-1, false, false, "xmm4"};
info.m_info[XMM5] = {-1, false, false, "xmm5"};
info.m_info[XMM6] = {-1, false, false, "xmm6"};
info.m_info[XMM7] = {-1, false, false, "xmm7"};
info.m_info[XMM8] = {-1, true, false, "xmm8"};
info.m_info[XMM9] = {-1, true, false, "xmm9"};
info.m_info[XMM10] = {-1, true, false, "xmm10"};
info.m_info[XMM11] = {-1, true, false, "xmm11"};
info.m_info[XMM12] = {-1, true, false, "xmm12"};
info.m_info[XMM13] = {-1, true, false, "xmm13"};
info.m_info[XMM14] = {-1, true, false, "xmm14"};
info.m_info[XMM15] = {-1, true, false, "xmm15"};
info.m_info[XMM0] = {false, false, "xmm0"};
info.m_info[XMM1] = {false, false, "xmm1"};
info.m_info[XMM2] = {false, false, "xmm2"};
info.m_info[XMM3] = {false, false, "xmm3"};
info.m_info[XMM4] = {false, false, "xmm4"};
info.m_info[XMM5] = {false, false, "xmm5"};
info.m_info[XMM6] = {false, false, "xmm6"};
info.m_info[XMM7] = {false, false, "xmm7"};
info.m_info[XMM8] = {true, false, "xmm8"};
info.m_info[XMM9] = {true, false, "xmm9"};
info.m_info[XMM10] = {true, false, "xmm10"};
info.m_info[XMM11] = {true, false, "xmm11"};
info.m_info[XMM12] = {true, false, "xmm12"};
info.m_info[XMM13] = {true, false, "xmm13"};
info.m_info[XMM14] = {true, false, "xmm14"};
info.m_info[XMM15] = {true, false, "xmm15"};
info.m_arg_regs = std::array<Register, N_ARGS>({RDI, RSI, RDX, RCX, R8, R9, R10, R11});
info.m_gpr_arg_regs = std::array<Register, N_ARGS>({RDI, RSI, RDX, RCX, R8, R9, R10, R11});
// skip xmm0 so it can be used for return.
info.m_xmm_arg_regs =
std::array<Register, N_ARGS>({XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8});
info.m_saved_gprs = std::array<Register, N_SAVED_GPRS>({RBX, RBP, R10, R11, R12});
info.m_saved_xmms =
std::array<Register, N_SAVED_XMMS>({XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15});
+6 -4
View File
@@ -121,7 +121,6 @@ class RegisterInfo {
static RegisterInfo make_register_info();
struct Info {
int argument_id = -1; // -1 if not argument
bool saved = false; // does the callee save it?
bool special = false; // is it a special GOAL register?
std::string name;
@@ -130,13 +129,15 @@ class RegisterInfo {
};
const Info& get_info(Register r) const { return m_info.at(r.id()); }
Register get_arg_reg(int id) const { return m_arg_regs.at(id); }
Register get_gpr_arg_reg(int id) const { return m_gpr_arg_regs.at(id); }
Register get_xmm_arg_reg(int id) const { return m_xmm_arg_regs.at(id); }
Register get_saved_gpr(int id) const { return m_saved_gprs.at(id); }
Register get_saved_xmm(int id) const { return m_saved_xmms.at(id); }
Register get_process_reg() const { return R13; }
Register get_st_reg() const { return R14; }
Register get_offset_reg() const { return R15; }
Register get_ret_reg() const { return RAX; }
Register get_gpr_ret_reg() const { return RAX; }
Register get_xmm_ret_reg() const { return XMM0; }
const std::vector<Register>& get_gpr_alloc_order() { return m_gpr_alloc_order; }
const std::vector<Register>& get_xmm_alloc_order() { return m_xmm_alloc_order; }
const std::vector<Register>& get_gpr_temp_alloc_order() { return m_gpr_temp_only_alloc_order; }
@@ -148,7 +149,8 @@ class RegisterInfo {
private:
RegisterInfo() = default;
std::array<Info, N_REGS> m_info;
std::array<Register, N_ARGS> m_arg_regs;
std::array<Register, N_ARGS> m_gpr_arg_regs;
std::array<Register, N_ARGS> m_xmm_arg_regs;
std::array<Register, N_SAVED_GPRS> m_saved_gprs;
std::array<Register, N_SAVED_XMMS> m_saved_xmms;
std::array<Register, N_SAVED_XMMS + N_SAVED_GPRS> m_saved_all;
@@ -0,0 +1,27 @@
(let ((tv (new 'static 'vec4s :x 1.0 :y 2.0 :z 3.0 :w 4.0)))
(let ((temp (print tv)))
(format #t "~%")
(set! (-> temp x) 10.0)
(set! (-> tv y) 20.0)
(print tv)
(format #t "~%")
(print temp)
(format #t "~%")
)
)
(defun test-print-sound-name-chars ((arg0 int) (name sound-name) (arg2 int))
(let ((mem (new 'stack 'array 'uint8 16)))
(set! (-> (the (pointer sound-name) mem)) name)
(let ((i 0))
(while (and (< i 16) (nonzero? (-> mem i)))
(format #t " ~c" (-> mem i))
(+! i 1)
)
)
(format #t "~%")
(format #t "arg0: ~d arg2: ~d~%" arg0 arg2)
)
)
(test-print-sound-name-chars 1 (string->sound-name "0123456789abcdefghij") 2)
+11
View File
@@ -735,6 +735,17 @@ TEST_F(WithGameTests, WeirdMultiply) {
"0\n"});
}
TEST_F(WithGameTests, Function128) {
runner.run_static_test(
env, testCategory, "test-function128.gc",
{"#<vector 1.0000 2.0000 3.0000 4.0000 @ #x400000003f800000>\n"
"#<vector 1.0000 20.0000 3.0000 4.0000 @ #x41a000003f800000>\n"
"#<vector 10.0000 2.0000 3.0000 4.0000 @ #x4000000041200000>\n"
" 0 1 2 3 4 5 6 7 8 9 a b c d e\n"
"arg0: 1 arg2: 2\n"
"0\n"});
}
TEST(TypeConsistency, TypeConsistency) {
Compiler compiler;
compiler.enable_throw_on_redefines();