aboutsummaryrefslogtreecommitdiff
path: root/Math_Ext.jai
diff options
context:
space:
mode:
authordam <dam@gudinoff>2023-07-06 21:37:43 +0100
committerdam <dam@gudinoff>2023-07-06 21:37:43 +0100
commit0932c04aa6930ab376be18a86ec1385d27271d75 (patch)
treee4781118ef62a71b361a1be5a41028e85014bf78 /Math_Ext.jai
parentb0e635801a343eb381975313f3118827660f0a7c (diff)
downloadtask-time-tracker-0932c04aa6930ab376be18a86ec1385d27271d75.tar.zst
task-time-tracker-0932c04aa6930ab376be18a86ec1385d27271d75.zip
Integer Saturating Arithmetic v1.0
Diffstat (limited to 'Math_Ext.jai')
-rw-r--r--Math_Ext.jai418
1 files changed, 0 insertions, 418 deletions
diff --git a/Math_Ext.jai b/Math_Ext.jai
deleted file mode 100644
index 20ccfe3..0000000
--- a/Math_Ext.jai
+++ /dev/null
@@ -1,418 +0,0 @@
-// Integer saturating arighmetic (with branch-free procedures on x64).
-// Expects signed values in two's complement.
-
-#import "Basic";
-#import "Compiler";
-#import "Math";
-#import "String";
-
-
-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, $USE_GENERIC: bool = false) -> result: $Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } // #dump
-{
-
- #if USE_GENERIC || CPU != .X64 {
-
- #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;
-
- } else {
-
- #if Tr == u8 { MAX :: U8_MAX; }
- #if Tr == u16 { MAX :: U16_MAX; }
- #if Tr == u32 { MAX :: U32_MAX; }
- #if Tr == u64 { MAX :: U64_MAX; }
-
- if (x > MAX - y) then return MAX, true;
-
- }
-
- return x + y, false;
-
- } else {
-
- result: Tr = ---;
- saturated: bool = ---;
-
-
- ADD_SIGNED_ASM :: #string DONE
- #asm {
- mov result, -1; // Pre-set result with signed maximum (set all bits...
- shr.SIZE result, 1; // ...then, clear MSB).
- bt x, SIGN_BIT; // Test sign bit (affect CF).
- adc result, 0; // Overflow signed maximum to signed minimum if CF is set.
-
- add.SIZE x, y; // Add values (affect OF).
- seto saturated; // Set saturated flag if OF.
- cmovno result, x; // Move add-result to result if NOT OF.
- }
- DONE
-
- #if Tr == s8
- #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7");
- #if Tr == s16
- #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15");
- #if Tr == s32
- #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31");
- #if Tr == s64
- #insert #run replace(replace(ADD_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63");
-
-
- ADD_UNSIGNED_ASM :: #string DONE
- #asm {
- mov result, -1; // Pre-set result with unsigned maximum.
- add.SIZE x, y; // Add values (affect CF).
- setc saturated; // Set saturated flag if CF.
- cmovnc result, x; // Move add-result to result if NOT CF.
- }
- DONE
-
- #if Tr == u8
- #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".b");
- #if Tr == u16
- #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".w");
- #if Tr == u32
- #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".d");
- #if Tr == u64
- #insert #run replace(ADD_UNSIGNED_ASM, ".SIZE", ".q");
-
-
- return result, saturated;
-
- }
-}
-
-sub :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, overflow: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } // #dump
-{
-
- #if USE_GENERIC || CPU != .X64 {
-
- #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;
-
- } else {
-
- if (y > x) then return 0, true;
-
- }
-
- return x - y, false;
-
- } else {
-
- result: Tr = ---;
- saturated: bool = ---;
-
-
- SUB_SIGNED_ASM :: #string DONE
- #asm {
- mov result, -1; // Pre-set result with signed maximum (set all bits...
- shr.SIZE result, 1; // ...then, clear MSB).
- bt x, SIGN_BIT; // Test signal bit (affect CF).
- adc result, 0; // Overflow signed maximum to signed minimum if CF is set.
-
- sub.SIZE x, y; // Subtract values (affect OF).
- seto saturated; // Set saturated flag if OF.
- cmovno result, x; // Move subtract-result to result if NOT OF.
- }
- DONE
-
- #if Tr == s8
- #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7");
- #if Tr == s16
- #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15");
- #if Tr == s32
- #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31");
- #if Tr == s64
- #insert #run replace(replace(SUB_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63");
-
-
- SUB_UNSIGNED_ASM :: #string DONE
- #asm {
- xor result, result; // Pre-set result with usigned minimum (zero).
- sub.SIZE x, y; // Subtract values (affect CF).
- setc saturated; // Set saturated flag if CF.
- cmovnc result, x; // Move subtract-result to result if NOT CF.
- }
- DONE
-
- #if Tr == u8
- #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".b");
- #if Tr == u16
- #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".w");
- #if Tr == u32
- #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".d");
- #if Tr == u64
- #insert #run replace(SUB_UNSIGNED_ASM, ".SIZE", ".q");
-
-
- return result, saturated;
-
- }
-
-}
-
-mul :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, overflow: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } // #dump
-{
-
- #if USE_GENERIC || CPU != .X64 {
-
- #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 x == 0 || y == 0 then return 0, false;
- if x > 0 && y > 0 && x > MAX / y then return MAX, true;
- if x < 0 && y < 0 && x < MAX / y then return MAX, true;
- if (y < 0 && x > 0 && y < MIN / x) || (x < 0 && y > 0 && x < MIN / y) then return MIN, true;
-
- } else {
-
- #if Tr == u8 { MAX :: U8_MAX; }
- #if Tr == u16 { MAX :: U16_MAX; }
- #if Tr == u32 { MAX :: U32_MAX; }
- #if Tr == u64 { MAX :: U64_MAX; }
-
- if x == 0 || y == 0 then return 0, false;
- if x > MAX / y then return MAX, true;
-
- }
-
- return x * y, false;
-
- } else {
-
- result: Tr = ---;
- saturated: bool = ---;
-
- MUL_SIGNED_ASM :: #string DONE
- #asm {
- // Using two copies of the x value (x_, sign) seems to be a bit faster (not sure why).
- mov x_: gpr === a, x; // Pin copy of x value to register A.
-
- mov result, -1; // Pre-set result with signed maximum (set all bits...
- shr.SIZE result, 1; // ...then, clear MSB).
- mov sign:, x; // Use copy of x value.
- xor sign, y; // Calculate result signal bit using xor.
- bt sign, SIGN_BIT; // Test signal bit (affect CF).
- adc result, 0; // Overflow signed maximum to signed minimum if CF is set.
-
- imul.SIZE x_, y; // Multiply values (affect OF).
- seto saturated; // Set saturated flag if OF.
- cmovno result, x_; // Move multiply-result to result if NOT OF.
- }
- DONE
-
- #if Tr == s8
- #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".b"), "SIGN_BIT", "7");
- #if Tr == s16
- #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".w"), "SIGN_BIT", "15");
- #if Tr == s32
- #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".d"), "SIGN_BIT", "31");
- #if Tr == s64
- #insert #run replace(replace(MUL_SIGNED_ASM, ".SIZE", ".q"), "SIGN_BIT", "63");
-
-
- MUL_UNSIGNED_ASM :: #string DONE
- #asm {
- result === a; // Pin result to register A.
-
- mov result, x; // Move value x to result.
- mul.SIZE reg_d:, result, y; // Multiply values (affect CF).
- setc saturated; // Set saturated flag if CF.
- sbb mask:, mask; // If CF: mask = -1 (all bits set); else: mask = 0.
- or result, mask; // If CF was set, then result will be set to unsigned maximum (all bits set).
- }
- DONE
-
- #if Tr == u8
- #insert #run replace(replace(MUL_UNSIGNED_ASM, ".SIZE", ".b"), "reg_d:,", ""); // For 8bits mul, we do not need D register.
- #if Tr == u16
- #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".w");
- #if Tr == u32
- #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".d");
- #if Tr == u64
- #insert #run replace(MUL_UNSIGNED_ASM, ".SIZE", ".q");
-
-
- return result, saturated;
-
- }
-}
-
-div :: (x: $Tx, y: $Ty, $USE_GENERIC: bool = false) -> result: $Tr, remainder: Tr, saturated: bool #modify { #insert INTEGER_ARITHMETIC_TYPES_CHECK; } //#dump
-{
-
- #if USE_GENERIC || CPU != .X64 {
-
- #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 x == MIN && y == -1 then return MAX, -1, true;
-
- }
-
- result := x / y;
- remainder := x - (y * result);
- return result, remainder, false;
-
- } else {
-
- result: Tr = ---;
- remainder: Tr = ---;
- saturated: bool = ---;
-
- DIV_SIGNED_ASM :: #string DONE
- #asm {
- result === a; // Pin result to register A (to be used as dividend on idiv).
- remainder === d; // Pin remainder to register D.
-
- xor saturated, saturated; // Clear saturated.
-
- // Detect div(MIN/-1) and flag it on ZF.
- mov t_dividend:, -1; // Pre-set t_dividend with signed minimum (set all bits...
- shr.SIZE t_dividend, 1; // ...then, clear MSB...
- not t_dividend; // ...then, negate to obtain MSB set and all other bits cleared).
- //
- mov limit:, t_dividend; // Keep copy of signed minimum on limit.
- add limit, 1; // Set limit as signed minimum + 1.
- //
- xor.SIZE t_dividend, x; // Clear dividend if x value is equal to signed minimum.
- //
- mov t_divisor:, -1; // Pre-set test_divisor with -1.
- xor.SIZE t_divisor, y; // Clear test_divisor if y value is equal to -1.
- //
- or.SIZE t_dividend, t_divisor; // Or t_dividend with t_divisor (affect ZF).
-
- setz saturated; // Set saturated flag if ZF.
- mov result, x; // Copy x value to result (dividend).
- cmovz result, limit; // If ZF: copy limit (signed minimum + 1) to result (dividend).
-
- DIVIDE_PLACEHOLDER
-
- sub.SIZE remainder, saturated; // If saturated: remainder = 0 - 1; otherwise: remainder = x - 0.
- }
- DONE
-
- DIV_SIGNED_CALC_8BITS :: #string DONE
- cbw result; // Prepare dividend high bits (sign-extend).
- idiv.SIZE result, y; // Divide values.
- mov remainder, result; // Extract remainder from result's high bits.
- sar remainder, 8; // Shift remainder from high to low bits.
- DONE
-
- DIV_SIGNED_CALC_16BITS :: #string DONE
- cwd remainder, result; // Prepare dividend high bits (sign-extend).
- idiv.SIZE remainder, result, y; // Divide values.
- DONE
-
- DIV_SIGNED_CALC_32BITS :: #string DONE
- cdq remainder, result; // Prepare dividend high bits (sign-extend).
- idiv.SIZE remainder, result, y; // Divide values.
- DONE
-
- DIV_SIGNED_CALC_64BITS :: #string DONE
- cqo remainder, result; // Prepare dividend high bits (sign-extend).
- idiv.SIZE remainder, result, y; // Divide values.
- DONE
-
- #if Tr == s8
- #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_8BITS), ".SIZE", ".b");
- #if Tr == s16
- #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_16BITS), ".SIZE", ".w");
- #if Tr == s32
- #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_32BITS), ".SIZE", ".d");
- #if Tr == s64
- #insert #run replace(replace(DIV_SIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_SIGNED_CALC_64BITS), ".SIZE", ".q");
-
-
- DIV_UNSIGNED_ASM :: #string DONE
- #asm {
- result === a; // Pin result to register A.
- remainder === d; // Pin remainder to register D.
-
- xor result, result; // Clear result.
- xor remainder, remainder; // Clear remainder (required when used as dividend's high bits).
- xor saturated, saturated; // Clear saturated (unsigned division never saturates).
- mov result, x; // Copy x value to result.
-
- DIVIDE_PLACEHOLDER
- }
- DONE
-
- DIV_UNSIGNED_CALC_8BITS :: #string DONE
- div.SIZE result, y; // Divide values.
- mov remainder, result; // Extract remainder from result's high bits.
- sar remainder, 8; // Shift remainder from high to low bits.
- DONE
-
- DIV_UNSIGNED_CALC :: #string DONE
- div.SIZE remainder, result, y; // Divide values.
- DONE
-
- #if Tr == u8
- #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC_8BITS), ".SIZE", ".b");
- #if Tr == u16
- #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".w");
- #if Tr == u32
- #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".d");
- #if Tr == u64
- #insert #run replace(replace(DIV_UNSIGNED_ASM, "DIVIDE_PLACEHOLDER", DIV_UNSIGNED_CALC), ".SIZE", ".q");
-
-
- return result, remainder, saturated;
-
- }
-}