mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04:00
add conditional stuff
This commit is contained in:
parent
f46acdf87a
commit
9ec9b5a22a
|
@ -597,8 +597,9 @@ u32 RunDGOStateMachine(IsoMessage* _cmd, IsoBufferHeader* buffer) {
|
|||
|
||||
// once we're done, send the header to the EE, and start reading object data
|
||||
if (cmd->bytes_processed == sizeof(ObjectHeader)) {
|
||||
printf("[Overlord DGO] Got object header for %s, object size 0x%x bytes (sent to 0x%p)\n",
|
||||
cmd->objHeader.name, cmd->objHeader.size, cmd->ee_destination_buffer);
|
||||
// printf("[Overlord DGO] Got object header for %s, object size 0x%x bytes (sent
|
||||
// to 0x%p)\n",
|
||||
// cmd->objHeader.name, cmd->objHeader.size, cmd->ee_destination_buffer);
|
||||
DMA_SendToEE(&cmd->objHeader, sizeof(ObjectHeader), cmd->ee_destination_buffer);
|
||||
DMA_Sync();
|
||||
cmd->ee_destination_buffer += sizeof(ObjectHeader);
|
||||
|
|
|
@ -33,6 +33,14 @@
|
|||
)
|
||||
)
|
||||
|
||||
(defmacro blg ()
|
||||
`(begin
|
||||
(build-game)
|
||||
(dgo-load "kernel" global #xf #x200000)
|
||||
(dgo-load "game" global #xf #x200000)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro e ()
|
||||
`(:exit)
|
||||
)
|
||||
|
@ -109,4 +117,54 @@
|
|||
;; otherwise don't ignore it.
|
||||
`(define ,name (lambda :name ,name ,bindings ,@body))
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro while (test &rest body)
|
||||
(with-gensyms (reloop test-exit)
|
||||
`(begin
|
||||
(goto ,test-exit)
|
||||
(label ,reloop)
|
||||
,@body
|
||||
(label ,test-exit)
|
||||
(when-goto ,test ,reloop)
|
||||
#f
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; Backup some values, and restore after executing body.
|
||||
;; Non-dynamic (nonlocal jumps out of body will skip restore)
|
||||
(defmacro protect (defs &rest body)
|
||||
(if (null? defs)
|
||||
;; nothing to backup, just insert body (base case)
|
||||
`(begin ,@body)
|
||||
|
||||
;; a unique name for the thing we are backing up
|
||||
(with-gensyms (backup)
|
||||
;; store the original value of the first def in backup
|
||||
`(let ((,backup ,(first defs)))
|
||||
;; backup any other things which need backing up
|
||||
(protect ,(cdr defs)
|
||||
;; execute the body
|
||||
,@body
|
||||
)
|
||||
;; restore the first thing
|
||||
(set! ,(first defs) ,backup)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro +! (place amount)
|
||||
`(set! ,place (+ ,place ,amount))
|
||||
)
|
||||
|
||||
;; todo, handle too many arguments correct
|
||||
(defmacro if (condition true-case &rest others)
|
||||
(if (null? others)
|
||||
`(cond (,condition ,true-case))
|
||||
`(cond (,condition ,true-case)
|
||||
(else ,(first others))
|
||||
)
|
||||
)
|
||||
)
|
|
@ -77,7 +77,7 @@
|
|||
(define-extern new-dynamic-structure (function kheap type int structure))
|
||||
(define-extern method-set! (function type int function none)) ;; may actually return function.
|
||||
(define-extern link (function pointer string int kheap int pointer))
|
||||
(define-extern dgo-load (function string kheap int int))
|
||||
(define-extern dgo-load (function string kheap int int none))
|
||||
(define-extern link-begin (function pointer string int kheap int int))
|
||||
(define-extern link-resume (function int))
|
||||
;; mc-run
|
||||
|
|
|
@ -18,7 +18,77 @@
|
|||
;; there is an optional "docstring" that can go at the beginning of a function
|
||||
"Function which returns its input. The first function of the game!"
|
||||
|
||||
;; the last thing in the function body is the return value.
|
||||
;; the last thing in the function body is the return value. This is like "return x;" in C
|
||||
;; the return type of the function is figured out automatically by the compiler
|
||||
;; you don't have to specify it manually.
|
||||
x
|
||||
)
|
||||
|
||||
|
||||
(defun 1/ ((x float))
|
||||
"Reciprocal floating point"
|
||||
|
||||
;; this function computes 1.0 / x. GOAL allows strange function names like "1/".
|
||||
|
||||
;; Declaring this an inline function is like a C inline function, however code is
|
||||
;; still generated so it can be used a function object. GOAL inline functions have type
|
||||
;; checking, so they are preferable to macros when possible, to get better error messages.
|
||||
(declare (inline))
|
||||
|
||||
;; the division form will pick the math type (float, int) based on the type of the first
|
||||
;; argument. In this case, "1." is a floating point constant, so this becomes a floating point division.
|
||||
(/ 1. x)
|
||||
)
|
||||
|
||||
(defun + ((x int) (y int))
|
||||
"Compute the sum of two integers"
|
||||
|
||||
;; this wraps the compiler's built-in handling of "add two integers" in a GOAL function.
|
||||
;; now "+" can be used as a function object, but is limited to adding two integers when used like this.
|
||||
;; The compiler is smart enough to not use this function unless "+" is being used as a function object.
|
||||
;; ex: (+ a b c), (+ a b) ; won't use this function, uses built-in addition
|
||||
;; (set-combination-function! my-thing +) ; + becomes a function pointer in this case
|
||||
(+ x y)
|
||||
)
|
||||
|
||||
(defun - ((x int) (y int))
|
||||
"Compute the difference of two integers"
|
||||
(- x y)
|
||||
)
|
||||
|
||||
(defun * ((x int) (y int))
|
||||
"Compute the product of two integers"
|
||||
;; TODO - verify that this matches the PS2 exactly.
|
||||
;; Uses mult (three operand form) in MIPS
|
||||
(* x y)
|
||||
)
|
||||
|
||||
(defun / ((x int) (y int))
|
||||
"Compute the quotient of two integers"
|
||||
;; TODO - verify this matches the PS2 exactly
|
||||
(/ x y)
|
||||
)
|
||||
|
||||
;; todo ash
|
||||
;; todo mod
|
||||
;; todo rem
|
||||
|
||||
(defun abs ((a int))
|
||||
"Take the absolute value of an integer"
|
||||
|
||||
;; short function, good candidate for inlining
|
||||
(declare (inline))
|
||||
|
||||
;; The original implementation was inline assembly, to take advantage of branch delay slots:
|
||||
;; (or v0 a0 r0) ;; move input to output unchanged, for positive case
|
||||
;; (bltzl v0 end) ;; if negative, execute the branch delay slot below...
|
||||
;; (dsubu v0 r0 v0) ;; negate
|
||||
;; (label end)
|
||||
|
||||
|
||||
(if (> a 0) ;; condition is "a > 0"
|
||||
a ;; true case, return a
|
||||
(- a) ;; false case, return -a. (- a) is like (- 0 a)
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
(build-game)
|
||||
;(define-extern global kheap)
|
||||
;(define-extern dgo-load (function string kheap int int int))
|
||||
;(dgo-load "kernel" global #xf #x200000) ; todo, remove once kernel loads itself.
|
||||
;(dgo-load "game" global #xf #x200000)
|
||||
(dgo-load "kernel" global #xf #x200000) ; todo, remove once kernel loads itself.
|
||||
(dgo-load "game" global #xf #x200000)
|
||||
1
|
13
goal_src/test/test-defsmacro-defgmacro.gc
Normal file
13
goal_src/test/test-defsmacro-defgmacro.gc
Normal file
|
@ -0,0 +1,13 @@
|
|||
(defsmacro test-macro (x)
|
||||
;; goos macro
|
||||
`(+ ,x 2)
|
||||
)
|
||||
|
||||
(defmacro test-macro (x)
|
||||
;; goal macro which calls a goos macro of the same name
|
||||
(let ((goos-expansion (test-macro x)))
|
||||
`(+ ,goos-expansion 3)
|
||||
)
|
||||
)
|
||||
|
||||
(test-macro 15)
|
10
goal_src/test/test-desfun.gc
Normal file
10
goal_src/test/test-desfun.gc
Normal file
|
@ -0,0 +1,10 @@
|
|||
(desfun square (x)
|
||||
(* x x)
|
||||
)
|
||||
|
||||
|
||||
(defmacro call-square (x)
|
||||
(square x)
|
||||
)
|
||||
|
||||
(call-square 2)
|
1
goal_src/test/test-div-1.gc
Normal file
1
goal_src/test/test-div-1.gc
Normal file
|
@ -0,0 +1 @@
|
|||
(+ 1 2 (/ (* 2 5) (+ 1 2)))
|
3
goal_src/test/test-div-2.gc
Normal file
3
goal_src/test/test-div-2.gc
Normal file
|
@ -0,0 +1,3 @@
|
|||
(let ((x 30))
|
||||
(+ (/ x 10) 4)
|
||||
)
|
11
goal_src/test/test-factorial-loop.gc
Normal file
11
goal_src/test/test-factorial-loop.gc
Normal file
|
@ -0,0 +1,11 @@
|
|||
(defun factorial-iterative ((x integer))
|
||||
(let ((result 1))
|
||||
(while (!= x 1)
|
||||
(set! result (* result x))
|
||||
(set! x (- x 1))
|
||||
)
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
(factorial-iterative 10)
|
12
goal_src/test/test-factorial-recursive.gc
Normal file
12
goal_src/test/test-factorial-recursive.gc
Normal file
|
@ -0,0 +1,12 @@
|
|||
;; for now, recursive functions need to forward declare so they have their
|
||||
;; return type.
|
||||
;(defun-extern factorial-recursive ((x integer)) integer)
|
||||
(define-extern factorial-recursive (function integer integer))
|
||||
|
||||
(defun factorial-recursive ((x integer))
|
||||
(cond ((= x 1) x)
|
||||
(else (* x (factorial-recursive (- x 1))))
|
||||
)
|
||||
)
|
||||
|
||||
(factorial-recursive 10)
|
7
goal_src/test/test-mlet.gc
Normal file
7
goal_src/test/test-mlet.gc
Normal file
|
@ -0,0 +1,7 @@
|
|||
(defglobalconstant c1 3)
|
||||
|
||||
(+ c1 (mlet ((c1 4))
|
||||
c1
|
||||
)
|
||||
c1
|
||||
)
|
15
goal_src/test/test-protect.gc
Normal file
15
goal_src/test/test-protect.gc
Normal file
|
@ -0,0 +1,15 @@
|
|||
(let ((var1 10)
|
||||
(var2 20)
|
||||
(sum 0)
|
||||
)
|
||||
|
||||
(protect (var1 var2)
|
||||
(set! var1 1)
|
||||
(set! var2 2)
|
||||
(+! sum var1)
|
||||
(+! sum var2)
|
||||
)
|
||||
(+! sum var1)
|
||||
(+! sum var2)
|
||||
sum
|
||||
)
|
18
goal_src/test/test-set-symbol.gc
Normal file
18
goal_src/test/test-set-symbol.gc
Normal file
|
@ -0,0 +1,18 @@
|
|||
|
||||
(defun burp ((thing integer))
|
||||
(* thing 3)
|
||||
)
|
||||
|
||||
(define thing 2)
|
||||
|
||||
(set! thing 3)
|
||||
|
||||
|
||||
(+ (let ((thing 4))
|
||||
(set! thing (+ thing 1))
|
||||
thing
|
||||
)
|
||||
(burp thing)
|
||||
(set! thing 4)
|
||||
thing
|
||||
)
|
3
goal_src/test/test-three-reg-add.gc
Normal file
3
goal_src/test/test-three-reg-add.gc
Normal file
|
@ -0,0 +1,3 @@
|
|||
(let ((x 3))
|
||||
(+ (+ x 1) x)
|
||||
)
|
6
goal_src/test/test-three-reg-mult.gc
Normal file
6
goal_src/test/test-three-reg-mult.gc
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
(let ((x 3))
|
||||
(* x 4)
|
||||
(* x)
|
||||
x
|
||||
)
|
5
goal_src/test/test-three-reg-sub.gc
Normal file
5
goal_src/test/test-three-reg-sub.gc
Normal file
|
@ -0,0 +1,5 @@
|
|||
(let ((x 3))
|
||||
(- x 1)
|
||||
(- x)
|
||||
x
|
||||
)
|
|
@ -21,6 +21,7 @@ add_library(compiler
|
|||
compiler/compilation/Math.cpp
|
||||
compiler/compilation/Define.cpp
|
||||
compiler/compilation/Function.cpp
|
||||
compiler/compilation/ControlFlow.cpp
|
||||
compiler/Util.cpp
|
||||
logger/Logger.cpp
|
||||
regalloc/IRegister.cpp
|
||||
|
|
|
@ -99,6 +99,7 @@ class Compiler {
|
|||
Val* number_to_float(Val* in, Env* env);
|
||||
Val* number_to_binteger(Val* in, Env* env);
|
||||
Val* to_math_type(Val* in, MathMode mode, Env* env);
|
||||
bool is_none(Val* in);
|
||||
|
||||
public:
|
||||
// Atoms
|
||||
|
@ -123,14 +124,22 @@ class Compiler {
|
|||
Val* compile_in_package(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_build_dgo(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
||||
// ControlFlow
|
||||
Condition compile_condition(const goos::Object& condition, Env* env, bool invert);
|
||||
Val* compile_condition_as_bool(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_when_goto(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_cond(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
||||
// Define
|
||||
Val* compile_define(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_define_extern(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_set(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
||||
// Macro
|
||||
Val* compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_quote(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_mlet(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
||||
// Math
|
||||
Val* compile_add(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
|
|
@ -31,7 +31,7 @@ void Env::constrain_reg(IRegConstraint constraint) {
|
|||
/*!
|
||||
* Lookup the given symbol object as a lexical variable.
|
||||
*/
|
||||
Val* Env::lexical_lookup(goos::Object sym) {
|
||||
RegVal* Env::lexical_lookup(goos::Object sym) {
|
||||
return m_parent->lexical_lookup(std::move(sym));
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ void GlobalEnv::constrain_reg(IRegConstraint constraint) {
|
|||
/*!
|
||||
* Lookup the given symbol object as a lexical variable.
|
||||
*/
|
||||
Val* GlobalEnv::lexical_lookup(goos::Object sym) {
|
||||
RegVal* GlobalEnv::lexical_lookup(goos::Object sym) {
|
||||
(void)sym;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -218,6 +218,15 @@ void FunctionEnv::resolve_gotos() {
|
|||
}
|
||||
gt.ir->resolve(&kv_label->second);
|
||||
}
|
||||
|
||||
for (auto& gt : unresolved_cond_gotos) {
|
||||
auto kv_label = m_labels.find(gt.label);
|
||||
if (kv_label == m_labels.end()) {
|
||||
throw std::runtime_error("invalid when-goto destination " + gt.label);
|
||||
}
|
||||
gt.ir->label = kv_label->second;
|
||||
gt.ir->mark_as_resolved();
|
||||
}
|
||||
}
|
||||
|
||||
RegVal* FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) {
|
||||
|
@ -238,7 +247,7 @@ std::unordered_map<std::string, Label>& LabelEnv::get_label_map() {
|
|||
return m_labels;
|
||||
}
|
||||
|
||||
Val* FunctionEnv::lexical_lookup(goos::Object sym) {
|
||||
RegVal* FunctionEnv::lexical_lookup(goos::Object sym) {
|
||||
if (!sym.is_symbol()) {
|
||||
throw std::runtime_error("invalid symbol in lexical_lookup");
|
||||
}
|
||||
|
@ -259,7 +268,7 @@ std::string LexicalEnv::print() {
|
|||
return "lexical";
|
||||
}
|
||||
|
||||
Val* LexicalEnv::lexical_lookup(goos::Object sym) {
|
||||
RegVal* LexicalEnv::lexical_lookup(goos::Object sym) {
|
||||
if (!sym.is_symbol()) {
|
||||
throw std::runtime_error("invalid symbol in lexical_lookup");
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class Env {
|
|||
virtual void emit(std::unique_ptr<IR> ir);
|
||||
virtual RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind);
|
||||
virtual void constrain_reg(IRegConstraint constraint); // todo, remove!
|
||||
virtual Val* lexical_lookup(goos::Object sym);
|
||||
virtual RegVal* lexical_lookup(goos::Object sym);
|
||||
virtual BlockEnv* find_block(const std::string& name);
|
||||
virtual std::unordered_map<std::string, Label>& get_label_map();
|
||||
RegVal* make_gpr(TypeSpec ts);
|
||||
|
@ -54,7 +54,7 @@ class GlobalEnv : public Env {
|
|||
void emit(std::unique_ptr<IR> ir) override;
|
||||
RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind) override;
|
||||
void constrain_reg(IRegConstraint constraint) override;
|
||||
Val* lexical_lookup(goos::Object sym) override;
|
||||
RegVal* lexical_lookup(goos::Object sym) override;
|
||||
BlockEnv* find_block(const std::string& name) override;
|
||||
~GlobalEnv() = default;
|
||||
|
||||
|
@ -128,7 +128,14 @@ class DeclareEnv : public Env {
|
|||
class IR_GotoLabel;
|
||||
|
||||
struct UnresolvedGoto {
|
||||
IR_GotoLabel* ir;
|
||||
IR_GotoLabel* ir = nullptr;
|
||||
std::string label;
|
||||
};
|
||||
|
||||
class IR_ConditionalBranch;
|
||||
|
||||
struct UnresolvedConditionalGoto {
|
||||
IR_ConditionalBranch* ir = nullptr;
|
||||
std::string label;
|
||||
};
|
||||
|
||||
|
@ -146,13 +153,18 @@ class FunctionEnv : public DeclareEnv {
|
|||
const std::vector<IRegConstraint>& constraints() { return m_constraints; }
|
||||
void constrain(const IRegConstraint& c) { m_constraints.push_back(c); }
|
||||
void set_allocations(const AllocationResult& result) { m_regalloc_result = result; }
|
||||
Val* lexical_lookup(goos::Object sym) override;
|
||||
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();
|
||||
}
|
||||
|
||||
int idx_in_file = -1;
|
||||
|
||||
template <typename T, class... Args>
|
||||
|
@ -173,7 +185,8 @@ class FunctionEnv : public DeclareEnv {
|
|||
std::string method_of_type_name = "#f";
|
||||
bool is_asm_func = false;
|
||||
std::vector<UnresolvedGoto> unresolved_gotos;
|
||||
std::unordered_map<std::string, Val*> params;
|
||||
std::vector<UnresolvedConditionalGoto> unresolved_cond_gotos;
|
||||
std::unordered_map<std::string, RegVal*> params;
|
||||
|
||||
protected:
|
||||
void resolve_gotos();
|
||||
|
@ -189,6 +202,7 @@ class FunctionEnv : public DeclareEnv {
|
|||
bool m_aligned_stack_required = false;
|
||||
|
||||
std::unordered_map<std::string, Label> m_labels;
|
||||
std::vector<std::unique_ptr<Label>> m_unnamed_labels;
|
||||
};
|
||||
|
||||
class BlockEnv : public Env {
|
||||
|
@ -206,9 +220,9 @@ class BlockEnv : public Env {
|
|||
class LexicalEnv : public DeclareEnv {
|
||||
public:
|
||||
explicit LexicalEnv(Env* parent) : DeclareEnv(parent) {}
|
||||
Val* lexical_lookup(goos::Object sym) override;
|
||||
RegVal* lexical_lookup(goos::Object sym) override;
|
||||
std::string print() override;
|
||||
std::unordered_map<std::string, Val*> vars;
|
||||
std::unordered_map<std::string, RegVal*> vars;
|
||||
};
|
||||
|
||||
class LabelEnv : public Env {
|
||||
|
@ -232,6 +246,7 @@ class SymbolMacroEnv : public Env {
|
|||
public:
|
||||
explicit SymbolMacroEnv(Env* parent) : Env(parent) {}
|
||||
std::unordered_map<std::shared_ptr<goos::SymbolObject>, goos::Object> macros;
|
||||
std::string print() override { return "symbol-macro-env"; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -77,7 +77,26 @@ void IR_LoadConstant64::do_codegen(emitter::ObjectGenerator* gen,
|
|||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) {
|
||||
auto dest_reg = get_reg(m_dest, allocs, irec);
|
||||
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, m_value), 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
|
@ -198,6 +217,8 @@ void IR_RegSet::do_codegen(emitter::ObjectGenerator* gen,
|
|||
gen->add_instr(IGen::mov_gpr64_gpr64(dest_reg, val_reg), irec);
|
||||
} else if (val_reg.is_xmm() && dest_reg.is_gpr()) {
|
||||
gen->add_instr(IGen::movd_gpr32_xmm32(dest_reg, val_reg), irec);
|
||||
} else if (val_reg.is_gpr() && dest_reg.is_xmm()) {
|
||||
gen->add_instr(IGen::movd_xmm32_gpr32(dest_reg, val_reg), irec);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
@ -371,6 +392,8 @@ std::string IR_IntegerMath::print() {
|
|||
return fmt::format("subi {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::IMUL_32:
|
||||
return fmt::format("imul {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::IDIV_32:
|
||||
return fmt::format("idiv {}, {}", m_dest->print(), m_arg->print());
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -381,6 +404,10 @@ RegAllocInstr IR_IntegerMath::to_rai() {
|
|||
rai.write.push_back(m_dest->ireg());
|
||||
rai.read.push_back(m_dest->ireg());
|
||||
rai.read.push_back(m_arg->ireg());
|
||||
|
||||
if (m_kind == IntegerMathKind::IDIV_32) {
|
||||
rai.exclude.emplace_back(emitter::RDX);
|
||||
}
|
||||
return rai;
|
||||
}
|
||||
|
||||
|
@ -400,9 +427,50 @@ void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen,
|
|||
auto dr = get_reg(m_dest, allocs, irec);
|
||||
gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec);
|
||||
gen->add_instr(IGen::movsx_r64_r32(dr, dr), irec);
|
||||
}
|
||||
} break;
|
||||
|
||||
break;
|
||||
case IntegerMathKind::IDIV_32: {
|
||||
gen->add_instr(IGen::cdq(), irec);
|
||||
gen->add_instr(IGen::idiv_gpr32(get_reg(m_arg, allocs, irec)), irec);
|
||||
gen->add_instr(IGen::movsx_r64_r32(get_reg(m_dest, allocs, irec), emitter::RAX), irec);
|
||||
} break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// FloatMath
|
||||
/////////////////////
|
||||
|
||||
IR_FloatMath::IR_FloatMath(FloatMathKind kind, RegVal* dest, RegVal* arg)
|
||||
: m_kind(kind), m_dest(dest), m_arg(arg) {}
|
||||
|
||||
std::string IR_FloatMath::print() {
|
||||
switch (m_kind) {
|
||||
case FloatMathKind::DIV_SS:
|
||||
return fmt::format("divss {}, {}", m_dest->print(), m_arg->print());
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
RegAllocInstr IR_FloatMath::to_rai() {
|
||||
RegAllocInstr rai;
|
||||
rai.write.push_back(m_dest->ireg());
|
||||
rai.read.push_back(m_dest->ireg());
|
||||
rai.read.push_back(m_arg->ireg());
|
||||
return rai;
|
||||
}
|
||||
|
||||
void IR_FloatMath::do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) {
|
||||
switch (m_kind) {
|
||||
case FloatMathKind::DIV_SS:
|
||||
gen->add_instr(
|
||||
IGen::divss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -441,4 +509,106 @@ void IR_StaticVarLoad::do_codegen(emitter::ObjectGenerator* gen,
|
|||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// ConditionalBranch
|
||||
/////////////////////
|
||||
|
||||
std::string Condition::print() const {
|
||||
switch (kind) {
|
||||
case ConditionKind::NOT_EQUAL:
|
||||
return a->print() + " != " + b->print();
|
||||
case ConditionKind::EQUAL:
|
||||
return a->print() + " == " + b->print();
|
||||
case ConditionKind::LEQ:
|
||||
return a->print() + " <= " + b->print();
|
||||
case ConditionKind::GEQ:
|
||||
return a->print() + " >= " + b->print();
|
||||
case ConditionKind::LT:
|
||||
return a->print() + " < " + b->print();
|
||||
case ConditionKind::GT:
|
||||
return a->print() + " > " + b->print();
|
||||
default:
|
||||
throw std::runtime_error("unknown condition type in GoalCondition::print()");
|
||||
}
|
||||
}
|
||||
|
||||
RegAllocInstr Condition::to_rai() {
|
||||
RegAllocInstr rai;
|
||||
rai.read.push_back(a->ireg());
|
||||
rai.read.push_back(b->ireg());
|
||||
return rai;
|
||||
}
|
||||
|
||||
IR_ConditionalBranch::IR_ConditionalBranch(const Condition& _condition, Label _label)
|
||||
: condition(_condition), label(_label) {}
|
||||
|
||||
std::string IR_ConditionalBranch::print() {
|
||||
// todo, float/signed info?
|
||||
return fmt::format("j({}) {}", condition.print(), label.print());
|
||||
}
|
||||
|
||||
RegAllocInstr IR_ConditionalBranch::to_rai() {
|
||||
auto rai = condition.to_rai();
|
||||
assert(m_resolved);
|
||||
rai.jumps.push_back(label.idx);
|
||||
return rai;
|
||||
}
|
||||
|
||||
void IR_ConditionalBranch::do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) {
|
||||
Instruction jump_instr(0);
|
||||
assert(m_resolved);
|
||||
switch (condition.kind) {
|
||||
case ConditionKind::EQUAL:
|
||||
jump_instr = IGen::je_32();
|
||||
break;
|
||||
case ConditionKind::NOT_EQUAL:
|
||||
jump_instr = IGen::jne_32();
|
||||
break;
|
||||
case ConditionKind::LEQ:
|
||||
if (condition.is_signed) {
|
||||
jump_instr = IGen::jle_32();
|
||||
} else {
|
||||
jump_instr = IGen::jbe_32();
|
||||
}
|
||||
break;
|
||||
case ConditionKind::GEQ:
|
||||
if (condition.is_signed) {
|
||||
jump_instr = IGen::jge_32();
|
||||
} else {
|
||||
jump_instr = IGen::jae_32();
|
||||
}
|
||||
break;
|
||||
|
||||
case ConditionKind::LT:
|
||||
if (condition.is_signed) {
|
||||
jump_instr = IGen::jl_32();
|
||||
} else {
|
||||
jump_instr = IGen::jb_32();
|
||||
}
|
||||
break;
|
||||
case ConditionKind::GT:
|
||||
if (condition.is_signed) {
|
||||
jump_instr = IGen::jg_32();
|
||||
} else {
|
||||
jump_instr = IGen::ja_32();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (condition.is_float) {
|
||||
assert(false); // for now
|
||||
} else {
|
||||
gen->add_instr(IGen::cmp_gpr64_gpr64(get_reg(condition.a, allocs, irec),
|
||||
get_reg(condition.b, allocs, irec)),
|
||||
irec);
|
||||
}
|
||||
|
||||
auto jump_rec = gen->add_instr(jump_instr, irec);
|
||||
gen->link_instruction_jump(jump_rec, gen->get_future_ir_record_in_same_func(irec, label.idx));
|
||||
}
|
|
@ -239,4 +239,33 @@ class IR_FloatMath : public IR {
|
|||
RegVal* m_arg;
|
||||
};
|
||||
|
||||
enum class ConditionKind { NOT_EQUAL, EQUAL, LEQ, LT, GT, GEQ, INVALID_CONDITION };
|
||||
|
||||
struct Condition {
|
||||
ConditionKind kind = ConditionKind::INVALID_CONDITION;
|
||||
RegVal* a = nullptr;
|
||||
RegVal* b = nullptr;
|
||||
bool is_signed = false;
|
||||
bool is_float = false;
|
||||
RegAllocInstr to_rai();
|
||||
std::string print() const;
|
||||
};
|
||||
|
||||
class IR_ConditionalBranch : public IR {
|
||||
public:
|
||||
IR_ConditionalBranch(const Condition& condition, Label _label);
|
||||
std::string print() override;
|
||||
RegAllocInstr to_rai() override;
|
||||
void do_codegen(emitter::ObjectGenerator* gen,
|
||||
const AllocationResult& allocs,
|
||||
emitter::IR_Record irec) override;
|
||||
void mark_as_resolved() { m_resolved = true; }
|
||||
|
||||
Condition condition;
|
||||
Label label;
|
||||
|
||||
private:
|
||||
bool m_resolved = false;
|
||||
};
|
||||
|
||||
#endif // JAK_IR_H
|
||||
|
|
|
@ -167,4 +167,8 @@ emitter::RegKind Compiler::get_preferred_reg_kind(const TypeSpec& ts) {
|
|||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Compiler::is_none(Val* in) {
|
||||
return dynamic_cast<None*>(in);
|
||||
}
|
|
@ -21,8 +21,12 @@ RegVal* Val::to_gpr(Env* fe) {
|
|||
* Fallback to_xmm if a more optimized one is not provided.
|
||||
*/
|
||||
RegVal* Val::to_xmm(Env* fe) {
|
||||
(void)fe;
|
||||
throw std::runtime_error("Val::to_xmm NYI"); // todo
|
||||
auto rv = to_reg(fe);
|
||||
if (rv->ireg().kind == emitter::RegKind::XMM) {
|
||||
return rv;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
RegVal* RegVal::to_reg(Env* fe) {
|
||||
|
@ -35,7 +39,9 @@ RegVal* RegVal::to_gpr(Env* fe) {
|
|||
if (m_ireg.kind == emitter::RegKind::GPR) {
|
||||
return this;
|
||||
} else {
|
||||
throw std::runtime_error("RegVal::to_gpr NYI"); // todo
|
||||
auto re = fe->make_gpr(m_ts);
|
||||
fe->emit(std::make_unique<IR_RegSet>(re, this));
|
||||
return re;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +50,9 @@ RegVal* RegVal::to_xmm(Env* fe) {
|
|||
if (m_ireg.kind == emitter::RegKind::XMM) {
|
||||
return this;
|
||||
} else {
|
||||
throw std::runtime_error("RegVal::to_xmm NYI"); // todo
|
||||
auto re = fe->make_xmm(m_ts);
|
||||
fe->emit(std::make_unique<IR_RegSet>(re, this));
|
||||
return re;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,13 +45,13 @@ static const std::unordered_map<
|
|||
{"seval", &Compiler::compile_seval},
|
||||
//
|
||||
// // CONTROL FLOW
|
||||
// {"cond", &Compiler::compile_cond},
|
||||
// {"when-goto", &Compiler::compile_when_goto},
|
||||
{"cond", &Compiler::compile_cond},
|
||||
{"when-goto", &Compiler::compile_when_goto},
|
||||
//
|
||||
// // DEFINITION
|
||||
{"define", &Compiler::compile_define},
|
||||
{"define-extern", &Compiler::compile_define_extern},
|
||||
// {"set!", &Compiler::compile_set},
|
||||
{"set!", &Compiler::compile_set},
|
||||
// {"defun-extern", &Compiler::compile_defun_extern},
|
||||
// {"declare-method", &Compiler::compile_declare_method},
|
||||
//
|
||||
|
@ -72,7 +72,7 @@ static const std::unordered_map<
|
|||
{"inline", &Compiler::compile_inline},
|
||||
// {"with-inline", &Compiler::compile_with_inline},
|
||||
// {"rlet", &Compiler::compile_rlet},
|
||||
// {"mlet", &Compiler::compile_mlet},
|
||||
|
||||
// {"get-ra-ptr", &Compiler::compile_get_ra_ptr},
|
||||
//
|
||||
//
|
||||
|
@ -80,6 +80,7 @@ static const std::unordered_map<
|
|||
// // MACRO
|
||||
// {"print-type", &Compiler::compile_print_type},
|
||||
{"quote", &Compiler::compile_quote},
|
||||
{"mlet", &Compiler::compile_mlet},
|
||||
// {"defconstant", &Compiler::compile_defconstant},
|
||||
//
|
||||
// // OBJECT
|
||||
|
@ -113,14 +114,14 @@ static const std::unordered_map<
|
|||
// {"logxor", &Compiler::compile_logxor},
|
||||
// {"logand", &Compiler::compile_logand},
|
||||
// {"lognot", &Compiler::compile_lognot},
|
||||
// {"=", &Compiler::compile_condition_as_bool},
|
||||
// {"!=", &Compiler::compile_condition_as_bool},
|
||||
// {"eq?", &Compiler::compile_condition_as_bool},
|
||||
// {"not", &Compiler::compile_condition_as_bool},
|
||||
// {"<=", &Compiler::compile_condition_as_bool},
|
||||
// {">=", &Compiler::compile_condition_as_bool},
|
||||
// {"<", &Compiler::compile_condition_as_bool},
|
||||
// {">", &Compiler::compile_condition_as_bool},
|
||||
{"=", &Compiler::compile_condition_as_bool},
|
||||
{"!=", &Compiler::compile_condition_as_bool},
|
||||
{"eq?", &Compiler::compile_condition_as_bool},
|
||||
{"not", &Compiler::compile_condition_as_bool},
|
||||
{"<=", &Compiler::compile_condition_as_bool},
|
||||
{">=", &Compiler::compile_condition_as_bool},
|
||||
{"<", &Compiler::compile_condition_as_bool},
|
||||
{">", &Compiler::compile_condition_as_bool},
|
||||
//
|
||||
// // BUILDER (build-dgo/build-cgo?)
|
||||
{"build-dgos", &Compiler::compile_build_dgo},
|
||||
|
@ -202,7 +203,14 @@ Val* Compiler::compile_symbol(const goos::Object& form, Env* env) {
|
|||
return get_none();
|
||||
}
|
||||
|
||||
// todo mlet
|
||||
auto mlet_env = get_parent_env_of_type<SymbolMacroEnv>(env);
|
||||
while (mlet_env) {
|
||||
auto mlkv = mlet_env->macros.find(form.as_symbol());
|
||||
if (mlkv != mlet_env->macros.end()) {
|
||||
return compile_error_guard(mlkv->second, env);
|
||||
}
|
||||
mlet_env = get_parent_env_of_type<SymbolMacroEnv>(mlet_env->parent());
|
||||
}
|
||||
|
||||
auto lexical = env->lexical_lookup(form);
|
||||
if (lexical) {
|
||||
|
|
225
goalc/compiler/compilation/ControlFlow.cpp
Normal file
225
goalc/compiler/compilation/ControlFlow.cpp
Normal file
|
@ -0,0 +1,225 @@
|
|||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
/*!
|
||||
* Convert a condition expression into a GoalCondition for use in a conditional branch.
|
||||
* The reason for this design is to allow an optimization for
|
||||
* (if (< a b) ...) to be compiled without actually computing a true/false value for the (< a b)
|
||||
* expression. Instead, it will generate a cmp + jle sequence of instructions, which is much faster.
|
||||
*
|
||||
* This can be applied to _any_ GOAL form, and will return a GoalCondition which can be used with a
|
||||
* Branch IR to branch if the condition is true/false. When possible it applies the optimization
|
||||
* mentioned above, but will be fine in other cases too. I believe the original GOAL compiler had a
|
||||
* similar system.
|
||||
*
|
||||
* Will branch if the condition is true and the invert flag is false.
|
||||
* Will branch if the condition is false and the invert flag is true.
|
||||
*/
|
||||
Condition Compiler::compile_condition(const goos::Object& condition, Env* env, bool invert) {
|
||||
Condition gc;
|
||||
|
||||
// These are special conditions that can be optimized into a cmp + jxx instruction.
|
||||
const std::unordered_map<std::string, ConditionKind> conditions_inverted = {
|
||||
{"!=", ConditionKind::EQUAL}, {"eq?", ConditionKind::NOT_EQUAL},
|
||||
{"neq?", ConditionKind::EQUAL}, {"=", ConditionKind::NOT_EQUAL},
|
||||
{">", ConditionKind::LEQ}, {"<", ConditionKind::GEQ},
|
||||
{">=", ConditionKind::LT}, {"<=", ConditionKind::GT}};
|
||||
|
||||
const std::unordered_map<std::string, ConditionKind> conditions_normal = {
|
||||
{"!=", ConditionKind::NOT_EQUAL}, {"eq?", ConditionKind::EQUAL},
|
||||
{"neq?", ConditionKind::NOT_EQUAL}, {"=", ConditionKind::EQUAL},
|
||||
{">", ConditionKind::GT}, {"<", ConditionKind::LT},
|
||||
{">=", ConditionKind::GEQ}, {"<=", ConditionKind::LEQ}};
|
||||
|
||||
// possibly a form with an optimizable condition?
|
||||
if (condition.is_pair()) {
|
||||
auto first = pair_car(condition);
|
||||
auto rest = pair_cdr(condition);
|
||||
|
||||
if (first.is_symbol()) {
|
||||
auto fas = first.as_symbol();
|
||||
|
||||
// if there's a not, we can just try again to get an optimization with the invert flipped.
|
||||
if (fas->name == "not") {
|
||||
auto arg = pair_car(rest);
|
||||
if (!pair_cdr(rest).is_empty_list()) {
|
||||
throw_compile_error(condition, "A condition with \"not\" can have only one argument");
|
||||
}
|
||||
return compile_condition(arg, env, !invert);
|
||||
}
|
||||
|
||||
auto& conditions = invert ? conditions_inverted : conditions_normal;
|
||||
auto nc_kv = conditions.find(fas->name);
|
||||
|
||||
if (nc_kv != conditions.end()) {
|
||||
// it is an optimizable condition!
|
||||
gc.kind = nc_kv->second;
|
||||
|
||||
// get args...
|
||||
auto args = get_va(rest, rest);
|
||||
va_check(rest, args, {{}, {}}, {});
|
||||
auto first_arg = compile_error_guard(args.unnamed.at(0), env);
|
||||
auto second_arg = compile_error_guard(args.unnamed.at(1), env);
|
||||
|
||||
if (is_number(first_arg->type())) {
|
||||
// it's a numeric comparison, so we may need to coerce.
|
||||
auto math_mode = get_math_mode(first_arg->type());
|
||||
|
||||
// there is no support for comparing bintegers, so we turn the binteger comparison into an
|
||||
// integer.
|
||||
if (is_binteger(first_arg->type())) {
|
||||
first_arg = number_to_integer(first_arg, env);
|
||||
}
|
||||
|
||||
// convert second one to appropriate type as needed
|
||||
if (is_number(second_arg->type())) {
|
||||
second_arg = to_math_type(second_arg, math_mode, env);
|
||||
}
|
||||
}
|
||||
|
||||
// use signed comparison only if first argument is a signed integer (or coerced binteger)
|
||||
// (floating point ignores this)
|
||||
gc.is_signed = is_singed_integer_or_binteger(first_arg->type());
|
||||
|
||||
// pick between a floating point and an integer comparison.
|
||||
if (is_float(first_arg->type())) {
|
||||
gc.a = first_arg->to_xmm(env);
|
||||
gc.b = second_arg->to_xmm(env);
|
||||
gc.is_float = true;
|
||||
} else {
|
||||
gc.a = first_arg->to_gpr(env);
|
||||
gc.b = second_arg->to_gpr(env);
|
||||
}
|
||||
|
||||
return gc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not something we can process more. Just check if we get false.
|
||||
// todo - it's possible to optimize a false comparison because the false offset is zero
|
||||
gc.kind = invert ? ConditionKind::EQUAL : ConditionKind::NOT_EQUAL;
|
||||
gc.a = compile_error_guard(condition, env)->to_gpr(env);
|
||||
gc.b = compile_get_sym_obj("#f", env)->to_gpr(env);
|
||||
|
||||
return gc;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_condition_as_bool(const goos::Object& form,
|
||||
const goos::Object& rest,
|
||||
Env* env) {
|
||||
(void)rest;
|
||||
auto c = compile_condition(form, env, true);
|
||||
auto result = compile_get_sym_obj("#f", env)->to_gpr(env); // todo - can be optimized.
|
||||
Label label(get_parent_env_of_type<FunctionEnv>(env), -5);
|
||||
auto branch_ir = std::make_unique<IR_ConditionalBranch>(c, label);
|
||||
auto branch_ir_ref = branch_ir.get();
|
||||
env->emit(std::move(branch_ir));
|
||||
|
||||
// move true
|
||||
env->emit(std::make_unique<IR_RegSet>(
|
||||
result, compile_get_sym_obj("#t", env)->to_gpr(env))); // todo, can be optimized
|
||||
branch_ir_ref->label.idx = branch_ir_ref->label.func->code().size();
|
||||
branch_ir_ref->mark_as_resolved();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_when_goto(const goos::Object& form, const goos::Object& _rest, Env* env) {
|
||||
(void)form;
|
||||
auto* rest = &_rest;
|
||||
auto condition_code = pair_car(*rest);
|
||||
rest = &pair_cdr(*rest);
|
||||
|
||||
auto label = symbol_string(pair_car(*rest));
|
||||
expect_empty_list(pair_cdr(*rest));
|
||||
|
||||
// compile as condition (will set flags register with a cmp instruction)
|
||||
auto condition = compile_condition(condition_code, env, false);
|
||||
auto branch = std::make_unique<IR_ConditionalBranch>(condition, Label());
|
||||
get_parent_env_of_type<FunctionEnv>(env)->unresolved_cond_gotos.push_back({branch.get(), label});
|
||||
env->emit(std::move(branch));
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_cond(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto result = env->make_gpr(m_ts.make_typespec("object"));
|
||||
|
||||
auto fenv = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto end_label = fenv->alloc_unnamed_label();
|
||||
end_label->func = fenv;
|
||||
end_label->idx = -3; // placeholder
|
||||
|
||||
bool got_else = false;
|
||||
|
||||
std::vector<TypeSpec> case_result_types;
|
||||
|
||||
for_each_in_list(rest, [&](const goos::Object& o) {
|
||||
auto test = pair_car(o);
|
||||
auto clauses = pair_cdr(o);
|
||||
|
||||
if (got_else) {
|
||||
throw_compile_error(form, "cannot have anything after an else in a cond");
|
||||
}
|
||||
|
||||
if (test.is_symbol() && symbol_string(test) == "else") {
|
||||
got_else = true;
|
||||
}
|
||||
|
||||
if (got_else) {
|
||||
// just set the output to this.
|
||||
Val* case_result = get_none();
|
||||
for_each_in_list(clauses, [&](const goos::Object& clause) {
|
||||
case_result = compile_error_guard(clause, env);
|
||||
});
|
||||
|
||||
case_result_types.push_back(case_result->type());
|
||||
|
||||
// optimization - if we get junk, don't bother moving it, just leave junk in return.
|
||||
if (!is_none(case_result)) {
|
||||
env->emit(std::make_unique<IR_RegSet>(result, case_result->to_gpr(env)));
|
||||
}
|
||||
|
||||
} else {
|
||||
// CONDITION CHECK
|
||||
auto condition = compile_condition(test, env, true);
|
||||
|
||||
// BRANCH FWD
|
||||
auto branch_ir = std::make_unique<IR_ConditionalBranch>(condition, Label());
|
||||
auto branch_ir_ref = branch_ir.get();
|
||||
branch_ir->mark_as_resolved();
|
||||
env->emit(std::move(branch_ir));
|
||||
|
||||
// CODE
|
||||
Val* case_result = get_none();
|
||||
for_each_in_list(clauses, [&](const goos::Object& clause) {
|
||||
case_result = compile_error_guard(clause, env);
|
||||
});
|
||||
|
||||
case_result_types.push_back(case_result->type());
|
||||
if (!is_none(case_result)) {
|
||||
env->emit(std::make_unique<IR_RegSet>(result, case_result->to_gpr(env)));
|
||||
}
|
||||
|
||||
// GO TO END
|
||||
auto ir_goto_end = std::make_unique<IR_GotoLabel>(end_label);
|
||||
env->emit(std::move(ir_goto_end));
|
||||
|
||||
// PATCH BRANCH FWD
|
||||
branch_ir_ref->label.idx = fenv->code().size();
|
||||
}
|
||||
});
|
||||
|
||||
if (!got_else) {
|
||||
// if no else, clause, return #f. But don't retype. I don't know how I feel about this typing
|
||||
// setup.
|
||||
auto get_false = std::make_unique<IR_LoadSymbolPointer>(result, "#f");
|
||||
env->emit(std::move(get_false));
|
||||
}
|
||||
|
||||
result->set_type(m_ts.lowest_common_ancestor(case_result_types));
|
||||
|
||||
// PATCH END
|
||||
end_label->idx = fenv->code().size();
|
||||
|
||||
return result;
|
||||
}
|
|
@ -60,4 +60,43 @@ Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Objec
|
|||
}
|
||||
m_symbol_types[symbol_string(sym)] = new_type;
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_set(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
|
||||
auto& destination = args.unnamed.at(0);
|
||||
auto source = compile_error_guard(args.unnamed.at(1), env)->to_reg(env);
|
||||
|
||||
if (destination.is_symbol()) {
|
||||
// destination is just a symbol, so it's either a lexical variable or a global.
|
||||
|
||||
// first, attempt a lexical set:
|
||||
auto lex_place = env->lexical_lookup(destination);
|
||||
if (lex_place) {
|
||||
// typecheck and set!
|
||||
typecheck(form, lex_place->type(), source->type(), "set! lexical variable");
|
||||
env->emit(std::make_unique<IR_RegSet>(lex_place, source));
|
||||
return source;
|
||||
} else {
|
||||
// try to set symbol
|
||||
auto existing = m_symbol_types.find(destination.as_symbol()->name);
|
||||
if (existing == m_symbol_types.end()) {
|
||||
throw_compile_error(
|
||||
form, "could not find something called " + symbol_string(destination) + " to set!");
|
||||
} else {
|
||||
typecheck(form, existing->second, source->type(), "set! global symbol");
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto sym_val =
|
||||
fe->alloc_val<SymbolVal>(symbol_string(destination), m_ts.make_typespec("symbol"));
|
||||
auto result_in_gpr = source->to_gpr(env);
|
||||
env->emit(std::make_unique<IR_SetSymbolValue>(sym_val, result_in_gpr));
|
||||
return result_in_gpr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw_compile_error(form, "Set not implemented for this yet");
|
||||
}
|
||||
assert(false);
|
||||
}
|
|
@ -115,4 +115,22 @@ Val* Compiler::compile_defglobalconstant(const goos::Object& form,
|
|||
m_goos.global_environment.as_env()->vars[sym] = value;
|
||||
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_mlet(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto defs = pair_car(rest);
|
||||
auto body = pair_cdr(rest);
|
||||
|
||||
auto fenv = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto menv = fenv->alloc_env<SymbolMacroEnv>(env);
|
||||
|
||||
for_each_in_list(defs, [&](const goos::Object& o) {
|
||||
auto def_args = get_va(form, o);
|
||||
va_check(form, def_args, {goos::ObjectType::SYMBOL, {}}, {});
|
||||
menv->macros[def_args.unnamed.at(0).as_symbol()] = def_args.unnamed.at(1);
|
||||
});
|
||||
|
||||
Val* result = get_none();
|
||||
for_each_in_list(body, [&](const goos::Object& o) { result = compile_error_guard(o, menv); });
|
||||
return result;
|
||||
}
|
|
@ -48,7 +48,7 @@ Val* Compiler::number_to_integer(Val* in, Env* env) {
|
|||
} else if (is_integer(ts)) {
|
||||
return in;
|
||||
} else {
|
||||
assert(false);
|
||||
throw std::runtime_error("Can't convert " + in->print() + " to an integer.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,12 +215,31 @@ Val* Compiler::compile_div(const goos::Object& form, const goos::Object& rest, E
|
|||
auto first_type = first_val->type();
|
||||
auto math_type = get_math_mode(first_type);
|
||||
switch (math_type) {
|
||||
case MATH_FLOAT:
|
||||
case MATH_INT: {
|
||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||
auto first_thing = first_val->to_gpr(env);
|
||||
auto result = env->make_ireg(first_type, emitter::RegKind::GPR);
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first_thing));
|
||||
|
||||
{
|
||||
IRegConstraint result_rax_constraint;
|
||||
result_rax_constraint.instr_idx = fe->code().size() - 1;
|
||||
result_rax_constraint.ireg = result->ireg();
|
||||
result_rax_constraint.desired_register = emitter::RAX;
|
||||
fe->constrain(result_rax_constraint);
|
||||
|
||||
env->emit(std::make_unique<IR_IntegerMath>(
|
||||
IntegerMathKind::IDIV_32, result,
|
||||
to_math_type(compile_error_guard(args.unnamed.at(1), env), math_type, env)->to_gpr(env)));
|
||||
return result;
|
||||
}
|
||||
|
||||
case MATH_FLOAT: {
|
||||
auto result = env->make_xmm(first_type);
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first_val->to_xmm(env)));
|
||||
env->emit(std::make_unique<IR_FloatMath>(FloatMathKind::DIV_SS, result, to_math_type(compile_error_guard(args.unnamed.at(1), env), math_type, env)->to_xmm(env))));
|
||||
env->emit(std::make_unique<IR_FloatMath>(
|
||||
FloatMathKind::DIV_SS, result,
|
||||
to_math_type(compile_error_guard(args.unnamed.at(1), env), math_type, env)->to_xmm(env)));
|
||||
return result;
|
||||
}
|
||||
|
||||
case MATH_INVALID:
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
int main(int argc, char** argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
printf("goal compiler\n");
|
||||
printf("GOAL Compiler\n");
|
||||
|
||||
Compiler compiler;
|
||||
compiler.execute_repl();
|
||||
|
|
|
@ -225,6 +225,11 @@ TEST(CompilerAndRuntime, CompilerTests) {
|
|||
runner.run_test("test-sub-1.gc", {"4\n"});
|
||||
runner.run_test("test-sub-2.gc", {"4\n"});
|
||||
runner.run_test("test-mul-1.gc", {"-12\n"});
|
||||
runner.run_test("test-three-reg-add.gc", {"7\n"});
|
||||
runner.run_test("test-three-reg-sub.gc", {"3\n"});
|
||||
runner.run_test("test-three-reg-mult.gc", {"3\n"});
|
||||
runner.run_test("test-div-1.gc", {"6\n"});
|
||||
runner.run_test("test-div-2.gc", {"7\n"});
|
||||
|
||||
expected = "test-string";
|
||||
runner.run_test("test-string-symbol.gc", {expected}, expected.size());
|
||||
|
@ -234,6 +239,15 @@ TEST(CompilerAndRuntime, CompilerTests) {
|
|||
// float
|
||||
runner.run_test("test-floating-point-1.gc", {"1067316150\n"});
|
||||
|
||||
runner.run_test("test-mlet.gc", {"10\n"});
|
||||
runner.run_test("test-set-symbol.gc", {"22\n"});
|
||||
runner.run_test("test-defsmacro-defgmacro.gc", {"20\n"});
|
||||
runner.run_test("test-desfun.gc", {"4\n"});
|
||||
|
||||
runner.run_test("test-factorial-recursive.gc", {"3628800\n"});
|
||||
runner.run_test("test-factorial-loop.gc", {"3628800\n"});
|
||||
runner.run_test("test-protect.gc", {"33\n"});
|
||||
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
runner.print_summary();
|
||||
|
|
Loading…
Reference in a new issue