[PATCH 37/59] math: Fix modf{f} build on clang

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Thu Oct 23 14:00:37 GMT 2025



On 22/10/25 18:51, H.J. Lu wrote:
> On Thu, Oct 23, 2025 at 12:26 AM Adhemerval Zanella Netto
> <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 20/10/25 19:09, H.J. Lu wrote:
>>> On Tue, Oct 21, 2025 at 1:58 AM Adhemerval Zanella Netto
>>> <adhemerval.zanella@linaro.org> wrote:
>>>>
>>>>
>>>>
>>>> On 18/10/25 03:28, H.J. Lu wrote:
>>>>> On Sat, Oct 18, 2025 at 3:58 AM Adhemerval Zanella
>>>>> <adhemerval.zanella@linaro.org> wrote:
>>>>>>
>>>>>> We need to disable the internal optimize for math builtins
>>>>>> (NO_MATH_REDIRECT) for the USE_TRUNC_BUILTIN case.
>>>>>
>>>>> Does it change GCC codegen?
>>>>
>>>> No, both with clang and gcc either the builtin is issues (on
>>>> aarch64 for instance) or the internal symbol is called.
>>>
>>> Why does NO_MATH_REDIRECT make no difference here?
>>> Why is this macro needed?
>>
>> With clang the internal optimization to avoid PLT usage for symbol
>> that might be inline will make clang to *not* emit the builtin:
>>
>> include/math.h
>>
>> 177 MATH_REDIRECT (trunc, "__", MATH_REDIRECT_UNARY_ARGS)
>>
>> Without NO_MATH_REDIRECT, which disables it; build with clang
>> fails:
>>
>>>>> referenced by s_modf.c:34 ([...]/../sysdeps/ieee754/dbl-64/s_modf.c:34)
>>>>>               [...]/aarch64-linux-gnu-clang-21/libc_pic.os.clean:(__modf)
>> clang: error: linker command failed with exit code 1 (use -v to see invocation)
>>
>> gcc is not affected by this, it will emit the 'frintz' regardless
>> of MATH_REDIRECT.
> 
> So clang won't inline trunc without NO_MATH_REDIRECT?
> Why isn't gcc impacted?
> 

Here is a simplified testcase:

  extern float truncf (float __x) __attribute__ ((__nothrow__ )) __attribute__ ((__const__)); 
  extern float __truncf (float __x) __attribute__ ((__nothrow__ )) __attribute__ ((__const__));

  #ifdef ALIAS
  float (truncf) (float) asm ("__truncf");
  #endif

  float foo (float x) { return truncf (x); }

If ALIAS is defined clang will always emit a __truncf call. It seems that alias
are applied as early as possible, from -emit-llvm -mllvm -print-after-all
I see that ForceFunctionAttrsPass is the first used and it already issues
a "%call = call float @__truncf(float noundef %0) #2".

Without ALIAS defined, the first IR is already created with a builtin:

  %1 = call float @llvm.experimental.constrained.trunc.f32(float %0, metadata !"fpexcept.strict") #2

The gcc does not seems to expand the alias, or have an optimization to
to not consider it.  With -fdump-tree-all -fdump-rtl-all I see that the
143t.pre pass have:

---
[...]
ANYTHING = { ANYTHING }
ESCAPED = { ESCAPED NONLOCAL }
NONLOCAL = { ESCAPED NONLOCAL }
STOREDANYTHING = { }
INTEGER = { ANYTHING }
x = { NONLOCAL }
*__truncf = { }
callescape(10) = { NONLOCAL } same as x
CALLUSED(11) = { NONLOCAL } same as x
CALLCLOBBERED(12) = { }
callarg(13) = { NONLOCAL }
_2 = { NONLOCAL } same as callarg(13)
foo = { }


Alias information for foo

Aliased symbols


Call clobber information
[...]
float foo (float x)
{
  float _2;

  <bb 2> [local count: 1073741824]:
  _2 = truncf (x_1(D));
  return _2;

}
---


And later on 255r.expand I see that it has selected the expected instruction
since the alias is ignored:

;;
;; Full RTL generated for this function:
;;
(note 1 0 4 NOTE_INSN_DELETED)
(note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
(insn 2 4 3 2 (set (reg/v:SF 93 [ x ])
        (reg:SF 32 v0 [ x ])) "t.c":6:21 -1
     (nil))
(note 3 2 6 2 NOTE_INSN_FUNCTION_BEG)
(insn 6 3 10 2 (set (reg:SF 92 [ <retval> ])
        (unspec:SF [
                (reg/v:SF 93 [ x ])
            ] UNSPEC_FRINTZ)) "t.c":6:30 -1
     (nil))
(insn 10 6 11 2 (set (reg/i:SF 32 v0)
        (reg:SF 92 [ <retval> ])) "t.c":6:42 -1
     (nil))
(insn 11 10 0 2 (use (reg/i:SF 32 v0)) "t.c":6:42 -1
     (nil))



More information about the Libc-alpha mailing list