diff --git a/JSTests/stress/dfg-parseIntResult-should-not-wrap-above-int32.js b/JSTests/stress/dfg-parseIntResult-should-not-wrap-above-int32.js new file mode 100644 index 0000000000000..13e9f3dbfd67b --- /dev/null +++ b/JSTests/stress/dfg-parseIntResult-should-not-wrap-above-int32.js @@ -0,0 +1,38 @@ +// parseInt results outside int32 range must be boxed as doubles, not +// sign-wrapped int32s, once the call site tiers up to the DFG/FTL. +// parseIntResult() used to do static_cast(input) before the round-trip +// comparison deciding int32 vs double boxing; the cast is undefined behavior +// for out-of-range inputs, which let LTO fold the overflow guard away. + +function hex(s) { + return parseInt(s, 16); +} +noInline(hex); + +function dec(s) { + return parseInt(s); +} +noInline(dec); + +const hugeHex = "f".repeat(400); + +for (let i = 0; i < testLoopCount; ++i) { + let v = hex("80000000"); + if (v !== 2147483648) + throw "FAILED: parseInt('80000000', 16) === " + v + " at iteration " + i; + v = hex("ffffffff"); + if (v !== 4294967295) + throw "FAILED: parseInt('ffffffff', 16) === " + v + " at iteration " + i; + v = hex("7fffffff"); + if (v !== 2147483647) + throw "FAILED: parseInt('7fffffff', 16) === " + v + " at iteration " + i; + v = hex("-80000001"); + if (v !== -2147483649) + throw "FAILED: parseInt('-80000001', 16) === " + v + " at iteration " + i; + v = dec("2147483648"); + if (v !== 2147483648) + throw "FAILED: parseInt('2147483648') === " + v + " at iteration " + i; + v = hex(hugeHex); // overflows to Infinity + if (v !== Infinity) + throw "FAILED: parseInt('f'.repeat(400), 16) === " + v + " at iteration " + i; +} diff --git a/JSTests/stress/switch-imm-scrutinee-above-int32.js b/JSTests/stress/switch-imm-scrutinee-above-int32.js new file mode 100644 index 0000000000000..2ba88284ee4eb --- /dev/null +++ b/JSTests/stress/switch-imm-scrutinee-above-int32.js @@ -0,0 +1,56 @@ +// A double scrutinee outside int32 range must never match an immediate +// switch case. slow_path_switch_imm used to do static_cast(value) +// — undefined behavior for out-of-range doubles — before the range check; +// a UB-exploiting optimizer could then fold the check away and match the +// case whose label equals the wrapped/saturated conversion result. + +function bigSwitch(x) { + switch (x) { + case -2147483648: + return "int32-min"; + case 2147483647: + return "int32-max"; + case 0: + return "zero"; + default: + return "default"; + } +} +noInline(bigSwitch); + +function denseSwitch(x) { + // Dense enough for a jump table (SwitchType::Immediate). + switch (x) { + case -2147483648: return 0; + case -2147483647: return 1; + case -2147483646: return 2; + case -2147483645: return 3; + case -2147483644: return 4; + case -2147483643: return 5; + case -2147483642: return 6; + case -2147483641: return 7; + default: return -1; + } +} +noInline(denseSwitch); + +for (let i = 0; i < testLoopCount; ++i) { + let r = bigSwitch(2147483648); // 2^31, truncates to INT32_MIN on x86 + if (r !== "default") + throw "FAILED: bigSwitch(2^31) === " + r + " at iteration " + i; + r = bigSwitch(4294967296); // 2^32, truncates to 0 via modular wrap + if (r !== "default") + throw "FAILED: bigSwitch(2^32) === " + r + " at iteration " + i; + r = bigSwitch(Infinity); + if (r !== "default") + throw "FAILED: bigSwitch(Infinity) === " + r + " at iteration " + i; + r = bigSwitch(-2147483648); + if (r !== "int32-min") + throw "FAILED: bigSwitch(-2^31) === " + r + " at iteration " + i; + r = denseSwitch(2147483648); + if (r !== -1) + throw "FAILED: denseSwitch(2^31) === " + r + " at iteration " + i; + r = denseSwitch(-2147483648); + if (r !== 0) + throw "FAILED: denseSwitch(-2^31) === " + r + " at iteration " + i; +}