[llvm] [RISCV][SelectionDAG] Lower llvm.clear_cache to __riscv_flush_icache for glibc targets (PR #93481)
Roger Ferrer Ibáñez via llvm-commits
llvm-commits at lists.llvm.org
Mon May 27 08:16:57 PDT 2024
https://github.com/rofirrim created https://github.com/llvm/llvm-project/pull/93481
We are working on Fortran applications on RISC-V Linux and some of them rely on passing a pointer to an internal (aka nested) function that uses the enclosing context (e.g., a variable of the enclosing function). [Flang uses trampolines](https://flang.llvm.org/docs/InternalProcedureTrampolines.html) which rely on llvm.init.trampoline and llvm.adjust.trampoline. Those rely on having an executable stack.
This change is a preliminary step to support trampolines on RISC-V.
In this change we lower `llvm.clear_cache` intrinsic on glibc targets to `__riscv_flush_icache` ( https://www.gnu.org/software/libc/manual/html_node/RISC_002dV.html ) which is what GCC is currently doing (https://www.godbolt.org/z/qsd1P3fYT). This change also must extend the generic part so a target can lower the call to a function that does not have the same signature as the generic `__clear_cache`.
I could not find where it is specified that RISC-V on glibc targets must call `__riscv_flush_icache` so before landing this we may want to address this issue.
>From 2a51c76348a1ddbdedd34ccf877788629d20e27d Mon Sep 17 00:00:00 2001
From: Roger Ferrer Ibanez <roger.ferrer at bsc.es>
Date: Mon, 27 May 2024 12:13:10 +0000
Subject: [PATCH] [RISCV][SelectionDAG] Lower llvm.clear_cache to
__riscv_flush_icache for glibc targets
This change also must extend the generic part so a target can lower the
call to a function that does not have the same signature as the generic
__clear_cache.
---
llvm/include/llvm/CodeGen/TargetLowering.h | 9 ++-
.../SelectionDAG/SelectionDAGBuilder.cpp | 9 ++-
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 56 +++++++++++++++++++
llvm/lib/Target/RISCV/RISCVISelLowering.h | 5 ++
llvm/test/CodeGen/RISCV/clear-cache.ll | 49 ++++++++++++++++
5 files changed, 125 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/CodeGen/RISCV/clear-cache.ll
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 50a8c7eb75af5..3f052449b0a6f 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -4764,8 +4764,15 @@ class TargetLowering : public TargetLoweringBase {
return false;
}
+ /// Returns true if the target needs to lower __builtin___clear_cache in a
+ /// specific way that is incompatible with the clear_cache
+ /// signature. When returning false, the lowering will invoke
+ /// getClearCacheBuiltinName.
+ virtual bool isClearCacheBuiltinTargetSpecific() const { return false; }
+
/// Return the builtin name for the __builtin___clear_cache intrinsic
- /// Default is to invoke the clear cache library call
+ /// This is only used if isClearCacheBuiltinTargetSpecific returns false.
+ /// If nullptr is returned, the builtin is lowered to no code.
virtual const char * getClearCacheBuiltinName() const {
return "__clear_cache";
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index ca352da5d36eb..320ec0cdfffb6 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -7518,8 +7518,13 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
return;
case Intrinsic::clear_cache:
/// FunctionName may be null.
- if (const char *FunctionName = TLI.getClearCacheBuiltinName())
- lowerCallToExternalSymbol(I, FunctionName);
+ if (!TLI.isClearCacheBuiltinTargetSpecific()) {
+ if (const char *FunctionName = TLI.getClearCacheBuiltinName())
+ lowerCallToExternalSymbol(I, FunctionName);
+ } else {
+ // Turn this into a target intrinsic node.
+ visitTargetIntrinsic(I, Intrinsic);
+ }
return;
case Intrinsic::donothing:
case Intrinsic::seh_try_begin:
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index f0e5a7d393b6c..0e591034698b6 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -662,6 +662,12 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setBooleanContents(ZeroOrOneBooleanContent);
+ if (Subtarget.getTargetTriple().isOSGlibc()) {
+ // Custom lowering of llvm.clear_cache
+ setOperationAction({ISD::INTRINSIC_VOID, ISD::INTRINSIC_VOID}, MVT::Other,
+ Custom);
+ }
+
if (Subtarget.hasVInstructions()) {
setBooleanVectorContents(ZeroOrOneBooleanContent);
@@ -7120,6 +7126,39 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op,
}
}
+SDValue RISCVTargetLowering::emitFlushICache(SelectionDAG &DAG, SDValue InChain,
+ SDValue Start, SDValue End,
+ SDValue Flags, SDLoc DL) const {
+ TargetLowering::ArgListTy Args;
+ TargetLowering::ArgListEntry Entry;
+
+ // start
+ Entry.Node = Start;
+ Entry.Ty = PointerType::getUnqual(*DAG.getContext());
+ Args.push_back(Entry);
+
+ // end
+ Entry.Node = End;
+ Entry.Ty = PointerType::getUnqual(*DAG.getContext());
+ Args.push_back(Entry);
+
+ // flags
+ Entry.Node = Flags;
+ Entry.Ty = Type::getIntNTy(*DAG.getContext(), Subtarget.getXLen());
+ Args.push_back(Entry);
+
+ TargetLowering::CallLoweringInfo CLI(DAG);
+ EVT Ty = getPointerTy(DAG.getDataLayout());
+ CLI.setDebugLoc(DL).setChain(InChain).setLibCallee(
+ CallingConv::C, Type::getVoidTy(*DAG.getContext()),
+ DAG.getExternalSymbol("__riscv_flush_icache", Ty), std::move(Args));
+
+ std::pair<SDValue, SDValue> CallResult = LowerCallTo(CLI);
+
+ // This function returns void so only the out chain matters.
+ return CallResult.second;
+}
+
static SDValue getTargetNode(GlobalAddressSDNode *N, const SDLoc &DL, EVT Ty,
SelectionDAG &DAG, unsigned Flags) {
return DAG.getTargetGlobalAddress(N->getGlobal(), DL, Ty, 0, Flags);
@@ -9497,6 +9536,15 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_VOID(SDValue Op,
return getVCIXISDNodeVOID(Op, DAG, RISCVISD::SF_VC_VVW_SE);
case Intrinsic::riscv_sf_vc_fvw_se:
return getVCIXISDNodeVOID(Op, DAG, RISCVISD::SF_VC_FVW_SE);
+ case Intrinsic::clear_cache: {
+ if (Subtarget.getTargetTriple().isOSGlibc()) {
+ SDLoc DL(Op);
+ SDValue Flags = DAG.getConstant(0, DL, Subtarget.getXLenVT());
+ return emitFlushICache(DAG, Op.getOperand(0), Op.getOperand(2),
+ Op.getOperand(3), Flags, DL);
+ }
+ break;
+ }
}
return lowerVectorIntrinsicScalars(Op, DAG, Subtarget);
@@ -21684,6 +21732,14 @@ SDValue RISCVTargetLowering::expandIndirectJTBranch(const SDLoc &dl,
return TargetLowering::expandIndirectJTBranch(dl, Value, Addr, JTI, DAG);
}
+bool RISCVTargetLowering::isClearCacheBuiltinTargetSpecific() const {
+ // We do a manual lowering for glibc-based targets to call
+ // __riscv_flush_icache instead.
+ if (Subtarget.getTargetTriple().isOSGlibc())
+ return true;
+ return TargetLowering::isClearCacheBuiltinTargetSpecific();
+}
+
namespace llvm::RISCVVIntrinsicsTable {
#define GET_RISCVVIntrinsicsTable_IMPL
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 856ce06ba1c4f..a5301a997bf6b 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -897,6 +897,8 @@ class RISCVTargetLowering : public TargetLowering {
const RISCVTargetLowering &TLI,
RVVArgDispatcher &RVVDispatcher);
+ bool isClearCacheBuiltinTargetSpecific() const override;
+
private:
void analyzeInputArgs(MachineFunction &MF, CCState &CCInfo,
const SmallVectorImpl<ISD::InputArg> &Ins, bool IsRet,
@@ -1033,6 +1035,9 @@ class RISCVTargetLowering : public TargetLowering {
const APInt &AndMask) const override;
unsigned getMinimumJumpTableEntries() const override;
+
+ SDValue emitFlushICache(SelectionDAG &DAG, SDValue InChain, SDValue Start,
+ SDValue End, SDValue Flags, SDLoc DL) const;
};
/// As per the spec, the rules for passing vector arguments are as follows:
diff --git a/llvm/test/CodeGen/RISCV/clear-cache.ll b/llvm/test/CodeGen/RISCV/clear-cache.ll
new file mode 100644
index 0000000000000..84db1eb0d3bda
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/clear-cache.ll
@@ -0,0 +1,49 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=riscv32 < %s | FileCheck --check-prefix=RV32 %s
+; RUN: llc -mtriple=riscv64 < %s | FileCheck --check-prefix=RV64 %s
+; RUN: llc -mtriple=riscv32-unknown-linux-gnu < %s | FileCheck --check-prefix=RV32-GLIBC %s
+; RUN: llc -mtriple=riscv64-unknown-linux-gnu < %s | FileCheck --check-prefix=RV64-GLIBC %s
+
+declare void @llvm.clear_cache(ptr, ptr)
+
+define void @foo(ptr %a, ptr %b) nounwind {
+; RV32-LABEL: foo:
+; RV32: # %bb.0:
+; RV32-NEXT: addi sp, sp, -16
+; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32-NEXT: call __clear_cache
+; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32-NEXT: addi sp, sp, 16
+; RV32-NEXT: ret
+;
+; RV64-LABEL: foo:
+; RV64: # %bb.0:
+; RV64-NEXT: addi sp, sp, -16
+; RV64-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
+; RV64-NEXT: call __clear_cache
+; RV64-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
+; RV64-NEXT: addi sp, sp, 16
+; RV64-NEXT: ret
+;
+; RV32-GLIBC-LABEL: foo:
+; RV32-GLIBC: # %bb.0:
+; RV32-GLIBC-NEXT: addi sp, sp, -16
+; RV32-GLIBC-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
+; RV32-GLIBC-NEXT: li a2, 0
+; RV32-GLIBC-NEXT: call __riscv_flush_icache
+; RV32-GLIBC-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
+; RV32-GLIBC-NEXT: addi sp, sp, 16
+; RV32-GLIBC-NEXT: ret
+;
+; RV64-GLIBC-LABEL: foo:
+; RV64-GLIBC: # %bb.0:
+; RV64-GLIBC-NEXT: addi sp, sp, -16
+; RV64-GLIBC-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
+; RV64-GLIBC-NEXT: li a2, 0
+; RV64-GLIBC-NEXT: call __riscv_flush_icache
+; RV64-GLIBC-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
+; RV64-GLIBC-NEXT: addi sp, sp, 16
+; RV64-GLIBC-NEXT: ret
+ call void @llvm.clear_cache(ptr %a, ptr %b)
+ ret void
+}
More information about the llvm-commits
mailing list