[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