add float min max to the compiler

This commit is contained in:
water 2020-11-29 10:46:14 -05:00
parent 435b0da144
commit e93b54347f
14 changed files with 178 additions and 20 deletions

View file

@ -47,4 +47,19 @@ Just types. Done!
Types and one function. Done, but the matrix-copy! function isn't that efficient.
## quaternion-h.gc
Done!
Done!
## euler-h.gc
Needs static arrays
## transform-h.gc
Done!
## geometry-h.gc
Done!
## trigonometry-h.gc
Empty File.
## transformq-h.gc
Not done.

View file

@ -210,6 +210,8 @@
("ENGINE.CGO"
("types-h.o" "types-h")
("vu1-macros.o" "vu1-macros")
;; the "math" section
("math.o" "math")
("vector-h.o" "vector-h")
("gravity-h.o" "gravity-h")
@ -228,6 +230,8 @@
("euler.o" "euler")
("geometry.o" "geometry")
("trigonometry.o" "trigonometry")
("gsound-h.o" "gsound-h")
("timer-h.o" "timer-h")
("timer.o" "timer")

View file

@ -5,3 +5,32 @@
;; name in dgo: geometry-h
;; dgos: GAME, ENGINE
;; geometry-h
(deftype curve (structure)
((cverts uint32 :offset-assert 0)
(num-cverts int32 :offset-assert 4)
(knots uint32 :offset-assert 8)
(num-knots int32 :offset-assert 12)
(length float :offset-assert 16)
)
:method-count-assert 9
:size-assert #x14
:flag-assert #x900000014
)
;; geometry-h
(deftype border-plane (basic)
((name basic :offset-assert 4)
(action basic :offset-assert 8)
(slot int8 :offset-assert 12)
(trans vector :inline :offset-assert 16)
(normal vector :inline :offset-assert 32)
)
:method-count-assert 11
:size-assert #x30
:flag-assert #xb00000030
(:methods
(dummy-9 () none 9)
(dummy-10 () none 10)
)
)

View file

@ -5,30 +5,20 @@
;; name in dgo: math
;; dgos: GAME, ENGINE
;; contains various math helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; float macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; at some point, these could be more optimized.
;; at some point, this could be more optimized.
;; MIPS has an explicit abs.s instruction, but x86-64 doesn't.
;; modern clang on O3 does a comiss/branch and this is probably pretty close.
(defmacro fabs (x)
;"Floating point absolute value. On MIPS there is a abs.s instruction.
; In the future we could consider optimizing this more for x86, but this is good enough."
`(if (< ,x 0)
(- ,x)
,x)
)
(defmacro fmin (x y)
`(if (< ,x ,y)
,x
,y)
)
(defmacro fmax (x y)
`(if (> ,x ,y)
,x
,y)
`(if (< (the float ,x) 0)
(- (the float ,x))
(the float ,x))
)

View file

@ -5,3 +5,24 @@
;; name in dgo: transform-h
;; dgos: GAME, ENGINE
(deftype transform (structure)
((trans vector :inline :offset-assert 0)
(rot vector :inline :offset-assert 16)
(scale vector :inline :offset-assert 32)
)
:method-count-assert 9
:size-assert #x30
:flag-assert #x900000030
)
(deftype trs (basic)
((trans vector :inline :offset-assert 16)
(rot vector :inline :offset-assert 32)
(scale vector :inline :offset-assert 48)
)
:method-count-assert 9
:size-assert #x40
:flag-assert #x900000040
)

View file

@ -231,6 +231,8 @@ class Compiler {
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);
Val* compile_pointer_add(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_fmin(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_fmax(const goos::Object& form, const goos::Object& rest, Env* env);
// Function
Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env);

View file

@ -522,6 +522,10 @@ std::string IR_FloatMath::print() {
return fmt::format("addss {}, {}", m_dest->print(), m_arg->print());
case FloatMathKind::SUB_SS:
return fmt::format("subss {}, {}", m_dest->print(), m_arg->print());
case FloatMathKind::MAX_SS:
return fmt::format("maxss {}, {}", m_dest->print(), m_arg->print());
case FloatMathKind::MIN_SS:
return fmt::format("minss {}, {}", m_dest->print(), m_arg->print());
default:
throw std::runtime_error("Unsupported FloatMathKind");
}
@ -555,6 +559,14 @@ void IR_FloatMath::do_codegen(emitter::ObjectGenerator* gen,
gen->add_instr(
IGen::subss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
break;
case FloatMathKind::MAX_SS:
gen->add_instr(
IGen::maxss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
break;
case FloatMathKind::MIN_SS:
gen->add_instr(
IGen::minss_xmm_xmm(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec);
break;
default:
assert(false);
}

View file

@ -224,7 +224,7 @@ class IR_IntegerMath : public IR {
RegVal* m_arg;
};
enum class FloatMathKind { DIV_SS, MUL_SS, ADD_SS, SUB_SS };
enum class FloatMathKind { DIV_SS, MUL_SS, ADD_SS, SUB_SS, MIN_SS, MAX_SS };
class IR_FloatMath : public IR {
public:

View file

@ -124,6 +124,8 @@ static const std::unordered_map<
{"<", &Compiler::compile_condition_as_bool},
{">", &Compiler::compile_condition_as_bool},
{"&+", &Compiler::compile_pointer_add},
{"fmax", &Compiler::compile_fmax},
{"fmin", &Compiler::compile_fmin},
// BUILDER (build-dgo/build-cgo?)
{"build-dgos", &Compiler::compile_build_dgo},

View file

@ -195,6 +195,52 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E
return get_none();
}
Val* Compiler::compile_fmin(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest);
if (!args.named.empty() || args.unnamed.empty()) {
throw_compile_error(form, "Invalid fmin form");
}
// look at the first value to determine the math mode
auto first_val = compile_error_guard(args.unnamed.at(0), env);
if (get_math_mode(first_val->type()) != MATH_FLOAT) {
throw_compile_error(form, "Must use floats in fmin");
}
auto result = env->make_xmm(first_val->type());
env->emit(std::make_unique<IR_RegSet>(result, first_val->to_xmm(env)));
for (size_t i = 1; i < args.unnamed.size(); i++) {
auto val = compile_error_guard(args.unnamed.at(i), env);
if (get_math_mode(val->type()) != MATH_FLOAT) {
throw_compile_error(form, "Must use floats in fmin");
}
env->emit(std::make_unique<IR_FloatMath>(FloatMathKind::MIN_SS, result, val->to_xmm(env)));
}
return result;
}
Val* Compiler::compile_fmax(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest);
if (!args.named.empty() || args.unnamed.empty()) {
throw_compile_error(form, "Invalid fmax form");
}
// look at the first value to determine the math mode
auto first_val = compile_error_guard(args.unnamed.at(0), env);
if (get_math_mode(first_val->type()) != MATH_FLOAT) {
throw_compile_error(form, "Must use floats in fmax");
}
auto result = env->make_xmm(first_val->type());
env->emit(std::make_unique<IR_RegSet>(result, first_val->to_xmm(env)));
for (size_t i = 1; i < args.unnamed.size(); i++) {
auto val = compile_error_guard(args.unnamed.at(i), env);
if (get_math_mode(val->type()) != MATH_FLOAT) {
throw_compile_error(form, "Must use floats in fmax");
}
env->emit(std::make_unique<IR_FloatMath>(FloatMathKind::MAX_SS, result, val->to_xmm(env)));
}
return result;
}
Val* Compiler::compile_imul64(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest);
if (!args.named.empty() || args.unnamed.empty()) {

View file

@ -1740,6 +1740,34 @@ class IGen {
return instr;
}
/*!
* Floating point minimum.
*/
static Instruction minss_xmm_xmm(Register dst, Register src) {
assert(dst.is_xmm());
assert(src.is_xmm());
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x5d);
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Floating point maximum.
*/
static Instruction maxss_xmm_xmm(Register dst, Register src) {
assert(dst.is_xmm());
assert(src.is_xmm());
Instruction instr(0xf3);
instr.set_op2(0x0f);
instr.set_op3(0x5f);
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false);
instr.swap_op0_rex();
return instr;
}
/*!
* Convert GPR int32 to XMM float (single precision)
*/

View file

@ -0,0 +1,2 @@
(define format _format)
(format #t "~,,2f~%" (fmax 1.0 2.0 -1.2 3.7 2.2))

View file

@ -0,0 +1,2 @@
(define format _format)
(format #t "~,,2f~%" (fmin 1.0 2.0 -1.2 3.7 2.2))

View file

@ -75,3 +75,8 @@ TEST_F(FloatTests, Functions) {
env, testCategory, "nested-float-functions.static.gc",
{"i 1.4400 3.4000\nr 10.1523\ni 1.2000 10.1523\nr 17.5432\n17.543 10.152\n0\n"});
}
TEST_F(FloatTests, MinMax) {
runner.run_static_test(env, testCategory, "float-max.static.gc", {"3.70\n0\n"});
runner.run_static_test(env, testCategory, "float-min.static.gc", {"-1.20\n0\n"});
}