Skip to content

[Windows] Allow legalizing LDEXP_F128 and FREXP_F128 with libcalls #148326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

tgross35
Copy link
Contributor

@tgross35 tgross35 commented Jul 12, 2025

Attempting to use llvm.ldexp.f128 or llvm.frexp.f128 on Windows platforms results in a legalization failure. The needed functions ldexpf128 and frexpf128 aren't available on Windows, but it should be a failure to link that can be solved by adding a f128 library is preferable to crashing.

Note that due to 44744 this lowering actually incorrectly produces ldexpl and frexpl symbols, which aren't correct for _Float128 on Windows. This will be fixed separately.

This patch also adds {frexp,ldexp}.f128 tests on a couple architectures that were missing them, unrelated to the Windows-only change here.

Fixes: #144006

@llvmbot
Copy link
Member

llvmbot commented Jul 12, 2025

@llvm/pr-subscribers-backend-x86
@llvm/pr-subscribers-backend-mips
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-aarch64

Author: Trevor Gross (tgross35)

Changes

Attempting to use llvm.ldexp.f128 or llvm.frexp.f128 on Windows platforms results in a legalization failure. The needed functions ldexpf128 and frexpf128 aren't available on Windows, but it should be a failure to link that can be solved by adding a f128 library is preferable to crashing.

Note that due to [44744] this lowering actually incorrectly produces ldexpl and frexpl symbols, which aren't correct for _Float128 on Windows. This will be fixed separately.

This patch also adds {frexp,ldexp}.f128 tests on a couple architectures that were missing them, unrelated to the Windows-only change here.

Fixes: #144006
[44744]: #44744


Full diff: https://github.com/llvm/llvm-project/pull/148326.diff

8 Files Affected:

  • (modified) llvm/lib/IR/RuntimeLibcalls.cpp (-2)
  • (modified) llvm/test/CodeGen/AArch64/ldexp.ll (+13)
  • (modified) llvm/test/CodeGen/AArch64/llvm.frexp.ll (+67)
  • (modified) llvm/test/CodeGen/ARM/ldexp.ll (+8)
  • (modified) llvm/test/CodeGen/ARM/llvm.frexp.ll (+53)
  • (modified) llvm/test/CodeGen/Mips/ldexp.ll (+14)
  • (modified) llvm/test/CodeGen/X86/ldexp.ll (+57)
  • (modified) llvm/test/CodeGen/X86/llvm.frexp.ll (+133)
diff --git a/llvm/lib/IR/RuntimeLibcalls.cpp b/llvm/lib/IR/RuntimeLibcalls.cpp
index 64c9415c54d4d..2dbd6b2ef78ea 100644
--- a/llvm/lib/IR/RuntimeLibcalls.cpp
+++ b/llvm/lib/IR/RuntimeLibcalls.cpp
@@ -206,12 +206,10 @@ void RuntimeLibcallsInfo::initLibcalls(const Triple &TT,
   if (TT.isOSWindows() && !TT.isOSCygMing()) {
     setLibcallImpl(RTLIB::LDEXP_F32, RTLIB::Unsupported);
     setLibcallImpl(RTLIB::LDEXP_F80, RTLIB::Unsupported);
-    setLibcallImpl(RTLIB::LDEXP_F128, RTLIB::Unsupported);
     setLibcallImpl(RTLIB::LDEXP_PPCF128, RTLIB::Unsupported);
 
     setLibcallImpl(RTLIB::FREXP_F32, RTLIB::Unsupported);
     setLibcallImpl(RTLIB::FREXP_F80, RTLIB::Unsupported);
-    setLibcallImpl(RTLIB::FREXP_F128, RTLIB::Unsupported);
     setLibcallImpl(RTLIB::FREXP_PPCF128, RTLIB::Unsupported);
   }
 
diff --git a/llvm/test/CodeGen/AArch64/ldexp.ll b/llvm/test/CodeGen/AArch64/ldexp.ll
index 6019fa1490e3d..75822f512ffdb 100644
--- a/llvm/test/CodeGen/AArch64/ldexp.ll
+++ b/llvm/test/CodeGen/AArch64/ldexp.ll
@@ -147,4 +147,17 @@ entry:
   ret half %0
 }
 
+define fp128 @testExpf128(fp128 %val, i32 %a) {
+; SVE-LABEL: testExpf128:
+; SVE:       // %bb.0: // %entry
+; SVE-NEXT:    b ldexpl
+;
+; WINDOWS-LABEL: testExpf128:
+; WINDOWS:       // %bb.0: // %entry
+; WINDOWS-NEXT:    b ldexpl
+entry:
+  %ldexp = call fp128 @llvm.ldexp.f128.i32(fp128 %val, i32 %a)
+  ret fp128 %ldexp
+}
+
 declare half @llvm.ldexp.f16.i32(half, i32) memory(none)
diff --git a/llvm/test/CodeGen/AArch64/llvm.frexp.ll b/llvm/test/CodeGen/AArch64/llvm.frexp.ll
index 51400b96e8b77..03017035a3249 100644
--- a/llvm/test/CodeGen/AArch64/llvm.frexp.ll
+++ b/llvm/test/CodeGen/AArch64/llvm.frexp.ll
@@ -1431,6 +1431,73 @@ define <2 x i32> @test_frexp_v2f64_v2i32_only_use_exp(<2 x double> %a) {
   ret <2 x i32> %result.1
 }
 
+
+define { fp128, i32 } @test_frexp_f128_i32(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    add x0, sp, #12
+; CHECK-NEXT:    bl frexpl
+; CHECK-NEXT:    ldr w0, [sp, #12]
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
+;
+; WINDOWS-LABEL: test_frexp_f128_i32:
+; WINDOWS:       // %bb.0:
+; WINDOWS-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; WINDOWS-NEXT:    add x0, sp, #12
+; WINDOWS-NEXT:    bl frexpl
+; WINDOWS-NEXT:    ldr w0, [sp, #12]
+; WINDOWS-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; WINDOWS-NEXT:    ret
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  ret { fp128, i32 } %result
+}
+
+define fp128 @test_frexp_f128_i32_only_use_fract(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32_only_use_fract:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    add x0, sp, #12
+; CHECK-NEXT:    bl frexpl
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
+;
+; WINDOWS-LABEL: test_frexp_f128_i32_only_use_fract:
+; WINDOWS:       // %bb.0:
+; WINDOWS-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; WINDOWS-NEXT:    add x0, sp, #12
+; WINDOWS-NEXT:    bl frexpl
+; WINDOWS-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; WINDOWS-NEXT:    ret
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  %result.0 = extractvalue { fp128, i32 } %result, 0
+  ret fp128 %result.0
+}
+
+define i32 @test_frexp_f128_i32_only_use_exp(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32_only_use_exp:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    add x0, sp, #12
+; CHECK-NEXT:    bl frexpl
+; CHECK-NEXT:    ldr w0, [sp, #12]
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
+;
+; WINDOWS-LABEL: test_frexp_f128_i32_only_use_exp:
+; WINDOWS:       // %bb.0:
+; WINDOWS-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; WINDOWS-NEXT:    add x0, sp, #12
+; WINDOWS-NEXT:    bl frexpl
+; WINDOWS-NEXT:    ldr w0, [sp, #12]
+; WINDOWS-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; WINDOWS-NEXT:    ret
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  %result.0 = extractvalue { fp128, i32 } %result, 1
+  ret i32 %result.0
+}
+
 declare { float, i32 } @llvm.frexp.f32.i32(float) #0
 declare { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float>) #0
 declare { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float>) #0
diff --git a/llvm/test/CodeGen/ARM/ldexp.ll b/llvm/test/CodeGen/ARM/ldexp.ll
index cdf91eb902e05..1c2c35eaf4f37 100644
--- a/llvm/test/CodeGen/ARM/ldexp.ll
+++ b/llvm/test/CodeGen/ARM/ldexp.ll
@@ -55,4 +55,12 @@ entry:
   ret half %0
 }
 
+define fp128 @testExpf128(fp128 %val, i32 %a) {
+; LINUX: bl ldexpl
+; WINDOWS: bl ldexpl
+entry:
+  %0 = tail call fp128 @llvm.ldexp.f128.i32(fp128 %val, i32 %a)
+  ret fp128 %0
+}
+
 declare half @llvm.ldexp.f16.i32(half, i32) memory(none)
diff --git a/llvm/test/CodeGen/ARM/llvm.frexp.ll b/llvm/test/CodeGen/ARM/llvm.frexp.ll
index 43edb17fe1081..376426d701b3e 100644
--- a/llvm/test/CodeGen/ARM/llvm.frexp.ll
+++ b/llvm/test/CodeGen/ARM/llvm.frexp.ll
@@ -544,6 +544,59 @@ define <2 x i32> @test_frexp_v2f64_v2i32_only_use_exp(<2 x double> %a) {
   ret <2 x i32> %result.1
 }
 
+define { fp128, i32 } @test_frexp_f128_i32(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    push {r4, lr}
+; CHECK-NEXT:    sub sp, #8
+; CHECK-NEXT:    mov r12, r3
+; CHECK-NEXT:    ldr r3, [sp, #16]
+; CHECK-NEXT:    mov r4, r0
+; CHECK-NEXT:    add r0, sp, #4
+; CHECK-NEXT:    str r0, [sp]
+; CHECK-NEXT:    mov r0, r1
+; CHECK-NEXT:    mov r1, r2
+; CHECK-NEXT:    mov r2, r12
+; CHECK-NEXT:    bl frexpl
+; CHECK-NEXT:    ldr.w r12, [sp, #4]
+; CHECK-NEXT:    stm.w r4, {r0, r1, r2, r3, r12}
+; CHECK-NEXT:    add sp, #8
+; CHECK-NEXT:    pop {r4, pc}
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  ret { fp128, i32 } %result
+}
+
+define fp128 @test_frexp_f128_i32_only_use_fract(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32_only_use_fract:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    push {r7, lr}
+; CHECK-NEXT:    sub sp, #8
+; CHECK-NEXT:    add.w r12, sp, #4
+; CHECK-NEXT:    str.w r12, [sp]
+; CHECK-NEXT:    bl frexpl
+; CHECK-NEXT:    add sp, #8
+; CHECK-NEXT:    pop {r7, pc}
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  %result.0 = extractvalue { fp128, i32 } %result, 0
+  ret fp128 %result.0
+}
+
+define i32 @test_frexp_f128_i32_only_use_exp(fp128 %a) nounwind {
+; CHECK-LABEL: test_frexp_f128_i32_only_use_exp:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    push {r7, lr}
+; CHECK-NEXT:    sub sp, #8
+; CHECK-NEXT:    add.w r12, sp, #4
+; CHECK-NEXT:    str.w r12, [sp]
+; CHECK-NEXT:    bl frexpl
+; CHECK-NEXT:    ldr r0, [sp, #4]
+; CHECK-NEXT:    add sp, #8
+; CHECK-NEXT:    pop {r7, pc}
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  %result.0 = extractvalue { fp128, i32 } %result, 1
+  ret i32 %result.0
+}
+
 declare { float, i32 } @llvm.frexp.f32.i32(float) #0
 declare { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float>) #0
 declare { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float>) #0
diff --git a/llvm/test/CodeGen/Mips/ldexp.ll b/llvm/test/CodeGen/Mips/ldexp.ll
index 4debc6ddce4aa..29a9a2be734a2 100644
--- a/llvm/test/CodeGen/Mips/ldexp.ll
+++ b/llvm/test/CodeGen/Mips/ldexp.ll
@@ -161,6 +161,20 @@ define x86_fp80 @ldexp_f80(x86_fp80 %arg0, i32 %arg1) {
   ret x86_fp80 %ldexp
 }
 
+define fp128 @ldexp_f128(fp128 %arg0, i32 %arg1) nounwind {
+; SOFT-LABEL: ldexp_f128:
+; SOFT:       # %bb.0:
+; SOFT-NEXT:    addiu $sp, $sp, -32
+; SOFT-NEXT:    sw $ra, 28($sp) # 4-byte Folded Spill
+; SOFT-NEXT:    lw $1, 48($sp)
+; SOFT-NEXT:    jal ldexpl
+; SOFT-NEXT:    sw $1, 16($sp)
+; SOFT-NEXT:    lw $ra, 28($sp) # 4-byte Folded Reload
+; SOFT-NEXT:    jr $ra
+; SOFT-NEXT:    addiu $sp, $sp, 32
+  %ldexp = call fp128 @llvm.ldexp.f128.i32(fp128 %arg0, i32 %arg1)
+  ret fp128 %ldexp
+}
 
 declare double @llvm.ldexp.f64.i32(double, i32) #0
 declare float @llvm.ldexp.f32.i32(float, i32) #0
diff --git a/llvm/test/CodeGen/X86/ldexp.ll b/llvm/test/CodeGen/X86/ldexp.ll
index 859139463b7e3..5b4d4fec7aa32 100644
--- a/llvm/test/CodeGen/X86/ldexp.ll
+++ b/llvm/test/CodeGen/X86/ldexp.ll
@@ -623,6 +623,63 @@ define half @ldexp_f16(half %arg0, i32 %arg1) {
   ret half %ldexp
 }
 
+define fp128 @ldexp_f128(fp128 %arg0, i32 %arg1) {
+; X64-LABEL: ldexp_f128:
+; X64:       # %bb.0:
+; X64-NEXT:    jmp ldexpl@PLT # TAILCALL
+;
+; WIN64-LABEL: ldexp_f128:
+; WIN64:       # %bb.0:
+; WIN64-NEXT:    subq $56, %rsp
+; WIN64-NEXT:    .seh_stackalloc 56
+; WIN64-NEXT:    .seh_endprologue
+; WIN64-NEXT:    movaps (%rcx), %xmm0
+; WIN64-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-NEXT:    callq ldexpl
+; WIN64-NEXT:    nop
+; WIN64-NEXT:    .seh_startepilogue
+; WIN64-NEXT:    addq $56, %rsp
+; WIN64-NEXT:    .seh_endepilogue
+; WIN64-NEXT:    retq
+; WIN64-NEXT:    .seh_endproc
+;
+; WIN32-LABEL: ldexp_f128:
+; WIN32:       # %bb.0:
+; WIN32-NEXT:    pushl %ebp
+; WIN32-NEXT:    movl %esp, %ebp
+; WIN32-NEXT:    pushl %edi
+; WIN32-NEXT:    pushl %esi
+; WIN32-NEXT:    andl $-16, %esp
+; WIN32-NEXT:    subl $16, %esp
+; WIN32-NEXT:    movl 8(%ebp), %esi
+; WIN32-NEXT:    movl %esp, %eax
+; WIN32-NEXT:    pushl 28(%ebp)
+; WIN32-NEXT:    pushl 24(%ebp)
+; WIN32-NEXT:    pushl 20(%ebp)
+; WIN32-NEXT:    pushl 16(%ebp)
+; WIN32-NEXT:    pushl 12(%ebp)
+; WIN32-NEXT:    pushl %eax
+; WIN32-NEXT:    calll _ldexpl
+; WIN32-NEXT:    addl $24, %esp
+; WIN32-NEXT:    movl (%esp), %eax
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %edi
+; WIN32-NEXT:    movl %edi, 12(%esi)
+; WIN32-NEXT:    movl %edx, 8(%esi)
+; WIN32-NEXT:    movl %ecx, 4(%esi)
+; WIN32-NEXT:    movl %eax, (%esi)
+; WIN32-NEXT:    movl %esi, %eax
+; WIN32-NEXT:    leal -8(%ebp), %esp
+; WIN32-NEXT:    popl %esi
+; WIN32-NEXT:    popl %edi
+; WIN32-NEXT:    popl %ebp
+; WIN32-NEXT:    retl
+  %ldexp = call fp128 @llvm.ldexp.f128.i32(fp128 %arg0, i32 %arg1)
+  ret fp128 %ldexp
+}
+
 declare double @llvm.ldexp.f64.i32(double, i32) #0
 declare float @llvm.ldexp.f32.i32(float, i32) #0
 declare <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float>, <2 x i32>) #0
diff --git a/llvm/test/CodeGen/X86/llvm.frexp.ll b/llvm/test/CodeGen/X86/llvm.frexp.ll
index 8436c1052552e..ec99cda737fb4 100644
--- a/llvm/test/CodeGen/X86/llvm.frexp.ll
+++ b/llvm/test/CodeGen/X86/llvm.frexp.ll
@@ -624,6 +624,139 @@ define i32 @test_frexp_f64_i32_only_use_exp(double %a) {
 ;   ret <2 x i32> %result.1
 ; }
 
+define { fp128, i32 } @test_frexp_f128_i32(fp128 %a) nounwind {
+; X64-LABEL: test_frexp_f128_i32:
+; X64:       # %bb.0:
+; X64-NEXT:    pushq %rax
+; X64-NEXT:    leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT:    callq frexpl@PLT
+; X64-NEXT:    movl {{[0-9]+}}(%rsp), %eax
+; X64-NEXT:    popq %rcx
+; X64-NEXT:    retq
+;
+; WIN32-LABEL: test_frexp_f128_i32:
+; WIN32:       # %bb.0:
+; WIN32-NEXT:    pushl %ebp
+; WIN32-NEXT:    movl %esp, %ebp
+; WIN32-NEXT:    pushl %ebx
+; WIN32-NEXT:    pushl %edi
+; WIN32-NEXT:    pushl %esi
+; WIN32-NEXT:    andl $-16, %esp
+; WIN32-NEXT:    subl $48, %esp
+; WIN32-NEXT:    movl 8(%ebp), %esi
+; WIN32-NEXT:    leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT:    leal {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT:    pushl %eax
+; WIN32-NEXT:    pushl 24(%ebp)
+; WIN32-NEXT:    pushl 20(%ebp)
+; WIN32-NEXT:    pushl 16(%ebp)
+; WIN32-NEXT:    pushl 12(%ebp)
+; WIN32-NEXT:    pushl %ecx
+; WIN32-NEXT:    calll _frexpl
+; WIN32-NEXT:    addl $24, %esp
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %edi
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %ebx
+; WIN32-NEXT:    movl %ebx, 12(%esi)
+; WIN32-NEXT:    movl %edi, 8(%esi)
+; WIN32-NEXT:    movl %edx, 4(%esi)
+; WIN32-NEXT:    movl %ecx, (%esi)
+; WIN32-NEXT:    movl %eax, 16(%esi)
+; WIN32-NEXT:    movl %esi, %eax
+; WIN32-NEXT:    leal -12(%ebp), %esp
+; WIN32-NEXT:    popl %esi
+; WIN32-NEXT:    popl %edi
+; WIN32-NEXT:    popl %ebx
+; WIN32-NEXT:    popl %ebp
+; WIN32-NEXT:    retl
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  ret { fp128, i32 } %result
+}
+
+define fp128 @test_frexp_f128_i32_only_use_fract(fp128 %a) nounwind {
+; X64-LABEL: test_frexp_f128_i32_only_use_fract:
+; X64:       # %bb.0:
+; X64-NEXT:    pushq %rax
+; X64-NEXT:    leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT:    callq frexpl@PLT
+; X64-NEXT:    popq %rax
+; X64-NEXT:    retq
+;
+; WIN32-LABEL: test_frexp_f128_i32_only_use_fract:
+; WIN32:       # %bb.0:
+; WIN32-NEXT:    pushl %ebp
+; WIN32-NEXT:    movl %esp, %ebp
+; WIN32-NEXT:    pushl %edi
+; WIN32-NEXT:    pushl %esi
+; WIN32-NEXT:    andl $-16, %esp
+; WIN32-NEXT:    subl $32, %esp
+; WIN32-NEXT:    movl 8(%ebp), %esi
+; WIN32-NEXT:    leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT:    movl %esp, %ecx
+; WIN32-NEXT:    pushl %eax
+; WIN32-NEXT:    pushl 24(%ebp)
+; WIN32-NEXT:    pushl 20(%ebp)
+; WIN32-NEXT:    pushl 16(%ebp)
+; WIN32-NEXT:    pushl 12(%ebp)
+; WIN32-NEXT:    pushl %ecx
+; WIN32-NEXT:    calll _frexpl
+; WIN32-NEXT:    addl $24, %esp
+; WIN32-NEXT:    movl (%esp), %eax
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %edx
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %edi
+; WIN32-NEXT:    movl %edi, 12(%esi)
+; WIN32-NEXT:    movl %edx, 8(%esi)
+; WIN32-NEXT:    movl %ecx, 4(%esi)
+; WIN32-NEXT:    movl %eax, (%esi)
+; WIN32-NEXT:    movl %esi, %eax
+; WIN32-NEXT:    leal -8(%ebp), %esp
+; WIN32-NEXT:    popl %esi
+; WIN32-NEXT:    popl %edi
+; WIN32-NEXT:    popl %ebp
+; WIN32-NEXT:    retl
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  %result.0 = extractvalue { fp128, i32 } %result, 0
+  ret fp128 %result.0
+}
+
+define i32 @test_frexp_f128_i32_only_use_exp(fp128 %a) nounwind {
+; X64-LABEL: test_frexp_f128_i32_only_use_exp:
+; X64:       # %bb.0:
+; X64-NEXT:    pushq %rax
+; X64-NEXT:    leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT:    callq frexpl@PLT
+; X64-NEXT:    movl {{[0-9]+}}(%rsp), %eax
+; X64-NEXT:    popq %rcx
+; X64-NEXT:    retq
+;
+; WIN32-LABEL: test_frexp_f128_i32_only_use_exp:
+; WIN32:       # %bb.0:
+; WIN32-NEXT:    pushl %ebp
+; WIN32-NEXT:    movl %esp, %ebp
+; WIN32-NEXT:    andl $-16, %esp
+; WIN32-NEXT:    subl $48, %esp
+; WIN32-NEXT:    leal {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT:    leal {{[0-9]+}}(%esp), %ecx
+; WIN32-NEXT:    pushl %eax
+; WIN32-NEXT:    pushl 20(%ebp)
+; WIN32-NEXT:    pushl 16(%ebp)
+; WIN32-NEXT:    pushl 12(%ebp)
+; WIN32-NEXT:    pushl 8(%ebp)
+; WIN32-NEXT:    pushl %ecx
+; WIN32-NEXT:    calll _frexpl
+; WIN32-NEXT:    addl $24, %esp
+; WIN32-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; WIN32-NEXT:    movl %ebp, %esp
+; WIN32-NEXT:    popl %ebp
+; WIN32-NEXT:    retl
+  %result = call { fp128, i32 } @llvm.frexp.f128.i32(fp128 %a)
+  %result.0 = extractvalue { fp128, i32 } %result, 1
+  ret i32 %result.0
+}
+
 declare { float, i32 } @llvm.frexp.f32.i32(float) #0
 declare { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x float>) #0
 declare { <4 x float>, <4 x i32> } @llvm.frexp.v4f32.v4i32(<4 x float>) #0

@tgross35
Copy link
Contributor Author

This needs #76558 or something similar to actually produce a correct libcall here, it should be ldexpf128 rather than ldexpl. However, all other fp128 libcalls have a similar incorrect lowering, so it seems reasonable to consolidate everything into one bug than having a crash specifically for frexp/ldexp.

@nikic nikic requested a review from efriedma-quic July 12, 2025 09:59
Attempting to use `llvm.ldexp.f128` or `llvm.frexp.f128` on Windows
platforms results in a legalization failure. The needed functions
`ldexpf128` and `frexpf128` aren't available on Windows, but it should
be a failure to link that can be solved by adding a `f128` library is
preferable to crashing.

Note that due to [44744] this lowering actually incorrectly produces
`ldexpl` and `frexpl` symbols, which aren't correct for `_Float128` on
Windows. This will be fixed separately.

This patch also adds `{frexp,ldexp}.f128` tests on a couple
architectures that were missing them, unrelated to the Windows-only
change here.

[44744]: llvm#44744

Fixes: llvm#144006
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

llvm.ldexp.f128 lowering hits assertion failure on arm64 windows
2 participants