diff options
| author | dam <dam@gudinoff> | 2023-05-20 16:35:33 +0100 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2023-05-20 16:35:33 +0100 |
| commit | 03955d0d2db657aa8a5911ff959290fbaf60fd4c (patch) | |
| tree | c458252732e9241d292f2a5c1e994654832fcd62 | |
| parent | 1b278877b6605322d214b1faf9aeca5760415f90 (diff) | |
| download | task-time-tracker-03955d0d2db657aa8a5911ff959290fbaf60fd4c.tar.zst task-time-tracker-03955d0d2db657aa8a5911ff959290fbaf60fd4c.zip | |
Implemented branchless saturating integer sub.
| -rw-r--r-- | Math_Ext.jai | 217 |
1 files changed, 158 insertions, 59 deletions
diff --git a/Math_Ext.jai b/Math_Ext.jai index 1eef02b..07ea788 100644 --- a/Math_Ext.jai +++ b/Math_Ext.jai @@ -22,10 +22,12 @@ test_math_ext :: () { set_build_options_dc(.{do_output=false}); } */ - #import "Random"; - test_add :: (x: $Tx, y: $Ty, r: $Tr, t: Type, o: bool) -> errors_found: int { - tr, to := add(cast(Tx)x, cast(Ty)y); - print("add(%): % + % = % : %\n", t, x, y, r, o); + test_op :: (op: string, x: $Tx, y: $Ty, r: $Tr, t: Type, o: bool) -> errors_found: int #expand { + + #import "String"; + #insert #run replace("tr, to := OP(cast(Tx)x, cast(Ty)y);", "OP", op); + + print("%_%(%, %) = % : %\n", op, t, x, y, r, o); error := 0; if r != tr { error += 1; print(" > incorrect result value: got % expected %\n", tr, r); }; if t != type_of(tr) { error += 1; print(" > incorrect result type: got % expected %\n", type_of(tr), t); }; @@ -35,19 +37,50 @@ test_math_ext :: () { set_build_options_dc(.{do_output=false}); errors := 0; - // test_add(cast(u8)1, cast(u8)2, 3, u8, false); - // test_add(cast(u8)255, cast(u8)1, 255, u8, true); - - errors += test_add(cast( s8) S8_MAX, cast( s8)1, S8_MAX, s8, true); - errors += test_add(cast(s16)S16_MAX, cast( u8)1, S16_MAX, s16, true); - errors += test_add(cast(s32)S32_MAX, cast(s32)1, S32_MAX, s32, true); - errors += test_add(cast(s64)S64_MAX, cast(u32)1, S64_MAX, s64, true); + // Test signed add. + errors += test_op("add", cast( s8) S8_MAX, cast( s8)1, S8_MAX, s8, true); + errors += test_op("add", cast(s16)S16_MAX, cast( u8)1, S16_MAX, s16, true); + errors += test_op("add", cast(s32)S32_MAX, cast(s32)1, S32_MAX, s32, true); + errors += test_op("add", cast(s64)S64_MAX, cast(u32)1, S64_MAX, s64, true); + + errors += test_op("add", cast( s8) S8_MAX, cast( s8) S8_MIN, -1, s8, false); + errors += test_op("add", cast(s16)S16_MAX, cast(s16)S16_MIN, -1, s16, false); + errors += test_op("add", cast(s32)S32_MAX, cast(s32)S32_MIN, -1, s32, false); + errors += test_op("add", cast(s64)S64_MAX, cast(s64)S64_MIN, -1, s64, false); + + // Test unsigned add. + errors += test_op("add", cast( u8) U8_MAX, cast( u8)1, U8_MAX, u8, true); + errors += test_op("add", cast(u16)U16_MAX, cast(u16)1, U16_MAX, u16, true); + errors += test_op("add", cast(u32)U32_MAX, cast(u32)1, U32_MAX, u32, true); + errors += test_op("add", cast(u64)U64_MAX, cast(u64)1, U64_MAX, u64, true); + + errors += test_op("add", cast( u8) U8_MAX, cast( u8)0, U8_MAX, u8, false); + errors += test_op("add", cast(u16)U16_MAX, cast(u16)0, U16_MAX, u16, false); + errors += test_op("add", cast(u32)U32_MAX, cast(u32)0, U32_MAX, u32, false); + errors += test_op("add", cast(u64)U64_MAX, cast(u64)0, U64_MAX, u64, false); + + // Test signed sub. + errors += test_op("sub", cast( s8) S8_MIN, cast( s8)1, S8_MIN, s8, true); + errors += test_op("sub", cast(s16)S16_MIN, cast( u8)1, S16_MIN, s16, true); + errors += test_op("sub", cast(s32)S32_MIN, cast(s32)1, S32_MIN, s32, true); + errors += test_op("sub", cast(s64)S64_MIN, cast(u32)1, S64_MIN, s64, true); + + errors += test_op("sub", cast( s8)-1, cast( s8) S8_MAX, S8_MIN, s8, false); + errors += test_op("sub", cast(s16)-1, cast(s16)S16_MAX, S16_MIN, s16, false); + errors += test_op("sub", cast(s32)-1, cast(s32)S32_MAX, S32_MIN, s32, false); + errors += test_op("sub", cast(s64)-1, cast(s64)S64_MAX, S64_MIN, s64, false); + + // Test unsigned sub. + errors += test_op("sub", cast( u8)1, cast( u8) U8_MAX, 0, u8, true); + errors += test_op("sub", cast(u16)1, cast(u16)U16_MAX, 0, u16, true); + errors += test_op("sub", cast(u32)1, cast(u32)U32_MAX, 0, u32, true); + errors += test_op("sub", cast(u64)1, cast(u64)U64_MAX, 0, u64, true); + + errors += test_op("sub", cast( u8) U8_MAX, cast( u8)0, U8_MAX, u8, false); + errors += test_op("sub", cast(u16)U16_MAX, cast(u16)0, U16_MAX, u16, false); + errors += test_op("sub", cast(u32)U32_MAX, cast(u32)0, U32_MAX, u32, false); + errors += test_op("sub", cast(u64)U64_MAX, cast(u64)0, U64_MAX, u64, false); - errors += test_add(cast( u8) U8_MAX, cast( u8)1, U8_MAX, u8, true); - errors += test_add(cast(u16)U16_MAX, cast(u16)1, U16_MAX, u16, true); - errors += test_add(cast(u32)U32_MAX, cast(u32)1, U32_MAX, u32, true); - errors += test_add(cast(u64)U64_MAX, cast(u64)1, U64_MAX, u64, true); - // errors += test_add(cast(s32)66, cast(s64)-2, 64, s64, false); // errors += test_add(cast(u32)66, cast(s64)4, 70, s64, false); // errors += test_add(cast(s32)S32_MAX, cast(s64)1, 2147483648, s64, false); @@ -64,8 +97,8 @@ test_math_ext :: () { set_build_options_dc(.{do_output=false}); if errors > 0 print("# Found % %!\n", errors, ifx errors == 1 then "error" else "errors"); else print(" No errors found.\n"); - /* PERFORMANCE TEST + #import "Random"; best_generic: float; best_asm: float; for 0..100 { @@ -105,39 +138,39 @@ test_math_ext :: () { set_build_options_dc(.{do_output=false}); */ } -add :: (x: $Tx, y: $Ty) -> result: $Tr, saturated: bool //#dump - #modify { - type_info_x := cast(*Type_Info)Tx; - type_info_y := cast(*Type_Info)Ty; - if type_info_x.type != .INTEGER || type_info_y.type != .INTEGER return false, "Non integers values passed."; - tx := cast(*Type_Info_Integer)type_info_x; - ty := cast(*Type_Info_Integer)type_info_y; - - largest_type := - ifx tx.runtime_size > ty.runtime_size then Tx else - ifx ty.runtime_size > tx.runtime_size then Ty else - ifx tx.signed == ty.signed then Tx else - void; - - // Only allow to add different signedness values if largest type is the signed one (as in JAI). - if tx.signed == ty.signed { - Tx = largest_type; - Ty = largest_type; - Tr = largest_type; - } - else if tx.signed && Tx == largest_type { - Ty = largest_type; - Tr = largest_type; - } - else if ty.signed && Ty == largest_type { - Tx = largest_type; - Tr = largest_type; - } - else return false, "Number signedness mismatch."; - - print(">tx:ty:%:%\n", Tx, Ty); - return true; +INTEGER_ARITHMETIC_TYPES_CHECK :: #string DONE + type_info_x := cast(*Type_Info)Tx; + type_info_y := cast(*Type_Info)Ty; + if type_info_x.type != .INTEGER || type_info_y.type != .INTEGER return false, "Non integers values passed."; + tx := cast(*Type_Info_Integer)type_info_x; + ty := cast(*Type_Info_Integer)type_info_y; + + largest_type := + ifx tx.runtime_size > ty.runtime_size then Tx else + ifx ty.runtime_size > tx.runtime_size then Ty else + ifx tx.signed == ty.signed then Tx else + void; + + // Only allow to add different signedness values if largest type is the signed one (as in JAI). + if tx.signed == ty.signed { + Tx = largest_type; + Ty = largest_type; + Tr = largest_type; + } + else if tx.signed && Tx == largest_type { + Ty = largest_type; + Tr = largest_type; + } + else if ty.signed && Ty == largest_type { + Tx = largest_type; + Tr = largest_type; } + else return false, "Number signedness mismatch."; + + return true; +DONE + +add :: (x: $Tx, y: $Ty) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } // #dump { #if CPU != .X64 { @@ -177,7 +210,7 @@ add :: (x: $Tx, y: $Ty) -> result: $Tr, saturated: bool //#dump // Calculate limit based on x's sign. mov limit: gpr, MAX; mov sign: gpr, x; - shr sign, BITS; + shr.SIZE sign, BITS; add.SIZE limit, sign; mov result, x; @@ -198,7 +231,7 @@ add :: (x: $Tx, y: $Ty) -> result: $Tr, saturated: bool //#dump U_ADD_ASM :: #string DONE - #asm { // s8 + #asm { mov limit: gpr, MAX; mov result, x; add.SIZE result, y; @@ -222,16 +255,82 @@ add :: (x: $Tx, y: $Ty) -> result: $Tr, saturated: bool //#dump } } -sub :: (x: $Tx, y: $Ty) -> result: $Tr, overflow: bool //#dump - #modify { - return true; - } +sub :: (x: $Tx, y: $Ty) -> result: $Tr, overflow: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } //#dump { + #if CPU != .X64 { - if (y < 0 && x > S64_MAX + y) then return S64_MAX, true; - if (y > 0 && x < S64_MIN + y) then return S64_MIN, true; - return x - y, false; + + #if Tr == s8 || Tr == s16 || Tr == s32 || Tr == s64 { + + #if Tr == s8 { MAX :: S8_MAX; MIN :: S8_MIN; } + #if Tr == s16 { MAX :: S16_MAX; MIN :: S16_MIN; } + #if Tr == s32 { MAX :: S32_MAX; MIN :: S32_MIN; } + #if Tr == s64 { MAX :: S64_MAX; MIN :: S64_MIN; } + + if (y < 0 && x > MAX + y) then return MAX, true; + if (y > 0 && x < MIN + y) then return MIN, true; + return x - y, false; + + } else { + + if (y > x) then return 0, true; + return x - y, false; + + } + } else { - return x + y, false; // TODO Implement me please. + + #import "String"; + result: Tr = ---; + saturated: bool = ---; + + S_SUB_ASM :: #string DONE + #asm { + // Calculate limit based on x's sign. + mov limit: gpr, MAX; + mov sign: gpr, x; + shr.SIZE sign, BITS; + add.SIZE limit, sign; + + mov result, x; + sub.SIZE result, y; + seto saturated; + cmovo result, limit; + } + DONE + + #if Tr == s8 + #insert #run replace(replace(replace(S_SUB_ASM, ".SIZE", ".b"), "MAX", "127"), "BITS", "7"); + #if Tr == s16 + #insert #run replace(replace(replace(S_SUB_ASM, ".SIZE", ".w"), "MAX", "32767"), "BITS", "15"); + #if Tr == s32 + #insert #run replace(replace(replace(S_SUB_ASM, ".SIZE", ".d"), "MAX", "2147483647"), "BITS", "31"); + #if Tr == s64 + #insert #run replace(replace(replace(S_SUB_ASM, ".SIZE", ".q"), "MAX", "9223372036854775807"), "BITS", "63"); + + + U_SUB_ASM :: #string DONE + #asm { + mov limit: gpr, 0; + mov result, x; + sub.SIZE result, y; + setc saturated; + cmovc result, limit; + } + DONE + + #if Tr == u8 + #insert #run replace(U_SUB_ASM, ".SIZE", ".b"); + #if Tr == u16 + #insert #run replace(U_SUB_ASM, ".SIZE", ".w"); + #if Tr == u32 + #insert #run replace(U_SUB_ASM, ".SIZE", ".d"); + #if Tr == u64 + #insert #run replace(U_SUB_ASM, ".SIZE", ".q"); + + + return result, saturated; + } + } |
