#import "Basic"; #import "Compiler"; #import "Math"; #run test_math_ext(); test_math_ext :: () { set_build_options_dc(.{do_output=false}); write_strings("=====================\n", "--- Test Math_Ext ---\n"); Test_Inputs :: struct(ia: $T, ib: T, ir: T, is: bool) { // t: Type; a := ia; b := ib; r := ir; s := is; }; /* tests := Test_Inputs.[ // .{1, 2, 3, false}, ]; for * tests { result, saturated := add(cast(it.t)it.a, cast(it.t)it.b); assert(result == it.r && saturated == it.s, "Failed: % %\n", result, saturated); } */ t1a := S64_MAX; t1b := 0; t1v, t1r := add(t1a, t1b); assert(t1v == S64_MAX && t1r == false, "Failed: % %\n", t1v, t1r); write_string("t1: OK\n"); // t2a := S64_MAX; // t2b := 1; // t2v, t2r := add(t2a, t2b); // assert(t2v == S64_MAX && t2r == true, "Failed: % %\n", t2v, t2r); } add_int64 :: (x :s64, y: s64) -> s64 #dump { // TODO Comparing implementations. return ifx (y > 0 && x > S64_MAX - y) then S64_MAX else ifx (y < 0 && x < S64_MIN - y) then S64_MIN else x + y; } sub_int64 :: (x :s64, y :s64) -> s64 { return ifx (y < 0 && x > S64_MAX + y) then S64_MAX else ifx (y > 0 && x < S64_MIN + y) then S64_MIN else x - y; } add :: (value_a: s64, value_b: s64) -> result: s64, saturated: bool #dump { // TODO Comparing implementaitons using dump result: s64 = ---; flag: bool = ---; #asm { mov d: gpr === d, 9223372036854775807; mov a: gpr === a, value_a; mov b: gpr === b, value_b; add a, b; seto flag; // Flag overflow. mov result, a; mov a, value_a; shr a, 63; add a, d; mov c: gpr, value_a; xor c, b; xor b, result; not b; or c, b; test c, c; cmovns result, a; } return result, flag; } /* // value_a: s64 = 2; // value_b: s64 = S64_MAX-1; value_a: s64 = -2; value_b: s64 = S64_MIN+1; print(">%\n", S64_MAX); argx := get_command_line_arguments(); if argx.count > 1 value_a = parse_int(*argx[1]); if argx.count > 2 value_b = parse_int(*argx[2]); result: s64 = ---; flags: s64;// = ---; #asm LAHF_SAHF { // TODO Remove LAHF_SAHF it not required. // Code from https://locklessinc.com/articles/sat_arithmetic/ // value_a === b; // value_b === c; // add value_a, value_b; // mov result, value_a; // Version 1 // mov b: gpr === b, value_a; // mov c: gpr === c, value_b; // add b, c; // mov result, b; // seto flags; // cmovns b, c; // s64b sat_adds64b(s64b x, s64b y) // { // u64b ux = x; // u64b uy = y; // u64b res = ux + uy; // // ux = (ux >> 63) + LONG_MAX; // // /* Force compiler to use cmovns instruction */ // if ((s64b) ((ux ^ uy) | ~(uy ^ res)) >= 0) // { // res = ux; // } // // return res; // } // Version 2 - WORKS mov d: gpr === d, 9223372036854775807; mov a: gpr === a, value_a; mov b: gpr === b, value_b; add a, b; seto flags; // Flag overflow. mov result, a; mov a, value_a; shr a, 63; add a, d; mov c: gpr, value_a; xor c, b; xor b, result; not b; or c, b; test c, c; cmovns result, a; // s64b sat_subs64b(s64b x, s64b y) // { // u64b ux = x; // u64b uy = y; // u64b res = ux - uy; // // ux = (ux >> 63) + LONG_MAX; // // // Force compiler to use cmovns instruction // if ((s64b)((ux ^ uy) & (ux ^ res)) < 0) // { // res = ux; // } // // return res; // } // TODO Use https://godbolt.org/ to help } print("% + % = %\n", value_a, value_b, result); print("flag: %\n", flags); return; */