Skip to content

RuntimeLibcalls: Add sincos to the RISCV runtime libcall set - #215115

Open
arsenm wants to merge 2 commits into
mainfrom
users/arsenm/runtime-libcalls/riscv-sincos
Open

RuntimeLibcalls: Add sincos to the RISCV runtime libcall set#215115
arsenm wants to merge 2 commits into
mainfrom
users/arsenm/runtime-libcalls/riscv-sincos

Conversation

@arsenm

@arsenm arsenm commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Inspection of the glibc sources suggests this is generically
available, with the target variance being for long double support.
The set of library functions is a large historical mess I'm attempting
to untangle. The traditional system had a large set of defaulted calls,
but sincos was a case which was explicitly enabled, and I'm assuming
riscv just never got around to adding it. It will be easier to
reorganize the library functions if synthetic architectural glibc
variance is eliminated.

Co-authored-by: Claude (Claude-Opus-4.8) noreply@anthropic.com

Inspection of the glibc sources suggests this is generically
available, with the target variance being for long double support.
The set of library functions is a large historical mess I'm attempting
to untangle. The traditional system had a large set of defaulted calls,
but sincos was a case which was explicitly enabled, and I'm assuming
riscv just never got around to adding it. It will be easier to
reorganize the library functions if synthetic architectural glibc
variance is eliminated.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply@anthropic.com>

arsenm commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@llvmorg-github-actions

llvmorg-github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-risc-v

Author: Matt Arsenault (arsenm)

Changes

Inspection of the glibc sources suggests this is generically
available, with the target variance being for long double support.
The set of library functions is a large historical mess I'm attempting
to untangle. The traditional system had a large set of defaulted calls,
but sincos was a case which was explicitly enabled, and I'm assuming
riscv just never got around to adding it. It will be easier to
reorganize the library functions if synthetic architectural glibc
variance is eliminated.

Co-authored-by: Claude (Claude-Opus-4.8) <noreply@anthropic.com>


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

3 Files Affected:

  • (modified) llvm/include/llvm/IR/RuntimeLibcalls.td (+1)
  • (added) llvm/test/CodeGen/RISCV/llvm.sincos.ll (+136)
  • (modified) llvm/test/CodeGen/RISCV/sincos-expansion.ll (+16-24)
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index b0d9663cca395..1b504c8de6add 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -3185,6 +3185,7 @@ def RISCVSystemLibrary
     : SystemRuntimeLibrary<isRISCV,
       (add DefaultRuntimeLibcallImpls,
            exp10f, exp10, exp10l_f128,
+           LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,
            __riscv_flush_icache,
            LibcallImpls<(add Int128RTLibcalls), isRISCV64>,
            DefaultStackProtector,
diff --git a/llvm/test/CodeGen/RISCV/llvm.sincos.ll b/llvm/test/CodeGen/RISCV/llvm.sincos.ll
new file mode 100644
index 0000000000000..bb4935ba885ab
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/llvm.sincos.ll
@@ -0,0 +1,136 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64-linux-gnu -mattr=+d < %s | FileCheck %s --check-prefix=RV64
+; RUN: llc -mtriple=riscv32-linux-gnu -mattr=+d < %s | FileCheck %s --check-prefix=RV32
+
+; Test the llvm.sincos intrinsic lowering to the sincos libcalls, which glibc
+; provides (sincosf/sincos/sincosl). RISCV long double is fp128, so f128 uses
+; sincosl.
+
+define { float, float } @test_sincos_f32(float %a) nounwind {
+; RV64-LABEL: test_sincos_f32:
+; RV64:       # %bb.0:
+; RV64-NEXT:    addi sp, sp, -16
+; RV64-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT:    addi a0, sp, 4
+; RV64-NEXT:    mv a1, sp
+; RV64-NEXT:    call sincosf
+; RV64-NEXT:    flw fa0, 4(sp)
+; RV64-NEXT:    flw fa1, 0(sp)
+; RV64-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT:    addi sp, sp, 16
+; RV64-NEXT:    ret
+;
+; RV32-LABEL: test_sincos_f32:
+; RV32:       # %bb.0:
+; RV32-NEXT:    addi sp, sp, -16
+; RV32-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT:    addi a0, sp, 8
+; RV32-NEXT:    addi a1, sp, 4
+; RV32-NEXT:    call sincosf
+; RV32-NEXT:    flw fa0, 8(sp)
+; RV32-NEXT:    flw fa1, 4(sp)
+; RV32-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT:    addi sp, sp, 16
+; RV32-NEXT:    ret
+  %result = call { float, float } @llvm.sincos.f32(float %a)
+  ret { float, float } %result
+}
+
+define { double, double } @test_sincos_f64(double %a) nounwind {
+; RV64-LABEL: test_sincos_f64:
+; RV64:       # %bb.0:
+; RV64-NEXT:    addi sp, sp, -32
+; RV64-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
+; RV64-NEXT:    addi a0, sp, 16
+; RV64-NEXT:    addi a1, sp, 8
+; RV64-NEXT:    call sincos
+; RV64-NEXT:    fld fa0, 16(sp)
+; RV64-NEXT:    fld fa1, 8(sp)
+; RV64-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
+; RV64-NEXT:    addi sp, sp, 32
+; RV64-NEXT:    ret
+;
+; RV32-LABEL: test_sincos_f64:
+; RV32:       # %bb.0:
+; RV32-NEXT:    addi sp, sp, -32
+; RV32-NEXT:    sw ra, 28(sp) # 4-byte Folded Spill
+; RV32-NEXT:    addi a0, sp, 16
+; RV32-NEXT:    addi a1, sp, 8
+; RV32-NEXT:    call sincos
+; RV32-NEXT:    fld fa0, 16(sp)
+; RV32-NEXT:    fld fa1, 8(sp)
+; RV32-NEXT:    lw ra, 28(sp) # 4-byte Folded Reload
+; RV32-NEXT:    addi sp, sp, 32
+; RV32-NEXT:    ret
+  %result = call { double, double } @llvm.sincos.f64(double %a)
+  ret { double, double } %result
+}
+
+define { fp128, fp128 } @test_sincos_f128(fp128 %a) nounwind {
+; RV64-LABEL: test_sincos_f128:
+; RV64:       # %bb.0:
+; RV64-NEXT:    addi sp, sp, -48
+; RV64-NEXT:    sd ra, 40(sp) # 8-byte Folded Spill
+; RV64-NEXT:    sd s0, 32(sp) # 8-byte Folded Spill
+; RV64-NEXT:    mv a4, a2
+; RV64-NEXT:    mv s0, a0
+; RV64-NEXT:    addi a2, sp, 16
+; RV64-NEXT:    mv a3, sp
+; RV64-NEXT:    mv a0, a1
+; RV64-NEXT:    mv a1, a4
+; RV64-NEXT:    call sincosl
+; RV64-NEXT:    ld a0, 16(sp)
+; RV64-NEXT:    ld a1, 24(sp)
+; RV64-NEXT:    ld a2, 0(sp)
+; RV64-NEXT:    ld a3, 8(sp)
+; RV64-NEXT:    sd a0, 0(s0)
+; RV64-NEXT:    sd a1, 8(s0)
+; RV64-NEXT:    sd a2, 16(s0)
+; RV64-NEXT:    sd a3, 24(s0)
+; RV64-NEXT:    ld ra, 40(sp) # 8-byte Folded Reload
+; RV64-NEXT:    ld s0, 32(sp) # 8-byte Folded Reload
+; RV64-NEXT:    addi sp, sp, 48
+; RV64-NEXT:    ret
+;
+; RV32-LABEL: test_sincos_f128:
+; RV32:       # %bb.0:
+; RV32-NEXT:    addi sp, sp, -80
+; RV32-NEXT:    sw ra, 76(sp) # 4-byte Folded Spill
+; RV32-NEXT:    sw s0, 72(sp) # 4-byte Folded Spill
+; RV32-NEXT:    lw a4, 0(a1)
+; RV32-NEXT:    lw a5, 4(a1)
+; RV32-NEXT:    lw a6, 8(a1)
+; RV32-NEXT:    lw a7, 12(a1)
+; RV32-NEXT:    mv s0, a0
+; RV32-NEXT:    addi a0, sp, 24
+; RV32-NEXT:    addi a1, sp, 8
+; RV32-NEXT:    addi a2, sp, 56
+; RV32-NEXT:    addi a3, sp, 40
+; RV32-NEXT:    sw a4, 8(sp)
+; RV32-NEXT:    sw a5, 12(sp)
+; RV32-NEXT:    sw a6, 16(sp)
+; RV32-NEXT:    sw a7, 20(sp)
+; RV32-NEXT:    call sincosl
+; RV32-NEXT:    lw a0, 40(sp)
+; RV32-NEXT:    lw a1, 44(sp)
+; RV32-NEXT:    lw a2, 48(sp)
+; RV32-NEXT:    lw a3, 52(sp)
+; RV32-NEXT:    lw a4, 56(sp)
+; RV32-NEXT:    lw a5, 60(sp)
+; RV32-NEXT:    lw a6, 64(sp)
+; RV32-NEXT:    lw a7, 68(sp)
+; RV32-NEXT:    sw a0, 16(s0)
+; RV32-NEXT:    sw a1, 20(s0)
+; RV32-NEXT:    sw a2, 24(s0)
+; RV32-NEXT:    sw a3, 28(s0)
+; RV32-NEXT:    sw a4, 0(s0)
+; RV32-NEXT:    sw a5, 4(s0)
+; RV32-NEXT:    sw a6, 8(s0)
+; RV32-NEXT:    sw a7, 12(s0)
+; RV32-NEXT:    lw ra, 76(sp) # 4-byte Folded Reload
+; RV32-NEXT:    lw s0, 72(sp) # 4-byte Folded Reload
+; RV32-NEXT:    addi sp, sp, 80
+; RV32-NEXT:    ret
+  %result = call { fp128, fp128 } @llvm.sincos.f128(fp128 %a)
+  ret { fp128, fp128 } %result
+}
diff --git a/llvm/test/CodeGen/RISCV/sincos-expansion.ll b/llvm/test/CodeGen/RISCV/sincos-expansion.ll
index 6f37d67134cb3..606768a8e2f53 100644
--- a/llvm/test/CodeGen/RISCV/sincos-expansion.ll
+++ b/llvm/test/CodeGen/RISCV/sincos-expansion.ll
@@ -7,20 +7,16 @@
 define float @test_sincos_f32(float %f) nounwind {
 ; CHECK-LABEL: test_sincos_f32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    addi sp, sp, -32
-; CHECK-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
-; CHECK-NEXT:    fsd fs0, 16(sp) # 8-byte Folded Spill
-; CHECK-NEXT:    fsd fs1, 8(sp) # 8-byte Folded Spill
-; CHECK-NEXT:    fmv.s fs0, fa0
-; CHECK-NEXT:    call sinf
-; CHECK-NEXT:    fmv.s fs1, fa0
-; CHECK-NEXT:    fmv.s fa0, fs0
-; CHECK-NEXT:    call cosf
-; CHECK-NEXT:    fadd.s fa0, fs1, fa0
-; CHECK-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
-; CHECK-NEXT:    fld fs0, 16(sp) # 8-byte Folded Reload
-; CHECK-NEXT:    fld fs1, 8(sp) # 8-byte Folded Reload
-; CHECK-NEXT:    addi sp, sp, 32
+; CHECK-NEXT:    addi sp, sp, -16
+; CHECK-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; CHECK-NEXT:    addi a0, sp, 4
+; CHECK-NEXT:    mv a1, sp
+; CHECK-NEXT:    call sincosf
+; CHECK-NEXT:    flw fa5, 0(sp)
+; CHECK-NEXT:    flw fa4, 4(sp)
+; CHECK-NEXT:    fadd.s fa0, fa4, fa5
+; CHECK-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; CHECK-NEXT:    addi sp, sp, 16
 ; CHECK-NEXT:    ret
   %sin = call float @sinf(float %f) readnone
   %cos = call float @cosf(float %f) readnone
@@ -57,17 +53,13 @@ define double @test_sincos_f64(double %f) nounwind {
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    addi sp, sp, -32
 ; CHECK-NEXT:    sd ra, 24(sp) # 8-byte Folded Spill
-; CHECK-NEXT:    fsd fs0, 16(sp) # 8-byte Folded Spill
-; CHECK-NEXT:    fsd fs1, 8(sp) # 8-byte Folded Spill
-; CHECK-NEXT:    fmv.d fs0, fa0
-; CHECK-NEXT:    call sin
-; CHECK-NEXT:    fmv.d fs1, fa0
-; CHECK-NEXT:    fmv.d fa0, fs0
-; CHECK-NEXT:    call cos
-; CHECK-NEXT:    fadd.d fa0, fs1, fa0
+; CHECK-NEXT:    addi a0, sp, 16
+; CHECK-NEXT:    addi a1, sp, 8
+; CHECK-NEXT:    call sincos
+; CHECK-NEXT:    fld fa5, 8(sp)
+; CHECK-NEXT:    fld fa4, 16(sp)
+; CHECK-NEXT:    fadd.d fa0, fa4, fa5
 ; CHECK-NEXT:    ld ra, 24(sp) # 8-byte Folded Reload
-; CHECK-NEXT:    fld fs0, 16(sp) # 8-byte Folded Reload
-; CHECK-NEXT:    fld fs1, 8(sp) # 8-byte Folded Reload
 ; CHECK-NEXT:    addi sp, sp, 32
 ; CHECK-NEXT:    ret
   %sin = call double @sin(double %f) readnone

@arsenm
arsenm marked this pull request as ready for review August 9, 2026 17:06
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; RUN: llc -mtriple=riscv64-linux-gnu -mattr=+d -verify-machineinstrs -o - %s | FileCheck %s

; Test adapted from AArch64. Demonstrates the cases where sin and cos libcalls

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose and behaviour of this test seem a bit puzzling to me. The test suggests that it is meant to demonstrate the cases where sin+cos are combined into sincos, but prior to this change, that didn't happen for any cases. And with this change, the 32/64 bit versions are combined when the readnone attribute is present, but this doesn't happen for the 128 bit version. Why?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

f128 / long double are more broken than not across most targets. I'm separately working on fixing that (#214944 is part)

: SystemRuntimeLibrary<isRISCV,
(add DefaultRuntimeLibcallImpls,
exp10f, exp10, exp10l_f128,
LibmHasSinCosF32, LibmHasSinCosF64, LibmHasSinCosF128,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity - if these are generally available on all targets, why not just get rid of the predicates?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's available on all targets, with glibc not any libc

Comment thread llvm/test/CodeGen/RISCV/llvm.sincos.ll Outdated
Co-authored-by: Craig Topper <craig.topper@sifive.com>

@lenary lenary left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

This was proposed before in #168708 - which seemed to have stalled due to f128 problems, but you've acknowledged there's more to do there to fix those.

@topperc topperc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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.

5 participants