mirror of
https://github.com/open-goal/jak-project
synced 2026-07-07 22:22:21 -04:00
Add support for stack integers (#135)
* add support for stack integers * update documentation * revise value type stack variables
This commit is contained in:
+3
-1
@@ -44,4 +44,6 @@
|
||||
- Creating a field of 128-bit value type no longer causes a compiler crash
|
||||
- 128-bit fields are inspected as `<cannot-print>`
|
||||
- Static fields can now contain floating point values
|
||||
- Fixed a bug where loading a float from an object and immediately using it math would cause a compiler crash
|
||||
- Fixed a bug where loading a float from an object and immediately using it math would cause a compiler crash
|
||||
|
||||
- Arrays of value types can be created on the stack with `new`.
|
||||
+36
-1
@@ -605,7 +605,10 @@ Get element from pair
|
||||
The type of the result is always `object`, as pairs can hold any `object`. The type-check for `car` and `cdr` is relaxed - it allows it to be applied to any `pair` or `object`. The reason for allowing `object` is so you can write `(car (car x))` instead of `(car (the pair (car x)))`. However, if the argument to `car` is not a `pair`, you will get garbage or a crash.
|
||||
|
||||
## `new`
|
||||
See section on creating new GOAL objects
|
||||
```lisp
|
||||
(new [allocation] [new-type-specification] [args])
|
||||
```
|
||||
See section on creating new GOAL objects.
|
||||
|
||||
## `print-type`
|
||||
Print the type of some GOAL expression at compile time.
|
||||
@@ -863,6 +866,38 @@ These can differ by padding for alignment.
|
||||
## Built-in Methods
|
||||
|
||||
## New - How To Create GOAL Objects
|
||||
GOAL has several different ways to create objects, all using the `new` form.
|
||||
|
||||
### Heap Allocated Objects
|
||||
A new object can be allocated on a heap with `(new 'global 'obj-type [new-method-arguments])`.
|
||||
This simply calls the `new` method of the given type. You can also replace `'global` with `'debug` to allocate on the debug heap.
|
||||
Currently these are the only two heaps supported, in the future you will be able to call the new method with other arguments
|
||||
to allow you to do an "in place new" or allocate on a different heap.
|
||||
|
||||
This will only work on structures and basics. If you want a heap allocated float/integer/pointer, create an array of size 1.
|
||||
This will work on dynamically sized items.
|
||||
|
||||
### Heap Allocated Arrays
|
||||
You can construct a heap array with `(new 'global 'inline-array 'obj-type count)` or `(new 'global 'array 'obj-type count)`.
|
||||
These objects are not initialized. Note that the `array` version creates a `(pointer obj-type)` plain array,
|
||||
__not__ a GOAL `array` type fancy array. In the future this may change because it is confusing.
|
||||
|
||||
Because these objects are uninitialized, you cannot provide constructor arguments.
|
||||
You cannot use this on dynamically sized member types. However, the array size can be determined at runtime.
|
||||
|
||||
### Static Objects
|
||||
You can create a static object with `(new 'static 'obj-type [field-def]...)`. For now it must be a structure or basic.
|
||||
Each field def looks like `:field-name field-value`. The `field-value` is evaluated at compile time. For now, fields
|
||||
can only be integers, floats, or symbols.
|
||||
|
||||
Fields which aren't explicitly initialized are zeroed, except for the type field of basics, which is properly initialized to the correct type.
|
||||
|
||||
This does not work on dynamically sized structures.
|
||||
|
||||
### Stack Allocated Objects
|
||||
Currently only arrays of integers, floats, or pointers can be stack allocated.
|
||||
For example, use `(new 'array 'int32 1)` to get a `(pointer int32)`. Unlike heap allocated arrays, these stack arrays
|
||||
must have a size that can be determined at compile time.
|
||||
|
||||
## Defining a `new` Method
|
||||
|
||||
|
||||
@@ -83,8 +83,9 @@ void CodeGenerator::do_function(FunctionEnv* env, int f_idx) {
|
||||
// do we include an extra push to get 8 more bytes to keep the stack aligned?
|
||||
bool bonus_push = false;
|
||||
|
||||
// the offset to add directly to rsp for stack variables (no push/pop)
|
||||
int manually_added_stack_offset = GPR_SIZE * allocs.stack_slots;
|
||||
// the offset to add directly to rsp for stack variables or spills (no push/pop)
|
||||
int manually_added_stack_offset =
|
||||
GPR_SIZE * (allocs.stack_slots_for_spills + allocs.stack_slots_for_vars);
|
||||
stack_offset += manually_added_stack_offset;
|
||||
|
||||
// do we need to align or manually offset?
|
||||
|
||||
@@ -165,6 +165,7 @@ void Compiler::color_object_file(FileEnv* env) {
|
||||
|
||||
input.max_vars = f->max_vars();
|
||||
input.constraints = f->constraints();
|
||||
input.stack_slots_for_stack_vars = f->stack_slots_used_for_stack_vars();
|
||||
|
||||
if (m_settings.debug_print_regalloc) {
|
||||
input.debug_settings.print_input = true;
|
||||
|
||||
@@ -261,6 +261,14 @@ RegVal* FunctionEnv::lexical_lookup(goos::Object sym) {
|
||||
return kv->second;
|
||||
}
|
||||
|
||||
StackVarAddrVal* FunctionEnv::allocate_stack_variable(const TypeSpec& ts, int size_bytes) {
|
||||
require_aligned_stack();
|
||||
int slots_used = (size_bytes + emitter::GPR_SIZE - 1) / emitter::GPR_SIZE;
|
||||
auto result = alloc_val<StackVarAddrVal>(ts, m_stack_var_slots_used, slots_used);
|
||||
m_stack_var_slots_used += slots_used;
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// LexicalEnv
|
||||
///////////////////
|
||||
|
||||
@@ -157,19 +157,18 @@ class FunctionEnv : public DeclareEnv {
|
||||
void constrain(const IRegConstraint& c) { m_constraints.push_back(c); }
|
||||
void set_allocations(const AllocationResult& result) { m_regalloc_result = result; }
|
||||
RegVal* lexical_lookup(goos::Object sym) override;
|
||||
|
||||
const AllocationResult& alloc_result() { return m_regalloc_result; }
|
||||
|
||||
bool needs_aligned_stack() const { return m_aligned_stack_required; }
|
||||
void require_aligned_stack() { m_aligned_stack_required = true; }
|
||||
|
||||
Label* alloc_unnamed_label() {
|
||||
m_unnamed_labels.emplace_back(std::make_unique<Label>());
|
||||
return m_unnamed_labels.back().get();
|
||||
}
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
StackVarAddrVal* allocate_stack_variable(const TypeSpec& ts, int size_bytes);
|
||||
int stack_slots_used_for_stack_vars() const { return m_stack_var_slots_used; }
|
||||
|
||||
int idx_in_file = -1;
|
||||
|
||||
template <typename T, class... Args>
|
||||
@@ -205,7 +204,7 @@ class FunctionEnv : public DeclareEnv {
|
||||
AllocationResult m_regalloc_result;
|
||||
|
||||
bool m_aligned_stack_required = false;
|
||||
|
||||
int m_stack_var_slots_used = 0;
|
||||
std::unordered_map<std::string, Label> m_labels;
|
||||
std::vector<std::unique_ptr<Label>> m_unnamed_labels;
|
||||
};
|
||||
|
||||
+55
-20
@@ -11,6 +11,30 @@ Register get_reg(const RegVal* rv, const AllocationResult& allocs, emitter::IR_R
|
||||
assert(ass.kind == Assignment::Kind::REGISTER);
|
||||
return ass.reg;
|
||||
}
|
||||
|
||||
void load_constant(u64 value,
|
||||
emitter::ObjectGenerator* gen,
|
||||
emitter::IR_Record irec,
|
||||
Register dest_reg) {
|
||||
s64 svalue = value;
|
||||
if (svalue == 0) {
|
||||
gen->add_instr(IGen::xor_gpr64_gpr64(dest_reg, dest_reg), irec);
|
||||
} else if (svalue > 0) {
|
||||
if (svalue < UINT32_MAX) {
|
||||
gen->add_instr(IGen::mov_gpr64_u32(dest_reg, value), irec);
|
||||
} else {
|
||||
// need a real 64 bit load
|
||||
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, value), irec);
|
||||
}
|
||||
} else {
|
||||
if (svalue >= INT32_MIN) {
|
||||
gen->add_instr(IGen::mov_gpr64_s32(dest_reg, svalue), irec);
|
||||
} else {
|
||||
// need a real 64 bit load
|
||||
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, value), irec);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
///////////
|
||||
@@ -77,26 +101,7 @@ void IR_LoadConstant64::do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) {
|
||||
auto dest_reg = get_reg(m_dest, allocs, irec);
|
||||
|
||||
s64 svalue = m_value;
|
||||
|
||||
if (svalue == 0) {
|
||||
gen->add_instr(IGen::xor_gpr64_gpr64(dest_reg, dest_reg), irec);
|
||||
} else if (svalue > 0) {
|
||||
if (svalue < UINT32_MAX) {
|
||||
gen->add_instr(IGen::mov_gpr64_u32(dest_reg, m_value), irec);
|
||||
} else {
|
||||
// need a real 64 bit load
|
||||
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, m_value), irec);
|
||||
}
|
||||
} else {
|
||||
if (svalue >= INT32_MIN) {
|
||||
gen->add_instr(IGen::mov_gpr64_s32(dest_reg, svalue), irec);
|
||||
} else {
|
||||
// need a real 64 bit load
|
||||
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, m_value), irec);
|
||||
}
|
||||
}
|
||||
load_constant(m_value, gen, irec, dest_reg);
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
@@ -858,4 +863,34 @@ void IR_IntToFloat::do_codegen(emitter::ObjectGenerator* gen,
|
||||
emitter::IR_Record irec) {
|
||||
gen->add_instr(IGen::int32_to_float(get_reg(m_dest, allocs, irec), get_reg(m_src, allocs, irec)),
|
||||
irec);
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
// GetStackAddr
|
||||
///////////////////////
|
||||
|
||||
IR_GetStackAddr::IR_GetStackAddr(const RegVal* dest, int slot) : m_dest(dest), m_slot(slot) {}
|
||||
|
||||
std::string IR_GetStackAddr::print() {
|
||||
return fmt::format("mov {}, stack-slot-{}", m_dest->print(), m_slot);
|
||||
}
|
||||
|
||||
RegAllocInstr IR_GetStackAddr::to_rai() {
|
||||
RegAllocInstr rai;
|
||||
rai.write.push_back(m_dest->ireg());
|
||||
return rai;
|
||||
}
|
||||
|
||||
void IR_GetStackAddr::do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) {
|
||||
auto dest_reg = get_reg(m_dest, allocs, irec);
|
||||
int offset = GPR_SIZE * (m_slot + allocs.stack_slots_for_spills);
|
||||
|
||||
// dest = offset
|
||||
load_constant(offset, gen, irec, dest_reg);
|
||||
// dest = offset + RSP
|
||||
gen->add_instr(IGen::add_gpr64_gpr64(dest_reg, RSP), irec);
|
||||
// dest = offset + RSP - offset
|
||||
gen->add_instr(IGen::sub_gpr64_gpr64(dest_reg, gRegInfo.get_offset_reg()), irec);
|
||||
}
|
||||
@@ -353,4 +353,18 @@ class IR_IntToFloat : public IR {
|
||||
const RegVal* m_src = nullptr;
|
||||
};
|
||||
|
||||
class IR_GetStackAddr : public IR {
|
||||
public:
|
||||
IR_GetStackAddr(const RegVal* dest, int slot);
|
||||
std::string print() override;
|
||||
RegAllocInstr to_rai() override;
|
||||
void do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) override;
|
||||
|
||||
private:
|
||||
const RegVal* m_dest = nullptr;
|
||||
int m_slot = -1;
|
||||
};
|
||||
|
||||
#endif // JAK_IR_H
|
||||
|
||||
+12
-2
@@ -27,7 +27,9 @@ RegVal* Val::to_xmm(Env* fe) {
|
||||
return rv;
|
||||
} else {
|
||||
assert(false);
|
||||
throw std::runtime_error("Register is not an XMM[0-15] register.");
|
||||
auto re = fe->make_xmm(coerce_to_reg_type(m_ts));
|
||||
fe->emit(std::make_unique<IR_RegSet>(re, rv));
|
||||
return re;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +117,7 @@ RegVal* MemoryOffsetVal::to_reg(Env* fe) {
|
||||
}
|
||||
|
||||
RegVal* MemoryDerefVal::to_reg(Env* fe) {
|
||||
// todo, support better loads/stores from the stack
|
||||
auto base_as_co = dynamic_cast<MemoryOffsetConstantVal*>(base);
|
||||
if (base_as_co) {
|
||||
auto re = fe->make_gpr(coerce_to_reg_type(m_ts));
|
||||
@@ -130,6 +133,7 @@ RegVal* MemoryDerefVal::to_reg(Env* fe) {
|
||||
}
|
||||
|
||||
RegVal* MemoryDerefVal::to_xmm(Env* fe) {
|
||||
// todo, support better loads/stores from the stack
|
||||
auto base_as_co = dynamic_cast<MemoryOffsetConstantVal*>(base);
|
||||
if (base_as_co) {
|
||||
auto re = fe->make_xmm(coerce_to_reg_type(m_ts));
|
||||
@@ -168,4 +172,10 @@ RegVal* PairEntryVal::to_reg(Env* fe) {
|
||||
info.size = 4;
|
||||
fe->emit(std::make_unique<IR_LoadConstOffset>(re, offset, base->to_gpr(fe), info));
|
||||
return re;
|
||||
}
|
||||
}
|
||||
|
||||
RegVal* StackVarAddrVal::to_reg(Env* fe) {
|
||||
auto re = fe->make_gpr(coerce_to_reg_type(m_ts));
|
||||
fe->emit(std::make_unique<IR_GetStackAddr>(re, m_slot));
|
||||
return re;
|
||||
}
|
||||
|
||||
+18
-5
@@ -152,6 +152,23 @@ struct MemLoadInfo {
|
||||
int size = -1;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A spot on the stack.
|
||||
*/
|
||||
class StackVarAddrVal : public Val {
|
||||
public:
|
||||
StackVarAddrVal(TypeSpec ts, int slot, int slot_count)
|
||||
: Val(std::move(ts)), m_slot(slot), m_slot_count(slot_count) {}
|
||||
int slot() const { return m_slot; }
|
||||
int slot_count() const { return m_slot_count; }
|
||||
std::string print() const override { return "stack-" + std::to_string(m_slot); }
|
||||
|
||||
RegVal* to_reg(Env* fe) override;
|
||||
|
||||
private:
|
||||
int m_slot, m_slot_count;
|
||||
};
|
||||
|
||||
class MemoryOffsetConstantVal : public Val {
|
||||
public:
|
||||
MemoryOffsetConstantVal(TypeSpec ts, Val* _base, int _offset)
|
||||
@@ -174,9 +191,6 @@ class MemoryOffsetVal : public Val {
|
||||
Val* offset = nullptr;
|
||||
};
|
||||
|
||||
// MemOffConstant
|
||||
// MemOffVar
|
||||
|
||||
class MemoryDerefVal : public Val {
|
||||
public:
|
||||
MemoryDerefVal(TypeSpec ts, Val* _base, MemLoadInfo _info)
|
||||
@@ -225,8 +239,7 @@ class FloatConstantVal : public Val {
|
||||
protected:
|
||||
StaticFloat* m_value = nullptr;
|
||||
};
|
||||
// IntegerConstant
|
||||
// FloatConstant
|
||||
|
||||
// Bitfield
|
||||
|
||||
#endif // JAK_VAL_H
|
||||
|
||||
@@ -507,7 +507,7 @@ Val* Compiler::compile_addr_of(const goos::Object& form, const goos::Object& res
|
||||
auto loc = compile_error_guard(args.unnamed.at(0), env);
|
||||
auto as_mem_deref = dynamic_cast<MemoryDerefVal*>(loc);
|
||||
if (!as_mem_deref) {
|
||||
throw_compile_error(form, "Cannot take the address of this");
|
||||
throw_compile_error(form, "Cannot take the address of this " + loc->print());
|
||||
}
|
||||
return as_mem_deref->base;
|
||||
}
|
||||
@@ -574,6 +574,8 @@ Val* Compiler::compile_print_type(const goos::Object& form, const goos::Object&
|
||||
|
||||
Val* Compiler::compile_new(const goos::Object& form, const goos::Object& _rest, Env* env) {
|
||||
// todo - support compound types.
|
||||
// todo - stack arrays?
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
|
||||
auto allocation = quoted_sym_as_string(pair_car(_rest));
|
||||
auto rest = &pair_cdr(_rest);
|
||||
@@ -649,6 +651,46 @@ Val* Compiler::compile_new(const goos::Object& form, const goos::Object& _rest,
|
||||
if (is_structure(type_of_object)) {
|
||||
return compile_new_static_structure_or_basic(form, type_of_object, *rest, env);
|
||||
}
|
||||
} else if (allocation == "stack") {
|
||||
auto type_of_object = m_ts.make_typespec(type_as_string);
|
||||
|
||||
printf("type as string is %s\n", type_as_string.c_str());
|
||||
if (type_as_string == "inline-array" || type_as_string == "array") {
|
||||
bool is_inline = type_as_string == "inline-array";
|
||||
auto elt_type = quoted_sym_as_string(pair_car(*rest));
|
||||
rest = &pair_cdr(*rest);
|
||||
|
||||
auto count_obj = pair_car(*rest);
|
||||
rest = &pair_cdr(*rest);
|
||||
// try to get the size as a compile time constant.
|
||||
int64_t constant_count = 0;
|
||||
bool is_constant_size = try_getting_constant_integer(count_obj, &constant_count, env);
|
||||
if (!is_constant_size) {
|
||||
throw_compile_error(form, "cannot create a dynamically sized stack array");
|
||||
}
|
||||
|
||||
if (!rest->is_empty_list()) {
|
||||
// got extra arguments
|
||||
throw_compile_error(form, "new array form got more arguments than expected");
|
||||
}
|
||||
|
||||
auto ts = is_inline ? m_ts.make_inline_array_typespec(elt_type)
|
||||
: m_ts.make_pointer_typespec(elt_type);
|
||||
auto info = m_ts.get_deref_info(ts);
|
||||
if (!info.can_deref) {
|
||||
throw_compile_error(form,
|
||||
fmt::format("Cannot make an {} of {}\n", type_as_string, ts.print()));
|
||||
}
|
||||
|
||||
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);
|
||||
return addr;
|
||||
}
|
||||
// todo, stack structures.
|
||||
}
|
||||
// todo, stack not-arrays
|
||||
}
|
||||
|
||||
throw_compile_error(form, "unsupported new form");
|
||||
|
||||
@@ -160,7 +160,8 @@ AllocationResult allocate_registers(const AllocationInput& input) {
|
||||
// prepare the result
|
||||
result.ok = true;
|
||||
result.needs_aligned_stack_for_spills = cache.used_stack;
|
||||
result.stack_slots = cache.current_stack_slot;
|
||||
result.stack_slots_for_spills = cache.current_stack_slot;
|
||||
result.stack_slots_for_vars = input.stack_slots_for_stack_vars;
|
||||
|
||||
// copy over the assignment result
|
||||
result.assignment.resize(cache.max_var);
|
||||
|
||||
@@ -68,8 +68,9 @@ struct AllocationResult {
|
||||
std::vector<std::vector<Assignment>> assignment; // variable, instruction
|
||||
std::vector<LiveInfo> ass_as_ranges; // another format, maybe easier?
|
||||
std::vector<emitter::Register> used_saved_regs; // which saved regs get clobbered?
|
||||
int stack_slots = 0; // how many space on the stack do we need?
|
||||
std::vector<StackOp> stack_ops; // additional instructions to spill/restore
|
||||
int stack_slots_for_spills = 0; // how many space on the stack do we need?
|
||||
int stack_slots_for_vars = 0;
|
||||
std::vector<StackOp> stack_ops; // additional instructions to spill/restore
|
||||
bool needs_aligned_stack_for_spills = false;
|
||||
};
|
||||
|
||||
@@ -81,6 +82,7 @@ struct AllocationInput {
|
||||
std::vector<IRegConstraint> constraints; // all register constraints
|
||||
int max_vars = -1; // maximum register id.
|
||||
std::vector<std::string> debug_instruction_names; // optional, for debug prints
|
||||
int stack_slots_for_stack_vars = 0;
|
||||
|
||||
struct {
|
||||
bool print_input = false;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
(let* ((x (new 'stack 'array 'int8 1))
|
||||
(x-addr (the uint x)))
|
||||
(if (and (> x-addr #x7ffff00)
|
||||
(< x-addr #x7ffffff))
|
||||
1
|
||||
0)
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
(let ((x (new 'stack 'array 'int32 1))
|
||||
(y (new 'stack 'array 'int8 2))
|
||||
(z (new 'stack 'array 'int8 1)))
|
||||
(set! (-> x) 10)
|
||||
(set! (-> z) 0)
|
||||
(set! (-> y) #xfffffffff)
|
||||
(set! (-> y 1) 3)
|
||||
(+ (-> x) (-> y) (-> z) (-> y 1))
|
||||
)
|
||||
@@ -0,0 +1,59 @@
|
||||
;; like test-approx-pi, but using stack variables
|
||||
(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)))
|
||||
(set! (-> rad) (* res res))
|
||||
(set! (-> count) 0)
|
||||
|
||||
|
||||
(dotimes (x res)
|
||||
(dotimes (y res)
|
||||
(if (> (-> rad) (+ (* x x) (* y y)))
|
||||
(+! (-> count) 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(* 4.0 (/ (the float (-> count)) (the float (-> rad))))
|
||||
)
|
||||
)
|
||||
|
||||
(let ((approx-pi (test-approx-pi-stack 1000)))
|
||||
(expect-true (> approx-pi 3.14))
|
||||
(expect-true (< approx-pi 3.15))
|
||||
)
|
||||
|
||||
|
||||
(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)))
|
||||
(set! (-> rad) (* res res))
|
||||
(set! (-> count) 0.0)
|
||||
(set! (-> x) 0.0)
|
||||
(set! (-> y) (-> x))
|
||||
(set! (-> scale) (/ 1.0 (-> rad)))
|
||||
|
||||
(while (< (-> x) res)
|
||||
(set! (-> y) 0.0)
|
||||
(while (< (-> y) res)
|
||||
(if (> (-> rad) (+ (* (-> x) (-> x)) (* (-> y) (-> y))))
|
||||
(+! (-> count) (-> scale))
|
||||
)
|
||||
(+! (-> y) 1.0)
|
||||
)
|
||||
(+! (-> x) 1.0)
|
||||
)
|
||||
(* 4.0 (-> count))
|
||||
)
|
||||
)
|
||||
|
||||
(let ((approx-pi (test-approx-pi-float-stack 500.0)))
|
||||
(expect-true (> approx-pi 3.14))
|
||||
(expect-true (< approx-pi 3.15))
|
||||
)
|
||||
|
||||
(finish-test)
|
||||
@@ -29,7 +29,6 @@
|
||||
(while (< x res)
|
||||
(set! y 0.0)
|
||||
(while (< y res)
|
||||
; (format #t "tapf ~f ~f ~f~%" x y res)
|
||||
(if (> rad (+ (* x x) (* y y)))
|
||||
(+! count scale)
|
||||
)
|
||||
|
||||
@@ -68,3 +68,8 @@ TEST_F(VariableTests, Let) {
|
||||
runner.run_static_test(env, testCategory, "let-star.static.gc", {"30\n"});
|
||||
runner.run_static_test(env, testCategory, "mlet.static.gc", {"10\n"});
|
||||
}
|
||||
|
||||
TEST_F(VariableTests, StackVars) {
|
||||
runner.run_static_test(env, testCategory, "stack-ints.gc", {"12\n"});
|
||||
runner.run_static_test(env, testCategory, "stack-ints-2.gc", {"1\n"});
|
||||
}
|
||||
@@ -219,6 +219,11 @@ TEST_F(WithGameTests, ApproxPi) {
|
||||
get_test_pass_string("approx-pi", 4));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, ApproxPiStack) {
|
||||
runner.run_static_test(env, testCategory, "test-approx-pi-stack.gc",
|
||||
get_test_pass_string("approx-pi-stack", 4));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, DynamicType) {
|
||||
runner.run_static_test(env, testCategory, "test-dynamic-type.gc",
|
||||
get_test_pass_string("dynamic-type", 4));
|
||||
|
||||
Reference in New Issue
Block a user