From aceabe91078c039ed263281720ca2fd6e9cc11ea Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Tue, 8 Jul 2025 15:35:25 +0900 Subject: [PATCH] DAG: Fall back to separate sin and cos when softening sincos Fix asserting in the error case. --- .../include/llvm/CodeGen/RuntimeLibcallUtil.h | 8 + .../SelectionDAG/LegalizeFloatTypes.cpp | 43 +++- llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 2 +- llvm/lib/CodeGen/TargetLoweringBase.cpp | 8 + llvm/test/CodeGen/AVR/sincos-soften-error.ll | 9 + llvm/test/CodeGen/WebAssembly/llvm.sincos.ll | 236 +++++++++++++++++- 6 files changed, 288 insertions(+), 18 deletions(-) create mode 100644 llvm/test/CodeGen/AVR/sincos-soften-error.ll diff --git a/llvm/include/llvm/CodeGen/RuntimeLibcallUtil.h b/llvm/include/llvm/CodeGen/RuntimeLibcallUtil.h index 451459eda25e9..f865b8c1583f8 100644 --- a/llvm/include/llvm/CodeGen/RuntimeLibcallUtil.h +++ b/llvm/include/llvm/CodeGen/RuntimeLibcallUtil.h @@ -68,6 +68,14 @@ LLVM_ABI Libcall getLDEXP(EVT RetVT); /// UNKNOWN_LIBCALL if there is none. LLVM_ABI Libcall getFREXP(EVT RetVT); +/// Return the SIN_* value for the given types, or UNKNOWN_LIBCALL if there is +/// none. +LLVM_ABI Libcall getSIN(EVT RetVT); + +/// Return the COS_* value for the given types, or UNKNOWN_LIBCALL if there is +/// none. +LLVM_ABI Libcall getCOS(EVT RetVT); + /// getSINCOS - Return the SINCOS_* value for the given types, or /// UNKNOWN_LIBCALL if there is none. LLVM_ABI Libcall getSINCOS(EVT RetVT); diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp index 14bbf6a2f5220..2cad36eff9c88 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeFloatTypes.cpp @@ -794,7 +794,7 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_FFREXP(SDNode *N) { return ReturnVal; } -SDValue DAGTypeLegalizer::SoftenFloatRes_UnaryWithTwoFPResults( +bool DAGTypeLegalizer::SoftenFloatRes_UnaryWithTwoFPResults( SDNode *N, RTLIB::Libcall LC, std::optional CallRetResNo) { assert(!N->isStrictFPOpcode() && "strictfp not implemented"); EVT VT = N->getValueType(0); @@ -803,7 +803,7 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_UnaryWithTwoFPResults( "expected both return values to have the same type"); if (!TLI.getLibcallName(LC)) - return SDValue(); + return false; EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT); @@ -849,17 +849,46 @@ SDValue DAGTypeLegalizer::SoftenFloatRes_UnaryWithTwoFPResults( SetSoftenedFloat(SDValue(N, ResNum), CreateStackLoad(SlackSlot)); } - return SDValue(); + return true; } SDValue DAGTypeLegalizer::SoftenFloatRes_FSINCOS(SDNode *N) { - return SoftenFloatRes_UnaryWithTwoFPResults( - N, RTLIB::getSINCOS(N->getValueType(0))); + EVT VT = N->getValueType(0); + if (SoftenFloatRes_UnaryWithTwoFPResults(N, RTLIB::getSINCOS(VT))) + return SDValue(); + + // Fall back on softening the separate sin and cos calls if available. + RTLIB::Libcall SinLC = RTLIB::getSIN(VT); + RTLIB::Libcall CosLC = RTLIB::getCOS(VT); + + SDValue SoftSin, SoftCos; + if (!TLI.getLibcallName(SinLC) || !TLI.getLibcallName(CosLC)) { + DAG.getContext()->emitError("do not know how to soften fsincos"); + + EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT); + SoftSin = SoftCos = DAG.getPOISON(NVT); + } else { + SoftSin = SoftenFloatRes_Unary(N, SinLC); + SoftCos = SoftenFloatRes_Unary(N, CosLC); + } + + SetSoftenedFloat(SDValue(N, 0), SoftSin); + SetSoftenedFloat(SDValue(N, 1), SoftCos); + return SDValue(); } SDValue DAGTypeLegalizer::SoftenFloatRes_FMODF(SDNode *N) { - return SoftenFloatRes_UnaryWithTwoFPResults( - N, RTLIB::getMODF(N->getValueType(0)), /*CallRetResNo=*/0); + EVT VT = N->getValueType(0); + if (SoftenFloatRes_UnaryWithTwoFPResults(N, RTLIB::getMODF(VT), + /*CallRetResNo=*/0)) + return SDValue(); + + EVT NVT = TLI.getTypeToTransformTo(*DAG.getContext(), VT); + DAG.getContext()->emitError("do not know how to soften fmodf"); + SDValue Poison = DAG.getPOISON(NVT); + SetSoftenedFloat(SDValue(N, 0), Poison); + SetSoftenedFloat(SDValue(N, 1), Poison); + return SDValue(); } SDValue DAGTypeLegalizer::SoftenFloatRes_FREM(SDNode *N) { diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h index 8643ae9d78159..9b537248d4ab4 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h @@ -564,7 +564,7 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer { // Convert Float Results to Integer. void SoftenFloatResult(SDNode *N, unsigned ResNo); SDValue SoftenFloatRes_Unary(SDNode *N, RTLIB::Libcall LC); - SDValue SoftenFloatRes_UnaryWithTwoFPResults( + bool SoftenFloatRes_UnaryWithTwoFPResults( SDNode *N, RTLIB::Libcall LC, std::optional CallRetResNo = {}); SDValue SoftenFloatRes_Binary(SDNode *N, RTLIB::Libcall LC); SDValue SoftenFloatRes_MERGE_VALUES(SDNode *N, unsigned ResNo); diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp index c9f927ade4f1f..85b148a466e52 100644 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -410,6 +410,14 @@ RTLIB::Libcall RTLIB::getFREXP(EVT RetVT) { FREXP_PPCF128); } +RTLIB::Libcall RTLIB::getSIN(EVT RetVT) { + return getFPLibCall(RetVT, SIN_F32, SIN_F64, SIN_F80, SIN_F128, SIN_PPCF128); +} + +RTLIB::Libcall RTLIB::getCOS(EVT RetVT) { + return getFPLibCall(RetVT, COS_F32, COS_F64, COS_F80, COS_F128, COS_PPCF128); +} + RTLIB::Libcall RTLIB::getSINCOS(EVT RetVT) { return getFPLibCall(RetVT, SINCOS_F32, SINCOS_F64, SINCOS_F80, SINCOS_F128, SINCOS_PPCF128); diff --git a/llvm/test/CodeGen/AVR/sincos-soften-error.ll b/llvm/test/CodeGen/AVR/sincos-soften-error.ll new file mode 100644 index 0000000000000..85f5aabb4e966 --- /dev/null +++ b/llvm/test/CodeGen/AVR/sincos-soften-error.ll @@ -0,0 +1,9 @@ +; RUN: not llc -mtriple=avr -filetype=null %s 2>&1 | FileCheck %s + +; CHECK: error: do not know how to soften fsincos +define { double, double } @test_sincos_f64(double %a) #0 { + %result = call { double, double } @llvm.sincos.f64(double %a) + ret { double, double } %result +} + +attributes #0 = { nounwind } diff --git a/llvm/test/CodeGen/WebAssembly/llvm.sincos.ll b/llvm/test/CodeGen/WebAssembly/llvm.sincos.ll index 9701f518a0ee0..3c10b09525573 100644 --- a/llvm/test/CodeGen/WebAssembly/llvm.sincos.ll +++ b/llvm/test/CodeGen/WebAssembly/llvm.sincos.ll @@ -378,16 +378,232 @@ define { <2 x double>, <2 x double> } @test_sincos_v2f64(<2 x double> %a) #0 { ret { <2 x double>, <2 x double> } %result } -; ; FIXME: Asserts -; define { fp128, fp128 } @test_sincos_f128(fp128 %a) #0 { -; %result = call { fp128, fp128 } @llvm.sincos.f128(fp128 %a) -; ret { fp128, fp128 } %result -; } +define { fp128, fp128 } @test_sincos_f128(fp128 %a) #0 { +; WASM32-LABEL: test_sincos_f128: +; WASM32: .functype test_sincos_f128 (i32, i64, i64) -> () +; WASM32-NEXT: .local i32 +; WASM32-NEXT: # %bb.0: +; WASM32-NEXT: global.get __stack_pointer +; WASM32-NEXT: i32.const 32 +; WASM32-NEXT: i32.sub +; WASM32-NEXT: local.tee 3 +; WASM32-NEXT: global.set __stack_pointer +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: local.get 1 +; WASM32-NEXT: local.get 2 +; WASM32-NEXT: call cosl +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: i32.const 16 +; WASM32-NEXT: i32.add +; WASM32-NEXT: local.get 1 +; WASM32-NEXT: local.get 2 +; WASM32-NEXT: call sinl +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: i64.load 8 +; WASM32-NEXT: i64.store 24 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: i64.load 0 +; WASM32-NEXT: i64.store 16 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: i64.load 24 +; WASM32-NEXT: i64.store 8 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: i64.load 16 +; WASM32-NEXT: i64.store 0 +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: i32.const 32 +; WASM32-NEXT: i32.add +; WASM32-NEXT: global.set __stack_pointer +; WASM32-NEXT: # fallthrough-return +; +; WASM64-LABEL: test_sincos_f128: +; WASM64: .functype test_sincos_f128 (i64, i64, i64) -> () +; WASM64-NEXT: .local i64 +; WASM64-NEXT: # %bb.0: +; WASM64-NEXT: global.get __stack_pointer +; WASM64-NEXT: i64.const 32 +; WASM64-NEXT: i64.sub +; WASM64-NEXT: local.tee 3 +; WASM64-NEXT: global.set __stack_pointer +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: local.get 1 +; WASM64-NEXT: local.get 2 +; WASM64-NEXT: call cosl +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: i64.const 16 +; WASM64-NEXT: i64.add +; WASM64-NEXT: local.get 1 +; WASM64-NEXT: local.get 2 +; WASM64-NEXT: call sinl +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: i64.load 8 +; WASM64-NEXT: i64.store 24 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: i64.load 0 +; WASM64-NEXT: i64.store 16 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: i64.load 24 +; WASM64-NEXT: i64.store 8 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: i64.load 16 +; WASM64-NEXT: i64.store 0 +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: i64.const 32 +; WASM64-NEXT: i64.add +; WASM64-NEXT: global.set __stack_pointer +; WASM64-NEXT: # fallthrough-return + %result = call { fp128, fp128 } @llvm.sincos.f128(fp128 %a) + ret { fp128, fp128 } %result +} -; ; FIXME: Asserts -; define { <2 x fp128>, <2 x fp128> } @test_sincos_v2f128(<2 x fp128> %a) #0 { -; %result = call { <2 x fp128>, <2 x fp128> } @llvm.sincos.v2f128(<2 x fp128> %a) -; ret { <2 x fp128>, <2 x fp128> } %result -; } +define { <2 x fp128>, <2 x fp128> } @test_sincos_v2f128(<2 x fp128> %a) #0 { +; WASM32-LABEL: test_sincos_v2f128: +; WASM32: .functype test_sincos_v2f128 (i32, i64, i64, i64, i64) -> () +; WASM32-NEXT: .local i32 +; WASM32-NEXT: # %bb.0: +; WASM32-NEXT: global.get __stack_pointer +; WASM32-NEXT: i32.const 64 +; WASM32-NEXT: i32.sub +; WASM32-NEXT: local.tee 5 +; WASM32-NEXT: global.set __stack_pointer +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i32.const 32 +; WASM32-NEXT: i32.add +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: local.get 4 +; WASM32-NEXT: call cosl +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: local.get 1 +; WASM32-NEXT: local.get 2 +; WASM32-NEXT: call cosl +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i32.const 48 +; WASM32-NEXT: i32.add +; WASM32-NEXT: local.get 3 +; WASM32-NEXT: local.get 4 +; WASM32-NEXT: call sinl +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i32.const 16 +; WASM32-NEXT: i32.add +; WASM32-NEXT: local.get 1 +; WASM32-NEXT: local.get 2 +; WASM32-NEXT: call sinl +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 40 +; WASM32-NEXT: i64.store 56 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 32 +; WASM32-NEXT: i64.store 48 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 8 +; WASM32-NEXT: i64.store 40 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 0 +; WASM32-NEXT: i64.store 32 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 56 +; WASM32-NEXT: i64.store 24 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 48 +; WASM32-NEXT: i64.store 16 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 24 +; WASM32-NEXT: i64.store 8 +; WASM32-NEXT: local.get 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i64.load 16 +; WASM32-NEXT: i64.store 0 +; WASM32-NEXT: local.get 5 +; WASM32-NEXT: i32.const 64 +; WASM32-NEXT: i32.add +; WASM32-NEXT: global.set __stack_pointer +; WASM32-NEXT: # fallthrough-return +; +; WASM64-LABEL: test_sincos_v2f128: +; WASM64: .functype test_sincos_v2f128 (i64, i64, i64, i64, i64) -> () +; WASM64-NEXT: .local i64 +; WASM64-NEXT: # %bb.0: +; WASM64-NEXT: global.get __stack_pointer +; WASM64-NEXT: i64.const 64 +; WASM64-NEXT: i64.sub +; WASM64-NEXT: local.tee 5 +; WASM64-NEXT: global.set __stack_pointer +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.const 32 +; WASM64-NEXT: i64.add +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: local.get 4 +; WASM64-NEXT: call cosl +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: local.get 1 +; WASM64-NEXT: local.get 2 +; WASM64-NEXT: call cosl +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.const 48 +; WASM64-NEXT: i64.add +; WASM64-NEXT: local.get 3 +; WASM64-NEXT: local.get 4 +; WASM64-NEXT: call sinl +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.const 16 +; WASM64-NEXT: i64.add +; WASM64-NEXT: local.get 1 +; WASM64-NEXT: local.get 2 +; WASM64-NEXT: call sinl +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 40 +; WASM64-NEXT: i64.store 56 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 32 +; WASM64-NEXT: i64.store 48 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 8 +; WASM64-NEXT: i64.store 40 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 0 +; WASM64-NEXT: i64.store 32 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 56 +; WASM64-NEXT: i64.store 24 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 48 +; WASM64-NEXT: i64.store 16 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 24 +; WASM64-NEXT: i64.store 8 +; WASM64-NEXT: local.get 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.load 16 +; WASM64-NEXT: i64.store 0 +; WASM64-NEXT: local.get 5 +; WASM64-NEXT: i64.const 64 +; WASM64-NEXT: i64.add +; WASM64-NEXT: global.set __stack_pointer +; WASM64-NEXT: # fallthrough-return + %result = call { <2 x fp128>, <2 x fp128> } @llvm.sincos.v2f128(<2 x fp128> %a) + ret { <2 x fp128>, <2 x fp128> } %result +} attributes #0 = { nounwind }