diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index aaf565df3..b5e48ebd2 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -45,6 +45,12 @@ `(:exit) ) +(defmacro db () + `(begin + (set-config! print-ir #t) + (set-config! print-regalloc #t) + ) + ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CONDITIONAL COMPILATION diff --git a/goal_src/kernel/gcommon.gc b/goal_src/kernel/gcommon.gc index 1ea525e12..f611a24b9 100644 --- a/goal_src/kernel/gcommon.gc +++ b/goal_src/kernel/gcommon.gc @@ -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 diff --git a/goal_src/test/test-ash.gc b/goal_src/test/test-ash.gc new file mode 100644 index 000000000..e93ae2c3e --- /dev/null +++ b/goal_src/test/test-ash.gc @@ -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))) \ No newline at end of file diff --git a/goal_src/test/test-format.gc b/goal_src/test/test-format.gc new file mode 100644 index 000000000..88c4df9bd --- /dev/null +++ b/goal_src/test/test-format.gc @@ -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 \ No newline at end of file diff --git a/goal_src/test/test-logand.gc b/goal_src/test/test-logand.gc new file mode 100644 index 000000000..b6bf10077 --- /dev/null +++ b/goal_src/test/test-logand.gc @@ -0,0 +1 @@ +(logand #b100100 #b011100) ; 4 \ No newline at end of file diff --git a/goal_src/test/test-logior.gc b/goal_src/test/test-logior.gc new file mode 100644 index 000000000..a85f87c8d --- /dev/null +++ b/goal_src/test/test-logior.gc @@ -0,0 +1 @@ +(logior #b100100 #b011100) \ No newline at end of file diff --git a/goal_src/test/test-logxor.gc b/goal_src/test/test-logxor.gc new file mode 100644 index 000000000..d7109eb44 --- /dev/null +++ b/goal_src/test/test-logxor.gc @@ -0,0 +1 @@ +(logxor #b100100 #b011100) \ No newline at end of file diff --git a/goal_src/test/test-mod.gc b/goal_src/test/test-mod.gc new file mode 100644 index 000000000..18dfb311d --- /dev/null +++ b/goal_src/test/test-mod.gc @@ -0,0 +1,3 @@ +(let ((x 33)) + (+ (mod x 10) 4) + ) diff --git a/goal_src/test/test-negative-integer-symbol.gc b/goal_src/test/test-negative-integer-symbol.gc new file mode 100644 index 000000000..bc0082ea7 --- /dev/null +++ b/goal_src/test/test-negative-integer-symbol.gc @@ -0,0 +1,2 @@ +(define a-negative-symbol -123) +a-negative-symbol \ No newline at end of file diff --git a/goal_src/test/test-nested-function-call-2.gc b/goal_src/test/test-nested-function-call-2.gc new file mode 100644 index 000000000..ef7719679 --- /dev/null +++ b/goal_src/test/test-nested-function-call-2.gc @@ -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))) diff --git a/goal_src/test/test-shiftvs.gc b/goal_src/test/test-shiftvs.gc new file mode 100644 index 000000000..09f438eaf --- /dev/null +++ b/goal_src/test/test-shiftvs.gc @@ -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 \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 2241a52e3..dd1bd904c 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -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); diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index e30b2d039..abef14706 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -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); } diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index a703c4f09..ceeadddbc 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -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}, diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index 84de857ab..47350a7ee 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -222,7 +222,7 @@ Val* Compiler::compile_div(const goos::Object& form, const goos::Object& rest, E env->emit(std::make_unique(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(result, in)); + env->emit(std::make_unique(sa_in, sa)); + auto fenv = get_parent_env_of_type(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(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(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(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(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(result, first)); + env->emit(std::make_unique(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(result, first)); + env->emit(std::make_unique(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(result, first)); + env->emit(std::make_unique(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(result, first)); + env->emit(std::make_unique(IntegerMathKind::NOT_64, result, nullptr)); + return result; } \ No newline at end of file diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index 481e19f7b..1848c2925 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -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; } diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp index 3d6d0ba95..aaf97a8f8 100644 --- a/test/test_compiler_and_runtime.cpp +++ b/test/test_compiler_and_runtime.cpp @@ -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 #\n"; + + expected += "test S a string a-symbol another string!\n"; + expected += "test C ) ]\n"; + expected += "test P (no type) #\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();