mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-27 08:20:47 +00:00
add more math
This commit is contained in:
parent
f972cdcab5
commit
a47620fd6d
@ -45,6 +45,12 @@
|
||||
`(:exit)
|
||||
)
|
||||
|
||||
(defmacro db ()
|
||||
`(begin
|
||||
(set-config! print-ir #t)
|
||||
(set-config! print-regalloc #t)
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; CONDITIONAL COMPILATION
|
||||
|
@ -69,9 +69,51 @@
|
||||
(/ x y)
|
||||
)
|
||||
|
||||
;; todo ash
|
||||
;; todo mod
|
||||
;; todo rem
|
||||
(defun ash ((value integer) (shift-amount integer))
|
||||
"Arithmetic shift value by shift-amount.
|
||||
A positive shift-amount will shift to the left and a negative will shift to the right.
|
||||
"
|
||||
|
||||
;; currently the compiler does not support "ash", so this function is also used to implement "ash".
|
||||
;; in the future, the compiler should be able to use constant propagation to turn constant shifts
|
||||
;; into x86 constant shifts when possible (which are faster). The GOAL compiler seems to do this.
|
||||
|
||||
;; The original implementation was inline assembly, to take advantage of branch delay slots:
|
||||
;; (or v1 a0 r0) ;; likely inserted by register coloring, not entirely needed
|
||||
;; (bgezl a1 end) ;; branch to function end if positive shift (left)...
|
||||
;; (dsllv v0 v1 a1) ;; do left shift in delay slot
|
||||
;;
|
||||
;; (dsubu a0 r0 a1) ;; negative shift amount for right shift
|
||||
;; (dsrav v0 v1 a0) ;; do right shift
|
||||
;; (label end)
|
||||
|
||||
(declare (inline))
|
||||
(if (> shift-amount 0)
|
||||
;; these correspond to x86-64 variable shift instructions.
|
||||
;; the exact behavior of GOAL shifts (signed/unsigned) are unknown so for now shifts must
|
||||
;; be manually specified.
|
||||
(shlv value shift-amount)
|
||||
(sarv value (- shift-amount))
|
||||
)
|
||||
)
|
||||
|
||||
(defun mod ((a integer) (b integer))
|
||||
"Compute mod. It does what you expect for positive numbers. For negative numbers, nobody knows what to expect.
|
||||
This is a 32-bit operation. It uses an idiv on x86 and gets the remainder."
|
||||
|
||||
;; The original implementation is div, mfhi
|
||||
;; todo - verify this is exactly the same as the PS2.
|
||||
(mod a b)
|
||||
)
|
||||
|
||||
|
||||
(defun rem ((a integer) (b integer))
|
||||
"Compute remainder (32-bit). It is identical to mod. It uses a idiv and gets the remainder"
|
||||
|
||||
;; The original implementation is div, mfhi
|
||||
;; todo - verify this is exactly the same as the PS2.
|
||||
(mod a b)
|
||||
)
|
||||
|
||||
(defun abs ((a int))
|
||||
"Take the absolute value of an integer"
|
||||
@ -84,7 +126,7 @@
|
||||
;; (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
|
||||
@ -94,13 +136,13 @@
|
||||
|
||||
(defun min ((a integer) (b integer))
|
||||
"Compute minimum."
|
||||
|
||||
|
||||
;; The original implementation was inline assembly, to take advantage of branch delay slots:
|
||||
;; (or v0 a0 r0) ;; move first arg to output (case of second arg being min)
|
||||
;; (or v1 a1 r0) ;; move second arg to v1 (likely strange coloring)
|
||||
;; (slt a0 v0 v1) ;; compare args
|
||||
;; (movz v0 v1 a0) ;; conditional move the second arg to v0 if it's the minimum
|
||||
|
||||
|
||||
(declare (inline))
|
||||
(if (> a b) b a)
|
||||
)
|
||||
@ -111,10 +153,34 @@
|
||||
(if (> a b) a b)
|
||||
)
|
||||
|
||||
;; todo logior
|
||||
;; todo lognor
|
||||
;; todo logxor
|
||||
;; todo lognot
|
||||
(defun logior ((a integer) (b integer))
|
||||
"Compute the bitwise inclusive-or"
|
||||
(logior a b)
|
||||
)
|
||||
|
||||
(defun logand ((a integer) (b integer))
|
||||
"Compute the bitwise and"
|
||||
(logand a b)
|
||||
)
|
||||
|
||||
(defun lognor ((a integer) (b integer))
|
||||
"Compute not or."
|
||||
;; Note - MIPS has a 'nor' instruction, but x86 doesn't.
|
||||
;; the GOAL x86 compiler therefore doesn't have a nor operation,
|
||||
;; so lognor is implemented by this inline function instead.
|
||||
(declare (inline))
|
||||
(lognot (logior a b))
|
||||
)
|
||||
|
||||
(defun logxor ((a integer) (b integer))
|
||||
"Compute the logical exclusive-or"
|
||||
(logxor a b)
|
||||
)
|
||||
|
||||
(defun lognot ((a integer))
|
||||
"Compute the bitwise not"
|
||||
(lognot a)
|
||||
)
|
||||
|
||||
(defun false-func ()
|
||||
"Return false"
|
||||
@ -135,6 +201,9 @@
|
||||
;; format. (In OpenGOAL, there's actually two trampoline functions, to make the 8 arguments all work.)
|
||||
;; For some reason, the C Kernel names this trampoline function _format. We need to set the value of format
|
||||
;; _format in order for format to work.
|
||||
|
||||
;; I suspect this was to let us define (yet another) function here which set up C-style var args (supported from C Kernel)
|
||||
;; or 128-bit arguments (unimplemented in C Kernel), but both of these were never finished.
|
||||
(define format _format)
|
||||
|
||||
;; todo bfloat
|
||||
|
10
goal_src/test/test-ash.gc
Normal file
10
goal_src/test/test-ash.gc
Normal file
@ -0,0 +1,10 @@
|
||||
(defun ash ((value integer) (shift-amount integer))
|
||||
(declare (inline))
|
||||
(if (> shift-amount 0)
|
||||
(shlv value shift-amount)
|
||||
(sarv value (- shift-amount))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
(+ (ash (+ 1 2) (/ 6 2)) (ash (- 12) (- 1)))
|
49
goal_src/test/test-format.gc
Normal file
49
goal_src/test/test-format.gc
Normal file
@ -0,0 +1,49 @@
|
||||
(define-extern _format function)
|
||||
(define format _format)
|
||||
|
||||
(format #t "test newline~%newline~%")
|
||||
(format #t "test tilde ~~ ~%")
|
||||
|
||||
;; test g, G
|
||||
|
||||
(format #t "test A print boxed-string: ~a~%" "boxed string!")
|
||||
(format #t "test A print symbol: ~A~%" 'a-symbol)
|
||||
(format #t "test A make boxed object longer: ~17A!~%" "srt")
|
||||
(format #t "test A non-default pad: ~12,'za~%" 'pad-me)
|
||||
(format #t "test A shorten(4): ~4a~%" 'a234567)
|
||||
(format #t "test A don'tchange(4): ~4a~%" 'a234)
|
||||
(format #t "test A shorten with pad(4): ~4,'za~%" 'shorten-me)
|
||||
(format #t "test A a few things ~a ~a ~a ~a~%" "one thing" 'a-second integer print)
|
||||
|
||||
|
||||
(format #t "test S ~s ~s ~s~%" "a string" 'a-symbol "another string!")
|
||||
|
||||
(format #t "test C ~c ~c~%" 41 #x5d)
|
||||
|
||||
(format #t "test P (no type) ~p~%" print)
|
||||
(format #t "test P (with type) ~`integer`p~%" print)
|
||||
|
||||
(format #t "test I (no type) ~i" inspect)
|
||||
(format #t "test I (with type) ~`integer`i" inspect)
|
||||
|
||||
(format #t "test X ~x ~10x ~10,'.x ~3x ~3,'.x~%" #xbaadbeef #x2 #x3 #x0badbeef #x0badbeef)
|
||||
|
||||
(format #t "test D ~d ~10d ~10,'.d ~3d ~3,'.d~%" #xbaadbeef #x2 #x3 #x0badbeef #x0badbeef)
|
||||
|
||||
(format #t "test B ~b ~10b ~10,'.b ~3b ~3,'.b~%" #xbaadbeef #x2 #x3 #x0badbeef #x0badbeef)
|
||||
|
||||
;; test f, F, r, R, m, M
|
||||
|
||||
(format #t "test E ~E ~e ~e~%" 1 20 301)
|
||||
(format #t "test pass through ~10,'b,2W~%")
|
||||
|
||||
(format #t "test tab~Taftertab~%")
|
||||
(format #t "test many ~d ~d ~d ~d ~d~%" 1 2 3 4 5)
|
||||
|
||||
|
||||
;; test floats
|
||||
(format #t "test f ~F ~12,'0,2f ~f~%" -1.234 1.234 -1.234)
|
||||
(format #t "test r ~R ~12,'0,2r ~r~%" 1.234 1.567 1.987)
|
||||
(format #t "test m ~M ~12,'0,2m ~m~%" 1.234 1.567 1.987)
|
||||
(format #t "test as float ~`float`P~%" -99987623.23123)
|
||||
0
|
1
goal_src/test/test-logand.gc
Normal file
1
goal_src/test/test-logand.gc
Normal file
@ -0,0 +1 @@
|
||||
(logand #b100100 #b011100) ; 4
|
1
goal_src/test/test-logior.gc
Normal file
1
goal_src/test/test-logior.gc
Normal file
@ -0,0 +1 @@
|
||||
(logior #b100100 #b011100)
|
1
goal_src/test/test-logxor.gc
Normal file
1
goal_src/test/test-logxor.gc
Normal file
@ -0,0 +1 @@
|
||||
(logxor #b100100 #b011100)
|
3
goal_src/test/test-mod.gc
Normal file
3
goal_src/test/test-mod.gc
Normal file
@ -0,0 +1,3 @@
|
||||
(let ((x 33))
|
||||
(+ (mod x 10) 4)
|
||||
)
|
2
goal_src/test/test-negative-integer-symbol.gc
Normal file
2
goal_src/test/test-negative-integer-symbol.gc
Normal file
@ -0,0 +1,2 @@
|
||||
(define a-negative-symbol -123)
|
||||
a-negative-symbol
|
16
goal_src/test/test-nested-function-call-2.gc
Normal file
16
goal_src/test/test-nested-function-call-2.gc
Normal file
@ -0,0 +1,16 @@
|
||||
(defun first-function ((a integer))
|
||||
(+ a 2)
|
||||
(+ a 3)
|
||||
(+ a 4)
|
||||
(+ a 6)
|
||||
(+ a 10)
|
||||
)
|
||||
|
||||
(defun second-function ((b integer))
|
||||
(if (> 2 b)
|
||||
0
|
||||
10
|
||||
)
|
||||
)
|
||||
|
||||
(second-function (second-function (second-function 3)))
|
2
goal_src/test/test-shiftvs.gc
Normal file
2
goal_src/test/test-shiftvs.gc
Normal file
@ -0,0 +1,2 @@
|
||||
(+ (shlv 2 3) (shlv 1 0) (shlv 0 4) (shrv 2 3) (shrv 10 2) (shlv -2 1) (sarv -16 2))
|
||||
;; 16 1 0 0 2 -4 -4
|
@ -101,6 +101,8 @@ class Compiler {
|
||||
Val* to_math_type(Val* in, MathMode mode, Env* env);
|
||||
bool is_none(Val* in);
|
||||
|
||||
Val* compile_variable_shift(const RegVal* in, const RegVal* sa, Env* env, IntegerMathKind kind);
|
||||
|
||||
public:
|
||||
// Atoms
|
||||
|
||||
@ -146,6 +148,14 @@ class Compiler {
|
||||
Val* compile_sub(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_mul(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_div(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_shlv(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_sarv(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_shrv(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_mod(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_logxor(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_lognot(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_logand(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
Val* compile_logior(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
||||
// Function
|
||||
Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||
|
@ -394,6 +394,22 @@ std::string IR_IntegerMath::print() {
|
||||
return fmt::format("imul {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::IDIV_32:
|
||||
return fmt::format("idiv {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::IMOD_32:
|
||||
return fmt::format("imod {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::SARV_64:
|
||||
return fmt::format("sarv {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::SHLV_64:
|
||||
return fmt::format("shlv {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::SHRV_64:
|
||||
return fmt::format("shrv {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::AND_64:
|
||||
return fmt::format("and {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::OR_64:
|
||||
return fmt::format("or {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::XOR_64:
|
||||
return fmt::format("xor {}, {}", m_dest->print(), m_arg->print());
|
||||
case IntegerMathKind::NOT_64:
|
||||
return fmt::format("not {}", m_dest->print());
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
@ -403,9 +419,12 @@ RegAllocInstr IR_IntegerMath::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());
|
||||
|
||||
if (m_kind == IntegerMathKind::IDIV_32) {
|
||||
if (m_kind != IntegerMathKind::NOT_64) {
|
||||
rai.read.push_back(m_arg->ireg());
|
||||
}
|
||||
|
||||
if (m_kind == IntegerMathKind::IDIV_32 || m_kind == IntegerMathKind::IMOD_32) {
|
||||
rai.exclude.emplace_back(emitter::RDX);
|
||||
}
|
||||
return rai;
|
||||
@ -423,6 +442,34 @@ void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen,
|
||||
gen->add_instr(
|
||||
IGen::sub_gpr64_gpr64(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
|
||||
break;
|
||||
case IntegerMathKind::AND_64:
|
||||
gen->add_instr(
|
||||
IGen::and_gpr64_gpr64(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
|
||||
break;
|
||||
case IntegerMathKind::OR_64:
|
||||
gen->add_instr(
|
||||
IGen::or_gpr64_gpr64(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
|
||||
break;
|
||||
case IntegerMathKind::XOR_64:
|
||||
gen->add_instr(
|
||||
IGen::xor_gpr64_gpr64(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
|
||||
break;
|
||||
case IntegerMathKind::NOT_64:
|
||||
gen->add_instr(IGen::not_gpr64(get_reg(m_dest, allocs, irec)), irec);
|
||||
assert(!m_arg);
|
||||
break;
|
||||
case IntegerMathKind::SHLV_64:
|
||||
gen->add_instr(IGen::shl_gpr64_cl(get_reg(m_dest, allocs, irec)), irec);
|
||||
assert(get_reg(m_arg, allocs, irec) == emitter::RCX);
|
||||
break;
|
||||
case IntegerMathKind::SHRV_64:
|
||||
gen->add_instr(IGen::shr_gpr64_cl(get_reg(m_dest, allocs, irec)), irec);
|
||||
assert(get_reg(m_arg, allocs, irec) == emitter::RCX);
|
||||
break;
|
||||
case IntegerMathKind::SARV_64:
|
||||
gen->add_instr(IGen::sar_gpr64_cl(get_reg(m_dest, allocs, irec)), irec);
|
||||
assert(get_reg(m_arg, allocs, irec) == emitter::RCX);
|
||||
break;
|
||||
case IntegerMathKind::IMUL_32: {
|
||||
auto dr = get_reg(m_dest, allocs, irec);
|
||||
gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec);
|
||||
@ -434,6 +481,13 @@ void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen,
|
||||
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;
|
||||
|
||||
case IntegerMathKind::IMOD_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::RDX), irec);
|
||||
} break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -103,17 +103,17 @@ static const std::unordered_map<
|
||||
{"-", &Compiler::compile_sub},
|
||||
{"*", &Compiler::compile_mul},
|
||||
{"/", &Compiler::compile_div},
|
||||
// {"shlv", &Compiler::compile_shlv},
|
||||
// {"shrv", &Compiler::compile_shrv},
|
||||
// {"sarv", &Compiler::compile_sarv},
|
||||
{"shlv", &Compiler::compile_shlv},
|
||||
{"shrv", &Compiler::compile_shrv},
|
||||
{"sarv", &Compiler::compile_sarv},
|
||||
// {"shl", &Compiler::compile_shl},
|
||||
// {"shr", &Compiler::compile_shr},
|
||||
// {"sar", &Compiler::compile_sar},
|
||||
// {"mod", &Compiler::compile_mod},
|
||||
// {"logior", &Compiler::compile_logior},
|
||||
// {"logxor", &Compiler::compile_logxor},
|
||||
// {"logand", &Compiler::compile_logand},
|
||||
// {"lognot", &Compiler::compile_lognot},
|
||||
{"mod", &Compiler::compile_mod},
|
||||
{"logior", &Compiler::compile_logior},
|
||||
{"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},
|
||||
|
@ -222,7 +222,7 @@ Val* Compiler::compile_div(const goos::Object& form, const goos::Object& rest, E
|
||||
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.instr_idx = fe->code().size();
|
||||
result_rax_constraint.ireg = result->ireg();
|
||||
result_rax_constraint.desired_register = emitter::RAX;
|
||||
fe->constrain(result_rax_constraint);
|
||||
@ -251,4 +251,145 @@ Val* Compiler::compile_div(const goos::Object& form, const goos::Object& rest, E
|
||||
}
|
||||
assert(false);
|
||||
return get_none();
|
||||
}
|
||||
|
||||
Val* Compiler::compile_shlv(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(env);
|
||||
return compile_variable_shift(first, second, env, IntegerMathKind::SHLV_64);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_sarv(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(env);
|
||||
return compile_variable_shift(first, second, env, IntegerMathKind::SARV_64);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_shrv(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(env);
|
||||
return compile_variable_shift(first, second, env, IntegerMathKind::SHRV_64);
|
||||
}
|
||||
|
||||
Val* Compiler::compile_variable_shift(const RegVal* in,
|
||||
const RegVal* sa,
|
||||
Env* env,
|
||||
IntegerMathKind kind) {
|
||||
auto result = env->make_gpr(in->type());
|
||||
auto sa_in = env->make_gpr(sa->type());
|
||||
|
||||
env->emit(std::make_unique<IR_RegSet>(result, in));
|
||||
env->emit(std::make_unique<IR_RegSet>(sa_in, sa));
|
||||
auto fenv = get_parent_env_of_type<FunctionEnv>(env);
|
||||
|
||||
IRegConstraint sa_con;
|
||||
sa_con.ireg = sa_in->ireg();
|
||||
sa_con.instr_idx = fenv->code().size();
|
||||
sa_con.desired_register = emitter::RCX;
|
||||
|
||||
if (get_math_mode(in->type()) != MathMode::MATH_INT ||
|
||||
get_math_mode(sa->type()) != MathMode::MATH_INT) {
|
||||
throw std::runtime_error("Can't shift a " + in->type().print() + " by a " + sa->type().print());
|
||||
}
|
||||
|
||||
fenv->constrain(sa_con);
|
||||
env->emit(std::make_unique<IR_IntegerMath>(kind, result, sa_in));
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_mod(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(env);
|
||||
auto fenv = get_parent_env_of_type<FunctionEnv>(env);
|
||||
|
||||
if (get_math_mode(first->type()) != MathMode::MATH_INT ||
|
||||
get_math_mode(second->type()) != MathMode::MATH_INT) {
|
||||
throw std::runtime_error("Can't mod a " + first->type().print() + " by a " +
|
||||
second->type().print());
|
||||
}
|
||||
|
||||
auto result = env->make_gpr(first->type());
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first));
|
||||
|
||||
IRegConstraint con;
|
||||
con.ireg = result->ireg();
|
||||
con.instr_idx = fenv->code().size();
|
||||
con.desired_register = emitter::RAX;
|
||||
|
||||
fenv->constrain(con);
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::IMOD_32, result, second));
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_logand(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(env);
|
||||
if (get_math_mode(first->type()) != MathMode::MATH_INT ||
|
||||
get_math_mode(second->type()) != MathMode::MATH_INT) {
|
||||
throw std::runtime_error("Can't logand a " + first->type().print() + " by a " +
|
||||
second->type().print());
|
||||
}
|
||||
|
||||
auto result = env->make_gpr(first->type());
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first));
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::AND_64, result, second));
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_logior(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(env);
|
||||
if (get_math_mode(first->type()) != MathMode::MATH_INT ||
|
||||
get_math_mode(second->type()) != MathMode::MATH_INT) {
|
||||
throw std::runtime_error("Can't logior a " + first->type().print() + " by a " +
|
||||
second->type().print());
|
||||
}
|
||||
|
||||
auto result = env->make_gpr(first->type());
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first));
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::OR_64, result, second));
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_logxor(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}, {}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
auto second = compile_error_guard(args.unnamed.at(1), env)->to_gpr(env);
|
||||
if (get_math_mode(first->type()) != MathMode::MATH_INT ||
|
||||
get_math_mode(second->type()) != MathMode::MATH_INT) {
|
||||
throw std::runtime_error("Can't logxor a " + first->type().print() + " by a " +
|
||||
second->type().print());
|
||||
}
|
||||
|
||||
auto result = env->make_gpr(first->type());
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first));
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::XOR_64, result, second));
|
||||
return result;
|
||||
}
|
||||
|
||||
Val* Compiler::compile_lognot(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||
auto args = get_va(form, rest);
|
||||
va_check(form, args, {{}}, {});
|
||||
auto first = compile_error_guard(args.unnamed.at(0), env)->to_gpr(env);
|
||||
if (get_math_mode(first->type()) != MathMode::MATH_INT) {
|
||||
throw std::runtime_error("Can't lognot a " + first->type().print());
|
||||
}
|
||||
|
||||
auto result = env->make_gpr(first->type());
|
||||
env->emit(std::make_unique<IR_RegSet>(result, first));
|
||||
env->emit(std::make_unique<IR_IntegerMath>(IntegerMathKind::NOT_64, result, nullptr));
|
||||
return result;
|
||||
}
|
@ -1477,27 +1477,30 @@ class IGen {
|
||||
/*!
|
||||
* Shift 64-bit gpr left by CL register
|
||||
*/
|
||||
static Instruction shl_gpr64_cl(uint8_t reg) {
|
||||
static Instruction shl_gpr64_cl(Register reg) {
|
||||
assert(reg.is_gpr());
|
||||
Instruction instr(0xd3);
|
||||
instr.set_modrm_and_rex(4, reg, 3, true);
|
||||
instr.set_modrm_and_rex(4, reg.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Shift 64-bit gpr right (logical) by CL register
|
||||
*/
|
||||
static Instruction shr_gpr64_cl(uint8_t reg) {
|
||||
static Instruction shr_gpr64_cl(Register reg) {
|
||||
assert(reg.is_gpr());
|
||||
Instruction instr(0xd3);
|
||||
instr.set_modrm_and_rex(5, reg, 3, true);
|
||||
instr.set_modrm_and_rex(5, reg.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Shift 64-bit gpr right (arithmetic) by CL register
|
||||
*/
|
||||
static Instruction sar_gpr64_cl(uint8_t reg) {
|
||||
static Instruction sar_gpr64_cl(Register reg) {
|
||||
assert(reg.is_gpr());
|
||||
Instruction instr(0xd3);
|
||||
instr.set_modrm_and_rex(7, reg, 3, true);
|
||||
instr.set_modrm_and_rex(7, reg.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
|
@ -113,19 +113,16 @@ struct CompilerTestRunner {
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(CompilerAndRuntime, BuildGame) {
|
||||
TEST(CompilerAndRuntime, BuildGameAndTest) {
|
||||
std::thread runtime_thread([]() { exec_runtime(0, nullptr); });
|
||||
Compiler compiler;
|
||||
|
||||
fprintf(stderr, "about to run test\n");
|
||||
|
||||
try {
|
||||
compiler.run_test("goal_src/test/test-build-game.gc");
|
||||
} catch (std::exception& e) {
|
||||
fprintf(stderr, "caught exception %s\n", e.what());
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
fprintf(stderr, "DONE!\n");
|
||||
|
||||
// todo, tests after loading the game.
|
||||
|
||||
@ -230,6 +227,14 @@ TEST(CompilerAndRuntime, CompilerTests) {
|
||||
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"});
|
||||
runner.run_test("test-shiftvs.gc", {"11\n"});
|
||||
runner.run_test("test-ash.gc", {"18\n"});
|
||||
runner.run_test("test-negative-integer-symbol.gc", {"-123\n"});
|
||||
runner.run_test("test-mod.gc", {"7\n"});
|
||||
runner.run_test("test-nested-function-call-2.gc", {"10\n"});
|
||||
runner.run_test("test-logand.gc", {"4\n"});
|
||||
runner.run_test("test-logior.gc", {"60\n"});
|
||||
runner.run_test("test-logxor.gc", {"56\n"});
|
||||
|
||||
expected = "test-string";
|
||||
runner.run_test("test-string-symbol.gc", {expected}, expected.size());
|
||||
@ -248,6 +253,21 @@ TEST(CompilerAndRuntime, CompilerTests) {
|
||||
runner.run_test("test-factorial-loop.gc", {"3628800\n"});
|
||||
runner.run_test("test-protect.gc", {"33\n"});
|
||||
|
||||
expected =
|
||||
"test newline\nnewline\ntest tilde ~ \ntest A print boxed-string: \"boxed string!\"\ntest A "
|
||||
"print symbol: a-symbol\ntest A make boxed object longer: \"srt\"!\ntest A "
|
||||
"non-default pad: zzzzzzpad-me\ntest A shorten(4): a23~\ntest A don'tchange(4): a234\ntest A "
|
||||
"shorten with pad(4): sho~\ntest A a few things \"one thing\" a-second integer #<compiled "
|
||||
"function @ #x161544>\n";
|
||||
|
||||
expected += "test S a string a-symbol another string!\n";
|
||||
expected += "test C ) ]\n";
|
||||
expected += "test P (no type) #<compiled function @ #x161544>\n";
|
||||
expected += "test P (with type) 1447236\n";
|
||||
|
||||
// todo, finish format testing.
|
||||
runner.run_test("test-format.gc", {expected}, expected.size());
|
||||
|
||||
compiler.shutdown_target();
|
||||
runtime_thread.join();
|
||||
runner.print_summary();
|
||||
|
Loading…
Reference in New Issue
Block a user