support inline arrays on stack (#726)

This commit is contained in:
water111 2021-07-26 21:39:05 -04:00 committed by GitHub
parent 1352adb6d4
commit eee8390ec0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 124 additions and 66 deletions

View file

@ -261,7 +261,8 @@ TP_Type get_stack_type_at_constant_offset(int offset,
if (offset == structure.hint.stack_offset) {
// special case just getting the variable
if (structure.hint.container_type == StackStructureHint::ContainerType::NONE) {
if (structure.hint.container_type == StackStructureHint::ContainerType::NONE ||
structure.hint.container_type == StackStructureHint::ContainerType::INLINE_ARRAY) {
return TP_Type::make_from_ts(coerce_to_reg_type(structure.ref_type));
}
}

View file

@ -518,12 +518,12 @@ void Env::set_stack_structure_hints(const std::vector<StackStructureHint>& hints
for (auto& hint : hints) {
StackStructureEntry entry;
entry.hint = hint;
// parse the type spec.
TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
auto type_info = dts->ts.lookup_type(base_typespec);
switch (hint.container_type) {
case StackStructureHint::ContainerType::NONE:
case StackStructureHint::ContainerType::NONE: {
// parse the type spec.
TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
auto type_info = dts->ts.lookup_type(base_typespec);
// just a plain object on the stack.
if (!type_info->is_reference()) {
throw std::runtime_error(
@ -540,8 +540,29 @@ void Env::set_stack_structure_hints(const std::vector<StackStructureHint>& hints
entry.ref_type.print(), entry.hint.stack_offset,
type_info->get_in_memory_alignment());
}
} break;
break;
case StackStructureHint::ContainerType::INLINE_ARRAY: {
TypeSpec base_typespec = dts->parse_type_spec(hint.element_type);
auto type_info = dts->ts.lookup_type(base_typespec);
if (!type_info->is_reference()) {
throw std::runtime_error(
fmt::format("Stack inline-array element type {} is not a reference and cannot be "
"stored in an inline-array. Use an array instead.",
base_typespec.print()));
}
entry.ref_type = TypeSpec("inline-array", {TypeSpec(base_typespec)});
entry.size = 1; // we assume that there is no constant propagation into this array and
// make this only trigger in get_stack_type if we hit exactly.
// sanity check the alignment
if (align(entry.hint.stack_offset, type_info->get_in_memory_alignment()) !=
entry.hint.stack_offset) {
lg::error("Misaligned stack variable of type {} offset {} required align {}\n",
entry.ref_type.print(), entry.hint.stack_offset,
type_info->get_in_memory_alignment());
}
} break;
default:
assert(false);
}

View file

@ -2617,6 +2617,10 @@ goos::Object StackStructureDefElement::to_form_internal(const Env&) const {
case StackStructureHint::ContainerType::NONE:
return pretty_print::build_list(
fmt::format("new 'stack-no-clear '{}", m_entry.ref_type.print()));
case StackStructureHint::ContainerType::INLINE_ARRAY:
return pretty_print::build_list(fmt::format("new 'stack-no-clear 'inline-array '{} {}",
m_entry.ref_type.get_single_arg().print(),
m_entry.hint.container_size));
default:
assert(false);
}

View file

@ -181,4 +181,7 @@
- `meters`, `degrees`, and `seconds` types have been added.
- Bitfields with `symbol` fields used in an immediate `(new 'static ...)` can now define the symbol in the `new` form.
- Bitfields with `float` fields used in an immediate `(new 'static ...)` in code can use a non-constant floating point value.
- Multiple variables assigned to the same register using `:reg` in `rlet` (or overlapping with `self` in a behavior) will now be merged to a single variable instead of causing a compiler error. Variables will have their own type, but they will all be an alias of the same exact register.
- Multiple variables assigned to the same register using `:reg` in `rlet` (or overlapping with `self` in a behavior) will now be merged to a single variable instead of causing a compiler error. Variables will have their own type, but they will all be an alias of the same exact register.
- Stack arrays of uint128 will now be 16-byte aligned instead of sometimes only 8.
- Inline arrays of structures are now allowed with `stack-no-clear`.
- Creating arrays on the stack now must be done with `stack-no-clear` as they are not memset to 0 or constructed in any way.

View file

@ -266,7 +266,7 @@
(defmacro print128 (value &key (stream #t))
"Print a 128-bit value"
`(let ((temp (new 'stack 'array 'uint64 2)))
`(let ((temp (new 'stack-no-clear 'array 'uint64 2)))
(set! (-> (the (pointer uint128) temp)) ,value)
(format ,stream "#x~16X~16X" (-> temp 1) (-> temp 0))
)

View file

@ -2265,7 +2265,7 @@
This will then return the value of the function you called!"
(rlet ((pp :reg r13 :type process))
(let ((param-array (new 'stack 'array 'uint64 6))
(let ((param-array (new 'stack-no-clear 'array 'uint64 6))
)
;; copy params to the stack.

View file

@ -3,6 +3,7 @@
#include "common/type_system/defenum.h"
#include "common/type_system/deftype.h"
#include "goalc/emitter/CallingConvention.h"
#include "common/util/math_util.h"
namespace {
@ -1022,6 +1023,9 @@ Val* Compiler::compile_stack_new(const goos::Object& form,
auto type_of_object = parse_typespec(unquote(type));
auto fe = get_parent_env_of_type<FunctionEnv>(env);
if (type_of_object == TypeSpec("inline-array") || type_of_object == TypeSpec("array")) {
if (call_constructor) {
throw_compiler_error(form, "Constructing stack arrays is not yet supported");
}
bool is_inline = type_of_object == TypeSpec("inline-array");
auto elt_type = quoted_sym_as_string(pair_car(*rest));
rest = &pair_cdr(*rest);
@ -1050,16 +1054,22 @@ Val* Compiler::compile_stack_new(const goos::Object& form,
if (!info.can_deref) {
throw_compiler_error(form, "Cannot make an {} of {}\n", type_of_object.print(), ts.print());
}
auto type_info = m_ts.lookup_type(ts.get_single_arg());
if (!m_ts.lookup_type(elt_type)->is_reference()) {
// not a reference type
int size_in_bytes = info.stride * constant_count;
auto addr = fe->allocate_stack_variable(ts, size_in_bytes);
auto addr = fe->allocate_aligned_stack_variable(ts, size_in_bytes,
type_info->get_in_memory_alignment());
return addr;
}
// todo
throw_compiler_error(form, "Static array of type {} is not yet supported.", ts.print());
return get_none();
int stride =
align(type_info->get_size_in_memory(), type_info->get_inline_array_stride_alignment());
assert(stride == info.stride);
int size_in_bytes = info.stride * constant_count;
auto addr = fe->allocate_aligned_stack_variable(ts, size_in_bytes, stride);
return addr;
} else {
auto ti = m_ts.lookup_type(type_of_object);

View file

@ -3,7 +3,7 @@
(format #t "TARGET FUNCTION ~D ~D ~D~%" a0 a1 a2)
(format #t "~D ~D ~D~%" a3 a4 a5)
(let ((stack-arr (new 'stack 'array 'uint8 12)))
(let ((stack-arr (new 'stack-no-clear 'array 'uint8 12)))
(format #t "Stack Alignemnt ~D/16~%" (logand 15 (the uint stack-arr)))
)
@ -28,7 +28,7 @@
)
(defun target-function-2 ()
(let ((stack-var (new 'stack 'array 'int32 1)))
(let ((stack-var (new 'stack-no-clear 'array 'int32 1)))
(set! (-> stack-var) 0)
(countdown (i 10)
(format #t "proc2: ~D~%" (-> stack-var))
@ -61,7 +61,7 @@
(defun init-child-proc (a0 a1 a2 a3 a4 a5)
(format #t "Args: ~D ~D ~D~%" a0 a1 a2)
(format #t "~D ~D ~D~%" a3 a4 a5)
(let ((stack-arr (new 'stack 'array 'uint8 12)))
(let ((stack-arr (new 'stack-no-clear 'array 'uint8 12)))
(format #t "Stack Alignemnt ~D/16~%" (logand 15 (the uint stack-arr)))
)
(if (eq? a0 (the int 0))
@ -189,4 +189,4 @@
)
)
)
)

View file

@ -1,7 +1,7 @@
(defun stack-test ()
(let ((arr (new 'stack 'array 'uint8 12)))
(let ((arr (new 'stack-no-clear 'array 'uint8 12)))
(logand #b1111 (the uint (&-> arr 3)))
)
)
(stack-test)
(stack-test)

View file

@ -1,4 +1,4 @@
(let* ((x (new 'stack 'array 'int8 1))
(let* ((x (new 'stack-no-clear 'array 'int8 1))
(x-addr (the uint x)))
(if (and (> x-addr #x7ffff00)
(< x-addr #x7ffffff))

View file

@ -1,9 +1,9 @@
(let ((x (new 'stack 'array 'int32 1))
(y (new 'stack 'array 'int8 2))
(z (new 'stack 'array 'int8 1)))
(let ((x (new 'stack-no-clear 'array 'int32 1))
(y (new 'stack-no-clear 'array 'int8 2))
(z (new 'stack-no-clear 'array 'int8 1)))
(set! (-> x) 10)
(set! (-> z) 0)
(set! (-> y) #xfffffffff)
(set! (-> y 1) 3)
(+ (-> x) (-> y) (-> z) (-> y 1))
)
)

View file

@ -2,8 +2,8 @@
(start-test "approx-pi-stack")
(defun test-approx-pi-stack ((res integer))
(let ((rad (new 'stack 'array 'int32 1))
(count (new 'stack 'array 'uint32 1)))
(let ((rad (new 'stack-no-clear 'array 'int32 1))
(count (new 'stack-no-clear 'array 'uint32 1)))
(set! (-> rad) (* res res))
(set! (-> count) 0)
@ -26,11 +26,11 @@
(defun test-approx-pi-float-stack ((res float))
(let* ((rad (new 'stack 'array 'float 1))
(count (new 'stack 'array 'float 1))
(x (new 'stack 'array 'float 1))
(y (new 'stack 'array 'float 1))
(scale (new 'stack 'array 'float 1)))
(let* ((rad (new 'stack-no-clear 'array 'float 1))
(count (new 'stack-no-clear 'array 'float 1))
(x (new 'stack-no-clear 'array 'float 1))
(y (new 'stack-no-clear 'array 'float 1))
(scale (new 'stack-no-clear 'array 'float 1)))
(set! (-> rad) (* res res))
(set! (-> count) 0.0)
(set! (-> x) 0.0)
@ -56,4 +56,4 @@
(expect-true (< approx-pi 3.15))
)
(finish-test)
(finish-test)

View file

@ -38,7 +38,7 @@
(ens test-enum 12))
)
(let ((obj (new 'stack 'enum-bitfield-test-type)))
(let ((obj (new 'stack-no-clear 'enum-bitfield-test-type)))
(format #t "bitfield spacing: ~d~%" (&- (&-> obj bf1) (&-> obj bf0)))
(format #t "enum spacing: ~d~%" (&- (&-> obj en1) (&-> obj en0)))
(format #t "bitfield array spacing: ~d~%" (&- (&-> obj bfs 1) (&-> obj bfs 0)))
@ -46,7 +46,7 @@
)
(defun function-enum-test ((x test-enum))
(let ((obj (new 'stack 'enum-bitfield-test-type)))
(let ((obj (new 'stack-no-clear 'enum-bitfield-test-type)))
(set! (-> obj en0) x)
(set! (-> obj ens 1) x)
)
@ -54,15 +54,15 @@
)
(defun function-bitfield-test ((x test-bitfield))
(let ((obj (new 'stack 'enum-bitfield-test-type)))
(let ((obj (new 'stack-no-clear 'enum-bitfield-test-type)))
(set! (-> obj bf0) x)
(set! (-> obj bfs 1) x)
)
(new 'static 'test-bitfield :f1 1)
)
(let ((obj (new 'stack 'enum-bitfield-test-type))
(mem (new 'stack 'array 'uint8 12))
(let ((obj (new 'stack-no-clear 'enum-bitfield-test-type))
(mem (new 'stack-no-clear 'array 'uint8 12))
)
(set! (-> obj bf0) (function-bitfield-test (new 'static 'test-bitfield :f1 1)))
(set! (-> obj bf0) (function-bitfield-test (the test-bitfield 1)))
@ -77,4 +77,4 @@
)
(format #t "sizes: ~d ~d~%" (size-of test-enum) (size-of test-bitfield))
(format #t "sizes: ~d ~d~%" (size-of test-enum) (size-of test-bitfield))

View file

@ -11,7 +11,7 @@
)
(defun test-print-sound-name-chars ((arg0 int) (name sound-name) (arg2 int))
(let ((mem (new 'stack 'array 'uint8 16)))
(let ((mem (new 'stack-no-clear 'array 'uint8 16)))
(set! (-> (the (pointer sound-name) mem)) name)
(let ((i 0))
(while (and (< i 16) (nonzero? (-> mem i)))

View file

@ -13,7 +13,7 @@
(sync *load-str-rpc* '#t)
(let* ((got-length (new 'stack 'array 'int32 1))
(let* ((got-length (new 'stack-no-clear 'array 'int32 1))
(status (str-load-status got-length)))
;(format #t "Status is ~A, length ~D~%" status (-> got-length))
@ -41,4 +41,4 @@
(expect-true (= 5 (length (-> text data 1 text))))
)
(finish-test)
(finish-test)

View file

@ -20,8 +20,8 @@
result
)
(let ((arr (new 'stack 'array 'uint8 128))
(arr2 (new 'stack 'array 'uint8 128)))
(let ((arr (new 'stack-no-clear 'array 'uint8 128))
(arr2 (new 'stack-no-clear 'array 'uint8 128)))
(dotimes (i 128)
(set! (-> arr i) i)
(set! (-> arr2 (- 127 i)) i)
@ -46,4 +46,4 @@
(set! x 0)
(set! z y)
z
)
)

View file

@ -1,7 +1,7 @@
(let ((v1 (new 'stack 'array 'uint8 16))
(v2 (new 'stack 'array 'uint8 16))
(v3 (new 'stack 'array 'uint8 16))
(let ((v1 (new 'stack-no-clear 'array 'uint8 16))
(v2 (new 'stack-no-clear 'array 'uint8 16))
(v3 (new 'stack-no-clear 'array 'uint8 16))
)
;; initialize stack arrays

View file

@ -1,6 +1,6 @@
(let ((v1 (new 'stack 'array 'uint8 16))
(v2 (new 'stack 'array 'uint8 16))
(v3 (new 'stack 'array 'uint8 16))
(let ((v1 (new 'stack-no-clear 'array 'uint8 16))
(v2 (new 'stack-no-clear 'array 'uint8 16))
(v3 (new 'stack-no-clear 'array 'uint8 16))
)
;; initialize stack arrays
@ -30,8 +30,8 @@
(print128 v3-quad) (format #t "~%")
)
(let ((s1 (new 'stack 'array 'uint32 4))
(s2 (new 'stack 'array 'uint32 4)))
(let ((s1 (new 'stack-no-clear 'array 'uint32 4))
(s2 (new 'stack-no-clear 'array 'uint32 4)))
(set! (-> s1 0) #xdeadbeef)
(set! (-> s1 1) #x12312323)
(set! (-> s1 2) #x11112222)
@ -52,8 +52,8 @@
)
)
(let ((s1 (new 'stack 'array 'uint32 4))
(s2 (new 'stack 'array 'uint32 4)))
(let ((s1 (new 'stack-no-clear 'array 'uint32 4))
(s2 (new 'stack-no-clear 'array 'uint32 4)))
(set! (-> s1 0) #x300)
(set! (-> s1 1) #x3)
(set! (-> s1 2) #x100)

View file

@ -3,7 +3,7 @@
;; note - this makes some assumptions about how the OpenGOAL compiler will lay things out
;; on the stack.
(let ((x (new 'stack 'array 'uint8 (size-of dma-bucket)))
(y (new 'stack 'array 'uint 1)))
(let ((x (new 'stack-no-clear 'array 'uint8 (size-of dma-bucket)))
(y (new 'stack-no-clear 'array 'uint 1)))
(format #t "size of stack array is ~d~%" (&- y x))
)

View file

@ -0,0 +1,13 @@
(defun test-stack-inline-array ()
(let ((t0 (new 'stack-no-clear 'array 'int32 1))
(ta (new 'stack-no-clear 'array 'int32 1))
(t1 (new 'stack-no-clear 'inline-array 'vector 3))
(t2 (new 'stack-no-clear 'inline-array 'vector 2)))
;; these are somewhat specific to the current strategy for laying out the stack.
(format #t "#x~X~%" (&- t1 ta))
(format #t "#x~X~%" (&- t2 t1))
(set! (-> t1 1 quad) (the-as uint128 0))
)
)
(test-stack-inline-array)

View file

@ -42,8 +42,8 @@
(format #t "~%")
;; vector-sin-rad!
(let ((in (new 'stack 'vector))
(out (new 'stack 'vector))
(let ((in (new 'stack-no-clear 'vector))
(out (new 'stack-no-clear 'vector))
)
(set-vector! in PI_OVER_6 (- PI_OVER_4) PI_OVER_3 (- PI_OVER_2))
(vector-sin-rad! out in)
@ -59,8 +59,8 @@
(format #t "~%")
;; vector-cos-rad!
(let ((in (new 'stack 'vector))
(out (new 'stack 'vector))
(let ((in (new 'stack-no-clear 'vector))
(out (new 'stack-no-clear 'vector))
)
(set-vector! in PI_OVER_6 (- PI_OVER_4) PI_OVER_3 (- PI_OVER_2))
(vector-cos-rad! out in)
@ -69,9 +69,9 @@
(format #t "~%")
;; vector-sincos-rad!
(let ((in (new 'stack 'vector))
(out-sin (new 'stack 'vector))
(out-cos (new 'stack 'vector))
(let ((in (new 'stack-no-clear 'vector))
(out-sin (new 'stack-no-clear 'vector))
(out-cos (new 'stack-no-clear 'vector))
)
(set-vector! in PI_OVER_6 (- PI_OVER_4) PI_OVER_3 (- PI_OVER_2))
(vector-sincos-rad! out-sin out-cos in)
@ -81,7 +81,7 @@
(format #t "~%")
;; sincos-rad! (has cosine bug)
(let ((out (new 'stack 'array 'float 2)))
(let ((out (new 'stack-no-clear 'array 'float 2)))
(sincos-rad! out PI_OVER_4)
(format #t "~f ~f~%" (-> out 0) (-> out 1))
(sincos-rad! out (- PI_OVER_2))
@ -91,7 +91,7 @@
;; sincos! (has cosine bug)
(format #t "sincos!~%")
(let ((out (new 'stack 'array 'float 2)))
(let ((out (new 'stack-no-clear 'array 'float 2)))
(sincos! out (+ 0.0 (degrees 765)))
(format #t "~f ~f~%" (-> out 0) (-> out 1))
(sincos! out (+ (degrees 750) -0.0))

View file

@ -816,6 +816,12 @@ TEST_F(WithGameTests, PandPorPnor) {
"#x0200000c0a0808080200000402000000\n0\n"});
}
TEST_F(WithGameTests, StackInlineArray) {
runner.run_static_test(env, testCategory, "test-stack-inline-array.gc",
{"#x8\n"
"#x30\n0\n"});
}
TEST(TypeConsistency, TypeConsistency) {
Compiler compiler;
compiler.enable_throw_on_redefines();