Try to restrict the known range of the exponent result of llvm.frexp
based on dominating conditions. Identify comparisons that imply the
incoming value cannot introduce an overflow in a downstream ldexp
use. This pattern appears in the implementation of some complex math
functions and allows finite only math to prune out more edge case
paths.
One attributor test for ldexp regresses due to the switch from
computeKnownBits to computeConstantRange. computeConstantRange
does not try to handle non-splat vector constants for the binary
operators.
As a side effect, this also improves knowing that ldexp can't
introduce overflow for the 0 case.
The patch infers the exponent range of llvm.frexp from a dominating fabs(x) <= 1.0 condition by using ilogb(1.0) == 0 as the maximum exponent. This is off by one for nonzero finite inputs: frexp(1.0) returns mantissa 0.5 and exponent 1. The reduced test makes this observable with icmp sle i32 %e, 0. InstCombine transforms the guarded block to ret i1 true, but Alive2 produces the counterexample x = 1.0, where the original returns false.
Alive2 verification result:
alive-tv -passes=instcombine%x = 1.0f%e = 1, %r = falsetrueThe weakness is in computeKnownExponentRangeFromContext: it treats ilogb(K) as an inclusive upper bound for the frexp exponent when the path implies fabs(x) <= K. For finite nonzero values, frexp's exponent is ilogb(x) + 1, so inclusive/equality predicates at the boundary are unsound. The same issue likely affects oeq/ueq and non-power-of-two constants such as 0.75, and zero requires special handling because ilogb(0.0) returns an error sentinel while frexp(0.0) returns exponent 0.
define i1 @bug_ole_one_exp_le0(float nofpclass(nan inf) %x) {
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp ole float %abs, 1.0
br i1 %c, label %small, label %large
small:
%fx = call { float, i32 } @llvm.frexp.f32.i32(float %x)
%e = extractvalue { float, i32 } %fx, 1
%r = icmp sle i32 %e, 0
ret i1 %r
large:
ret i1 false
}
declare float @llvm.fabs.f32(float) #0
declare { float, i32 } @llvm.frexp.f32.i32(float) #0
attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }; ModuleID = '/data/tmp/tmp0upfw8ci/orig.ll'
source_filename = "/data/tmp/tmp0upfw8ci/orig.ll"
define i1 @bug_ole_one_exp_le0(float nofpclass(nan inf) %x) {
entry:
%abs = call float @llvm.fabs.f32(float %x)
%c = fcmp ugt float %abs, 1.000000e+00
br i1 %c, label %large, label %small
small: ; preds = %entry
ret i1 true
large: ; preds = %entry
ret i1 false
}
; Function Attrs: nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none)
declare float @llvm.fabs.f32(float) #0
; Function Attrs: nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none)
declare { float, i32 } @llvm.frexp.f32.i32(float) #0
attributes #0 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
----------------------------------------
define i1 @bug_ole_one_exp_le0(float nofpclass(519) %x) {
entry:
%abs = fabs float nofpclass(519) %x
%c = fcmp ole float %abs, 1e+00
br i1 %c, label %small, label %large
large:
ret i1 0
small:
%fx = frexp float nofpclass(519) %x
%e = extractvalue {float, i32} %fx, 1
%r = icmp sle i32 %e, 0
ret i1 %r
}
=>
define i1 @bug_ole_one_exp_le0(float nofpclass(519) %x) {
entry:
%abs = fabs float nofpclass(519) %x
%c = fcmp ugt float %abs, 1e+00
br i1 %c, label %large, label %small
small:
ret i1 1
large:
ret i1 0
}
Transformation doesn't verify!
ERROR: Value mismatch
Example:
float nofpclass(519) %x = #x3f800000 (1e+00)
Source:
float %abs = #x3f800000 (1e+00)
i1 %c = #x1 (1)
>> Jump to %small
{float, i32} %fx = { #x3f000000 (5e-01), #x00000001 (1) }
i32 %e = #x00000001 (1)
i1 %r = #x0 (0)
Target:
float %abs = #x3f800000 (1e+00)
i1 %c = #x0 (0)
>> Jump to %small
Source value: #x0 (0)
Target value: #x1 (1)
Summary:
0 correct transformations
1 incorrect transformations
0 failed-to-prove transformations
0 Alive2 errors