#import "Basic"; #import "Compiler"; #import "Math"; // TODO Comparing implementaitons using dump #run test_math_ext(); test_math_ext :: () { set_build_options_dc(.{do_output=false}); // main :: () { write_strings("=====================\n", "--- Test Math_Ext ---\n"); // Different signals: only works if signaled variable is higher. /* #run cena(); cena :: () { a: s64 = -232; b: u32 = 4; c := a+b; print("\n\n--- --- ---\ntttt : % : % + % = %\n--- --- ---\n\n", type_of(c), a, b, c); } */ #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); 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); }; if o != to { error += 1; print(" > incorrect overflow flag: got % expected %\n", to, o); }; return error; } 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); 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); // errors += test_add(cast(s32)S32_MAX, cast(s32)1, S32_MAX, s32, true); // errors += test_add(cast(s64)S64_MAX, cast(s64)0, S64_MAX, s64, false); // errors += test_add(cast(s64)9223372036854775806, cast(s64)1, S64_MAX, s64, false); // errors += test_add(cast(s64)9223372036854775806, cast(s64)2, S64_MAX, s64, true); // errors += test_add(cast(u8)7, cast(u8)1, 8, u8, false); // errors += test_add(cast(u8)U8_MAX, cast(u8)1, U8_MAX, u8, true); // errors += test_add(cast(u16)10, cast(u8)3, 13, u16, false); // errors += test_add(cast(u8)1, cast(u16)U16_MAX, U16_MAX, u16, true); if errors > 0 print("# Found % %!\n", errors, ifx errors == 1 then "error" else "errors"); else print(" No errors found.\n"); /* PERFORMANCE TEST best_generic: float; best_asm: float; for 0..100 { size, time_generic, time_asm := performance_test(); perf_generic := cast(float)size/cast(float)to_microseconds(time_generic); perf_asm := cast(float)size/cast(float)to_microseconds(time_asm); best_generic = max(best_generic, perf_generic); best_asm = max(best_asm, perf_asm); } print("method : ops/usec\ngeneric : %\nasm : %\n", best_generic, best_asm); performance_test :: () -> sum_size: s64, time_generic: Apollo_Time, time_asm: Apollo_Time { SUM_SIZE := 2000000; numbers: [..] s64; array_reserve(*numbers, SUM_SIZE); for 0..SUM_SIZE-1 { array_add(*numbers, cast(s64)random_get()); } sum := 0; start := current_time_monotonic(); for numbers sum = add_bad(sum, it); time := current_time_monotonic() - start; sum_asm := 0; start_asm := current_time_monotonic(); for numbers sum_asm = add(sum_asm, it); time_asm := current_time_monotonic() - start_asm; assert(sum == sum_asm); return SUM_SIZE, time, time_asm; } */ } 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; } { #if 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; return x + y, false; } 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 { #import "String"; result: Tr = ---; saturated: bool = ---; S_ADD_ASM :: #string DONE #asm { // Calculate limit based on x's sign. mov limit: gpr, MAX; mov sign: gpr, x; shr sign, BITS; add.SIZE limit, sign; mov result, x; add.SIZE result, y; seto saturated; cmovo result, limit; } DONE #if Tr == s8 #insert #run replace(replace(replace(S_ADD_ASM, ".SIZE", ".b"), "MAX", "127"), "BITS", "7"); #if Tr == s16 #insert #run replace(replace(replace(S_ADD_ASM, ".SIZE", ".w"), "MAX", "32767"), "BITS", "15"); #if Tr == s32 #insert #run replace(replace(replace(S_ADD_ASM, ".SIZE", ".d"), "MAX", "2147483647"), "BITS", "31"); #if Tr == s64 #insert #run replace(replace(replace(S_ADD_ASM, ".SIZE", ".q"), "MAX", "9223372036854775807"), "BITS", "63"); U_ADD_ASM :: #string DONE #asm { // s8 mov limit: gpr, MAX; mov result, x; add.SIZE result, y; setc saturated; cmovc result, limit; } DONE #if Tr == u8 #insert #run replace(replace(U_ADD_ASM, ".SIZE", ".b"), "MAX", "255"); #if Tr == u16 #insert #run replace(replace(U_ADD_ASM, ".SIZE", ".w"), "MAX", "65535"); #if Tr == u32 #insert #run replace(replace(U_ADD_ASM, ".SIZE", ".d"), "MAX", "4294967295"); #if Tr == u64 #insert #run replace(replace(U_ADD_ASM, ".SIZE", ".q"), "MAX", "18446744073709551615"); return result, saturated; } } sub :: (x: $Tx, y: $Ty) -> result: $Tr, overflow: bool //#dump #modify { return true; } { #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; } else { return x + y, false; // TODO Implement me please. } }