better array indexing (#176)

This commit is contained in:
water111
2020-12-31 22:15:17 -05:00
committed by GitHub
parent c8d382b35c
commit feead303aa
8 changed files with 140 additions and 39 deletions
+4 -1
View File
@@ -103,4 +103,7 @@
- Improved back up and restore of xmm registers
- Fixed an off-by-one in move eliminator (previous version was correct, but did not generate as good code). Complicated functions are 2 to 10% smaller.
- Improved getting a stack address.
- Improved getting the value of `#f`, `#t`, and `()`.
- Improved getting the value of `#f`, `#t`, and `()`.
- Accessing a constant field of an array now constant propagates the memory offset like field access and avoids a runtime multiply.
- Fixed a bug where loading or storing a `vf` register from a memory location + constant offset would cause the compiler to throw an error.
- Accessing array elements uses more efficient indexing for power-of-two element sizes.
+1
View File
@@ -237,6 +237,7 @@ class Compiler {
StaticStructure* structure,
int offset,
Env* env);
void compile_constant_product(RegVal* dest, RegVal* src, int stride, Env* env);
template <typename... Args>
void throw_compiler_error(const goos::Object& code, const std::string& str, Args&&... args) {
+50
View File
@@ -2,6 +2,9 @@
#include "goalc/compiler/IR.h"
#include "common/goos/ParseHelpers.h"
/*!
* Parse arguments into a goos::Arguments format.
*/
goos::Arguments Compiler::get_va(const goos::Object& form, const goos::Object& rest) {
goos::Arguments args;
@@ -12,6 +15,10 @@ goos::Arguments Compiler::get_va(const goos::Object& form, const goos::Object& r
return args;
}
/*!
* Check arguments in a goos::Arguments format (named and unnamed) and throw a compiler error if it
* fails.
*/
void Compiler::va_check(
const goos::Object& form,
const goos::Arguments& args,
@@ -24,6 +31,10 @@ void Compiler::va_check(
}
}
/*!
* Iterate through elements of a goos list and apply the given function. Throw compiler error if the
* list is invalid.
*/
void Compiler::for_each_in_list(const goos::Object& list,
const std::function<void(const goos::Object&)>& f) {
const goos::Object* iter = &list;
@@ -38,14 +49,24 @@ void Compiler::for_each_in_list(const goos::Object& list,
}
}
/*!
* Convert a goos::Object that's a string to a std::string. Must be a string.
*/
std::string Compiler::as_string(const goos::Object& o) {
return o.as_string()->data;
}
/*!
* Convert a goos::Object that's a symbol to a std::string. Must be a string.
*/
std::string Compiler::symbol_string(const goos::Object& o) {
return o.as_symbol()->name;
}
/*!
* Convert a single quoted symbol into a std::string. Like 'hi -> "hi". Error if not a quoted
* symbol.
*/
std::string Compiler::quoted_sym_as_string(const goos::Object& o) {
auto args = get_va(o, o);
va_check(o, args, {{goos::ObjectType::SYMBOL}, {goos::ObjectType::SYMBOL}}, {});
@@ -55,6 +76,9 @@ std::string Compiler::quoted_sym_as_string(const goos::Object& o) {
return symbol_string(args.unnamed.at(1));
}
/*!
* Get a thing that's quoted. Error if the thing isn't quoted.
*/
goos::Object Compiler::unquote(const goos::Object& o) {
auto args = get_va(o, o);
va_check(o, args, {{goos::ObjectType::SYMBOL}, {}}, {});
@@ -64,6 +88,9 @@ goos::Object Compiler::unquote(const goos::Object& o) {
return args.unnamed.at(1);
}
/*!
* Determine if o is a quoted symbol like 'test.
*/
bool Compiler::is_quoted_sym(const goos::Object& o) {
if (o.is_pair()) {
auto car = pair_car(o);
@@ -255,4 +282,27 @@ std::vector<goos::Object> Compiler::get_list_as_vector(const goos::Object& o,
return result;
}
}
}
void Compiler::compile_constant_product(RegVal* dest, RegVal* src, int stride, Env* env) {
// todo - support imul with an imm.
assert(stride);
bool is_power_of_two = (stride & (stride - 1)) == 0;
if (stride == 1) {
env->emit_ir<IR_RegSet>(dest, src);
} else if (is_power_of_two) {
for (int i = 0; i < 16; i++) {
if (stride == (1 << i)) {
env->emit_ir<IR_RegSet>(dest, src);
env->emit_ir<IR_IntegerMath>(IntegerMathKind::SHL_64, dest, i);
return;
}
}
assert(false);
} else {
// get the multiplier
env->emit_ir<IR_LoadConstant64>(dest, stride);
env->emit_ir<IR_IntegerMath>(IntegerMathKind::IMUL_32, dest, src);
}
}
+34 -18
View File
@@ -109,13 +109,33 @@ RegVal* FloatConstantVal::to_reg(Env* fe) {
return re;
}
namespace {
/*!
* Constant propagate nested MemoryOffsetConstantVal's to get a single base + offset.
*/
Val* get_constant_offset_and_base(MemoryOffsetConstantVal* in, int64_t* offset_out) {
Val* next_base = in->base;
s64 total_offset = in->offset;
while (dynamic_cast<MemoryOffsetConstantVal*>(next_base)) {
auto bac = dynamic_cast<MemoryOffsetConstantVal*>(next_base);
total_offset += bac->offset;
next_base = bac->base;
}
*offset_out = total_offset;
return next_base;
}
} // namespace
RegVal* MemoryOffsetConstantVal::to_reg(Env* fe) {
auto re = fe->make_gpr(coerce_to_reg_type(m_ts));
if (offset == 0) {
fe->emit_ir<IR_RegSet>(re, base->to_gpr(fe));
s64 final_offset;
auto final_base = get_constant_offset_and_base(this, &final_offset);
if (final_offset == 0) {
fe->emit_ir<IR_RegSet>(re, final_base->to_gpr(fe));
} else {
fe->emit(std::make_unique<IR_LoadConstant64>(re, int64_t(offset)));
fe->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::ADD_64, re, base->to_gpr(fe)));
fe->emit(std::make_unique<IR_LoadConstant64>(re, int64_t(final_offset)));
fe->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::ADD_64, re, final_base->to_gpr(fe)));
}
return re;
@@ -129,35 +149,31 @@ RegVal* MemoryOffsetVal::to_reg(Env* fe) {
}
RegVal* MemoryDerefVal::to_reg(Env* fe) {
// todo, support better loads/stores from the stack
auto re = fe->make_gpr(coerce_to_reg_type(m_ts));
auto base_as_co = dynamic_cast<MemoryOffsetConstantVal*>(base);
if (base_as_co) {
auto re = fe->make_gpr(coerce_to_reg_type(m_ts));
fe->emit(std::make_unique<IR_LoadConstOffset>(re, base_as_co->offset,
base_as_co->base->to_gpr(fe), info));
return re;
s64 offset;
auto final_base = get_constant_offset_and_base(base_as_co, &offset);
fe->emit_ir<IR_LoadConstOffset>(re, offset, final_base->to_gpr(fe), info);
} else {
auto re = fe->make_gpr(coerce_to_reg_type(m_ts));
auto addr = base->to_gpr(fe);
fe->emit(std::make_unique<IR_LoadConstOffset>(re, 0, addr, info));
return re;
}
return re;
}
RegVal* MemoryDerefVal::to_fpr(Env* fe) {
// todo, support better loads/stores from the stack
auto base_as_co = dynamic_cast<MemoryOffsetConstantVal*>(base);
auto re = fe->make_fpr(coerce_to_reg_type(m_ts));
if (base_as_co) {
auto re = fe->make_fpr(coerce_to_reg_type(m_ts));
fe->emit(std::make_unique<IR_LoadConstOffset>(re, base_as_co->offset,
base_as_co->base->to_gpr(fe), info));
return re;
s64 offset;
auto final_base = get_constant_offset_and_base(base_as_co, &offset);
fe->emit_ir<IR_LoadConstOffset>(re, offset, final_base->to_gpr(fe), info);
} else {
auto re = fe->make_fpr(coerce_to_reg_type(m_ts));
auto addr = base->to_gpr(fe);
fe->emit(std::make_unique<IR_LoadConstOffset>(re, 0, addr, info));
return re;
}
return re;
}
RegVal* AliasVal::to_reg(Env* fe) {
-1
View File
@@ -304,7 +304,6 @@ Val* Compiler::compile_asm_svf(const goos::Object& form, const goos::Object& res
info.reg = RegClass::VECTOR_FLOAT;
if (as_co) {
// can do a clever offset here
assert(false);
env->emit_ir<IR_StoreConstOffset>(src, as_co->offset, as_co->base->to_gpr(env), 16, color);
} else {
env->emit_ir<IR_StoreConstOffset>(src, 0, dest->to_gpr(env), 16, color);
+42 -19
View File
@@ -498,28 +498,44 @@ Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest
}
}
auto index_value = compile_error_guard(field_obj, env)->to_gpr(env);
if (!is_integer(index_value->type())) {
throw_compiler_error(form, "Cannot use -> with {}.", field_obj.print());
int64_t constant_index_value;
RegVal* index_value = nullptr;
bool has_constant_idx = try_getting_constant_integer(field_obj, &constant_index_value, env);
if (!has_constant_idx) {
index_value = compile_error_guard(field_obj, env)->to_gpr(env);
if (!is_integer(index_value->type())) {
throw_compiler_error(form, "Cannot use -> with {}.", field_obj.print());
}
}
if (result->type().base_type() == "inline-array") {
auto di = m_ts.get_deref_info(result->type());
auto base_type = di.result_type;
assert(di.can_deref);
auto offset = compile_integer(di.stride, env)->to_gpr(env);
// todo, check for integer and avoid runtime multiply
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::IMUL_32, offset, index_value));
result = fe->alloc_val<MemoryOffsetVal>(di.result_type, result, offset);
if (has_constant_idx) {
result = fe->alloc_val<MemoryOffsetConstantVal>(di.result_type, result,
di.stride * constant_index_value);
} else {
// todo - use shifts if possible?
RegVal* offset = fe->make_gpr(TypeSpec("int"));
compile_constant_product(offset, index_value, di.stride, env);
result = fe->alloc_val<MemoryOffsetVal>(di.result_type, result, offset);
}
} else if (result->type().base_type() == "pointer") {
auto di = m_ts.get_deref_info(result->type());
auto base_type = di.result_type;
assert(di.mem_deref);
assert(di.can_deref);
auto offset = compile_integer(di.stride, env)->to_gpr(env);
// todo, check for integer and avoid runtime multiply
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::IMUL_32, offset, index_value));
auto loc = fe->alloc_val<MemoryOffsetVal>(result->type(), result, offset);
Val* loc = nullptr;
if (has_constant_idx) {
loc = fe->alloc_val<MemoryOffsetConstantVal>(result->type(), result,
constant_index_value * di.stride);
} else {
RegVal* offset = fe->make_gpr(TypeSpec("int"));
compile_constant_product(offset, index_value, di.stride, env);
loc = fe->alloc_val<MemoryOffsetVal>(result->type(), result, offset);
}
result = fe->alloc_val<MemoryDerefVal>(di.result_type, loc, MemLoadInfo(di));
result->mark_as_settable();
} else if (result->type().base_type() == "array") {
@@ -537,15 +553,22 @@ Val* Compiler::compile_deref(const goos::Object& form, const goos::Object& _rest
assert(base_type == result->type().get_single_arg());
assert(di.mem_deref);
assert(di.can_deref);
// the total offset is 12 + stride * idx
auto offset = compile_integer(ARRAY_DATA_OFFSET, env)->to_gpr(env);
auto stride = compile_integer(di.stride, env)->to_gpr(env);
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::IMUL_32, stride, index_value));
env->emit_ir<IR_IntegerMath>(IntegerMathKind::ADD_64, offset, stride);
// offset now contains the total offset.
Val* loc = nullptr;
if (has_constant_idx) {
loc = fe->alloc_val<MemoryOffsetConstantVal>(
loc_type, result, ARRAY_DATA_OFFSET + di.stride * constant_index_value);
} else {
// the total offset is 12 + stride * idx
auto arr_off = compile_integer(ARRAY_DATA_OFFSET, env)->to_gpr(env);
RegVal* offset = fe->make_gpr(TypeSpec("int"));
compile_constant_product(offset, index_value, di.stride, env);
env->emit_ir<IR_IntegerMath>(IntegerMathKind::ADD_64, offset, arr_off);
// create a location to deref (so we can do address-of and get this), with pointer type
loc = fe->alloc_val<MemoryOffsetVal>(loc_type, result, offset);
}
// create a location to deref (so we can do address-of and get this), with pointer type
auto loc = fe->alloc_val<MemoryOffsetVal>(loc_type, result, offset);
// and result type.
result = fe->alloc_val<MemoryDerefVal>(di.result_type, loc, MemLoadInfo(di));
// array values should be settable
@@ -0,0 +1,5 @@
(let ((arr (new 'global 'boxed-array int16 12))
(x 3))
;; 12 + 3 * 2 = 18
(format #t "~D~%" (&- (&-> arr 3) arr))
)
+4
View File
@@ -361,6 +361,10 @@ TEST_F(WithGameTests, XMMSpill) {
runner.run_static_test(env, testCategory, "test-xmm-spill.gc", {"253.0000\n0\n"});
}
TEST_F(WithGameTests, BoxedArrayIndex) {
runner.run_static_test(env, testCategory, "test-boxed-array-index.gc", {"18\n0\n"});
}
TEST(TypeConsistency, TypeConsistency) {
Compiler compiler;
compiler.enable_throw_on_redefines();