From 878b7b80c15d4b9a894f6ab1d4e8753de77568ad Mon Sep 17 00:00:00 2001 From: Koki Ueha Date: Sun, 15 Jun 2025 01:05:58 +0000 Subject: [PATCH] libc: Prevent FCSEL instruction from being used to avoid raising an unintended exception If you write an if expression in mem.doNotOptimizeAway like doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120);, FCSEL instruction is used on AArch64. FCSEL instruction selects one of the two registers according to the condition and copies its value. In this example, `x / 0x1p120` and `x + 0x1p120` are expressions that raise different floating-point exceptions. However, since both are actually evaluated before the FCSEL instruction, the exception not intended by the programmer may also be raised. To prevent FCSEL instruction from being used here, this commit splits doNotOptimizeAway in two. --- lib/compiler_rt/sin.zig | 16 ++++++++++++++-- lib/compiler_rt/sincos.zig | 16 ++++++++++++++-- lib/compiler_rt/tan.zig | 16 ++++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/compiler_rt/sin.zig b/lib/compiler_rt/sin.zig index 4ec2fa329ee214969965a2fdd68651bd61667707..e14779d27a2b2cd74e847a6191dc38a8d38749d4 100644 --- a/lib/compiler_rt/sin.zig +++ b/lib/compiler_rt/sin.zig @@ -49,7 +49,13 @@ pub fn sinf(x: f32) callconv(.c) f32 { if (ix <= 0x3f490fda) { // |x| ~<= pi/4 if (ix < 0x39800000) { // |x| < 2**-12 // raise inexact if x!=0 and underflow if subnormal - if (common.want_float_exceptions) mem.doNotOptimizeAway(if (ix < 0x00800000) x / 0x1p120 else x + 0x1p120); + if (common.want_float_exceptions) { + if (ix < 0x00800000) { + mem.doNotOptimizeAway(x / 0x1p120); + } else { + mem.doNotOptimizeAway(x + 0x1p120); + } + } return x; } return trig.__sindf(x); @@ -98,7 +104,13 @@ pub fn sin(x: f64) callconv(.c) f64 { if (ix <= 0x3fe921fb) { if (ix < 0x3e500000) { // |x| < 2**-26 // raise inexact if x != 0 and underflow if subnormal - if (common.want_float_exceptions) mem.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120); + if (common.want_float_exceptions) { + if (ix < 0x00100000) { + mem.doNotOptimizeAway(x / 0x1p120); + } else { + mem.doNotOptimizeAway(x + 0x1p120); + } + } return x; } return trig.__sin(x, 0.0, 0); diff --git a/lib/compiler_rt/sincos.zig b/lib/compiler_rt/sincos.zig index ec082ff436cfdcac6b6e2f093173e71de8634bd4..72b90a51e70339fc4828a90719dcf770a468afcc 100644 --- a/lib/compiler_rt/sincos.zig +++ b/lib/compiler_rt/sincos.zig @@ -46,7 +46,13 @@ pub fn sincosf(x: f32, r_sin: *f32, r_cos: *f32) callconv(.c) void { // |x| < 2**-12 if (ix < 0x39800000) { // raise inexact if x!=0 and underflow if subnormal - if (common.want_float_exceptions) mem.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120); + if (common.want_float_exceptions) { + if (ix < 0x00100000) { + mem.doNotOptimizeAway(x / 0x1p120); + } else { + mem.doNotOptimizeAway(x + 0x1p120); + } + } r_sin.* = x; r_cos.* = 1.0; return; @@ -134,7 +140,13 @@ pub fn sincos(x: f64, r_sin: *f64, r_cos: *f64) callconv(.c) void { // if |x| < 2**-27 * sqrt(2) if (ix < 0x3e46a09e) { // raise inexact if x != 0 and underflow if subnormal - if (common.want_float_exceptions) mem.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120); + if (common.want_float_exceptions) { + if (ix < 0x00100000) { + mem.doNotOptimizeAway(x / 0x1p120); + } else { + mem.doNotOptimizeAway(x + 0x1p120); + } + } r_sin.* = x; r_cos.* = 1.0; return; diff --git a/lib/compiler_rt/tan.zig b/lib/compiler_rt/tan.zig index 7a87cfecad9ee97319aed3069d9d1f98399111da..af8d68eb44447af300b2b1af992fc1e1492b6fca 100644 --- a/lib/compiler_rt/tan.zig +++ b/lib/compiler_rt/tan.zig @@ -51,7 +51,13 @@ pub fn tanf(x: f32) callconv(.c) f32 { if (ix <= 0x3f490fda) { // |x| ~<= pi/4 if (ix < 0x39800000) { // |x| < 2**-12 // raise inexact if x!=0 and underflow if subnormal - if (common.want_float_exceptions) mem.doNotOptimizeAway(if (ix < 0x00800000) x / 0x1p120 else x + 0x1p120); + if (common.want_float_exceptions) { + if (ix < 0x00800000) { + mem.doNotOptimizeAway(x / 0x1p120); + } else { + mem.doNotOptimizeAway(x + 0x1p120); + } + } return x; } return kernel.__tandf(x, false); @@ -89,7 +95,13 @@ pub fn tan(x: f64) callconv(.c) f64 { if (ix <= 0x3fe921fb) { if (ix < 0x3e400000) { // |x| < 2**-27 // raise inexact if x!=0 and underflow if subnormal - if (common.want_float_exceptions) mem.doNotOptimizeAway(if (ix < 0x00100000) x / 0x1p120 else x + 0x1p120); + if (common.want_float_exceptions) { + if (ix < 0x00100000) { + mem.doNotOptimizeAway(x / 0x1p120); + } else { + mem.doNotOptimizeAway(x + 0x1p120); + } + } return x; } return kernel.__tan(x, 0.0, false); -- 2.54.0