Skip to content
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

[SPIRV] Implement type deduction and reference to function declarations for indirect calls using SPV_INTEL_function_pointers #111159

Merged
merged 6 commits into from
Oct 15, 2024

Conversation

VyacheslavLevytskyy
Copy link
Contributor

@VyacheslavLevytskyy VyacheslavLevytskyy commented Oct 4, 2024

This PR improves implementation of SPV_INTEL_function_pointers and type inference for phi-nodes and indirect calls.

Copy link

github-actions bot commented Oct 4, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@VyacheslavLevytskyy VyacheslavLevytskyy changed the title [SPIRV] Fix indirect calls for function pointers [SPIRV] Implement type deduction and reference to function declarations for indirect calls using SPV_INTEL_function_pointers Oct 11, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Oct 11, 2024

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

Author: Vyacheslav Levytskyy (VyacheslavLevytskyy)

Changes

This PR improves implementation of SPV_INTEL_function_pointers and type inference for phi-nodes and indirect calls.


Patch is 31.89 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/111159.diff

9 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp (+16-2)
  • (modified) llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp (+21)
  • (modified) llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp (+202-55)
  • (modified) llvm/lib/Target/SPIRV/SPIRVUtils.cpp (+14)
  • (modified) llvm/lib/Target/SPIRV/SPIRVUtils.h (+3)
  • (added) llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-simple-hierarchy.ll (+88)
  • (modified) llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp_const.ll (+23-14)
  • (modified) llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp_two_calls.ll (+17-14)
  • (modified) llvm/test/CodeGen/SPIRV/instructions/select-phi.ll (+5-3)
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 55b41627802096..8210e20ce5b10e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -78,6 +78,11 @@ class SPIRVAsmPrinter : public AsmPrinter {
   void outputExecutionMode(const Module &M);
   void outputAnnotations(const Module &M);
   void outputModuleSections();
+  bool isHidden() {
+    return MF->getFunction()
+        .getFnAttribute(SPIRV_BACKEND_SERVICE_FUN_NAME)
+        .isValid();
+  }
 
   void emitInstruction(const MachineInstr *MI) override;
   void emitFunctionEntryLabel() override {}
@@ -131,7 +136,7 @@ void SPIRVAsmPrinter::emitFunctionHeader() {
   TII = ST->getInstrInfo();
   const Function &F = MF->getFunction();
 
-  if (isVerbose()) {
+  if (isVerbose() && !isHidden()) {
     OutStreamer->getCommentOS()
         << "-- Begin function "
         << GlobalValue::dropLLVMManglingEscape(F.getName()) << '\n';
@@ -149,11 +154,18 @@ void SPIRVAsmPrinter::outputOpFunctionEnd() {
 
 // Emit OpFunctionEnd at the end of MF and clear BBNumToRegMap.
 void SPIRVAsmPrinter::emitFunctionBodyEnd() {
+  // Do not emit anything if it's an internal service function.
+  if (isHidden())
+    return;
   outputOpFunctionEnd();
   MAI->BBNumToRegMap.clear();
 }
 
 void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) {
+  // Do not emit anything if it's an internal service function.
+  if (isHidden())
+    return;
+
   MCInst LabelInst;
   LabelInst.setOpcode(SPIRV::OpLabel);
   LabelInst.addOperand(MCOperand::createReg(MAI->getOrCreateMBBRegister(MBB)));
@@ -162,7 +174,9 @@ void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) {
 }
 
 void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
-  assert(!MBB.empty() && "MBB is empty!");
+  // Do not emit anything if it's an internal service function.
+  if (MBB.empty())
+    return;
 
   // If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so
   // OpLabel should be output after them.
diff --git a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
index 27a9cb0ba9b8c0..f8ce02a13c0f67 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
@@ -36,6 +36,13 @@ bool SPIRVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
                                     const Value *Val, ArrayRef<Register> VRegs,
                                     FunctionLoweringInfo &FLI,
                                     Register SwiftErrorVReg) const {
+  // Ignore if called from the internal service function
+  if (MIRBuilder.getMF()
+          .getFunction()
+          .getFnAttribute(SPIRV_BACKEND_SERVICE_FUN_NAME)
+          .isValid())
+    return true;
+
   // Maybe run postponed production of types for function pointers
   if (IndirectCalls.size() > 0) {
     produceIndirectPtrTypes(MIRBuilder);
@@ -280,6 +287,10 @@ bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
                                              const Function &F,
                                              ArrayRef<ArrayRef<Register>> VRegs,
                                              FunctionLoweringInfo &FLI) const {
+  // Discard the internal service function
+  if (F.getFnAttribute(SPIRV_BACKEND_SERVICE_FUN_NAME).isValid())
+    return true;
+
   assert(GR && "Must initialize the SPIRV type registry before lowering args.");
   GR->setCurrentFunc(MIRBuilder.getMF());
 
@@ -576,6 +587,16 @@ bool SPIRVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
     lowerFormalArguments(FirstBlockBuilder, *CF, VRegArgs, FuncInfo);
   }
 
+  // Ignore the call if it's called from the internal service function
+  if (MIRBuilder.getMF()
+          .getFunction()
+          .getFnAttribute(SPIRV_BACKEND_SERVICE_FUN_NAME)
+          .isValid()) {
+    // insert a no-op
+    MIRBuilder.buildTrap();
+    return true;
+  }
+
   unsigned CallOp;
   if (Info.CB->isIndirectCall()) {
     if (!ST->canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers))
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 370df24bc7af9e..8b7e9c48de6c75 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -69,6 +69,7 @@ class SPIRVEmitIntrinsics
   SPIRVGlobalRegistry *GR = nullptr;
   Function *F = nullptr;
   bool TrackConstants = true;
+  bool HaveFunPtrs = false;
   DenseMap<Instruction *, Constant *> AggrConsts;
   DenseMap<Instruction *, Type *> AggrConstTypes;
   DenseSet<Instruction *> AggrStores;
@@ -147,6 +148,10 @@ class SPIRVEmitIntrinsics
   void replaceWithPtrcasted(Instruction *CI, Type *NewElemTy, Type *KnownElemTy,
                             CallInst *AssignCI);
 
+  bool runOnFunction(Function &F);
+  bool postprocessTypes();
+  bool processFunctionPointers(Module &M);
+
 public:
   static char ID;
   SPIRVEmitIntrinsics() : ModulePass(ID) {
@@ -173,8 +178,6 @@ class SPIRVEmitIntrinsics
   StringRef getPassName() const override { return "SPIRV emit intrinsics"; }
 
   bool runOnModule(Module &M) override;
-  bool runOnFunction(Function &F);
-  bool postprocessTypes();
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     ModulePass::getAnalysisUsage(AU);
@@ -384,7 +387,8 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
 // Traverse User instructions to deduce an element pointer type of the operand.
 Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep(
     Value *Op, std::unordered_set<Value *> &Visited, bool UnknownElemTypeI8) {
-  if (!Op || !isPointerTy(Op->getType()))
+  if (!Op || !isPointerTy(Op->getType()) || isa<ConstantPointerNull>(Op) ||
+      isa<UndefValue>(Op))
     return nullptr;
 
   if (auto ElemTy = getPointeeType(Op->getType()))
@@ -481,12 +485,25 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
     if (isPointerTy(Op->getType()))
       Ty = deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<PHINode>(I)) {
-    for (unsigned i = 0; i < Ref->getNumIncomingValues(); i++) {
+    Type *BestTy = nullptr;
+    unsigned MaxN = 1;
+    DenseMap<Type *, unsigned> PhiTys;
+    for (int i = Ref->getNumIncomingValues() - 1; i >= 0; --i) {
       Ty = deduceElementTypeByUsersDeep(Ref->getIncomingValue(i), Visited,
                                         UnknownElemTypeI8);
-      if (Ty)
-        break;
+      if (!Ty)
+        continue;
+      auto It = PhiTys.try_emplace(Ty, 1);
+      if (!It.second) {
+        ++It.first->second;
+        if (It.first->second > MaxN) {
+          MaxN = It.first->second;
+          BestTy = Ty;
+        }
+      }
     }
+    if (BestTy)
+      Ty = BestTy;
   } else if (auto *Ref = dyn_cast<SelectInst>(I)) {
     for (Value *Op : {Ref->getTrueValue(), Ref->getFalseValue()}) {
       Ty = deduceElementTypeByUsersDeep(Op, Visited, UnknownElemTypeI8);
@@ -642,6 +659,93 @@ static inline Type *getAtomicElemTy(SPIRVGlobalRegistry *GR, Instruction *I,
   return nullptr;
 }
 
+// Try to deduce element type for a call base. Returns false if this is an
+// indirect function invocation, and true otherwise.
+static bool deduceOperandElementTypeCalledFunction(
+    SPIRVGlobalRegistry *GR, Instruction *I,
+    SPIRV::InstructionSet::InstructionSet InstrSet, CallInst *CI,
+    SmallVector<std::pair<Value *, unsigned>> &Ops, Type *&KnownElemTy) {
+  Function *CalledF = CI->getCalledFunction();
+  if (!CalledF)
+    return false;
+  std::string DemangledName =
+      getOclOrSpirvBuiltinDemangledName(CalledF->getName());
+  if (DemangledName.length() > 0 &&
+      !StringRef(DemangledName).starts_with("llvm.")) {
+    auto [Grp, Opcode, ExtNo] =
+        SPIRV::mapBuiltinToOpcode(DemangledName, InstrSet);
+    if (Opcode == SPIRV::OpGroupAsyncCopy) {
+      for (unsigned i = 0, PtrCnt = 0; i < CI->arg_size() && PtrCnt < 2; ++i) {
+        Value *Op = CI->getArgOperand(i);
+        if (!isPointerTy(Op->getType()))
+          continue;
+        ++PtrCnt;
+        if (Type *ElemTy = GR->findDeducedElementType(Op))
+          KnownElemTy = ElemTy; // src will rewrite dest if both are defined
+        Ops.push_back(std::make_pair(Op, i));
+      }
+    } else if (Grp == SPIRV::Atomic || Grp == SPIRV::AtomicFloating) {
+      if (CI->arg_size() < 2)
+        return true;
+      Value *Op = CI->getArgOperand(0);
+      if (!isPointerTy(Op->getType()))
+        return true;
+      switch (Opcode) {
+      case SPIRV::OpAtomicLoad:
+      case SPIRV::OpAtomicCompareExchangeWeak:
+      case SPIRV::OpAtomicCompareExchange:
+      case SPIRV::OpAtomicExchange:
+      case SPIRV::OpAtomicIAdd:
+      case SPIRV::OpAtomicISub:
+      case SPIRV::OpAtomicOr:
+      case SPIRV::OpAtomicXor:
+      case SPIRV::OpAtomicAnd:
+      case SPIRV::OpAtomicUMin:
+      case SPIRV::OpAtomicUMax:
+      case SPIRV::OpAtomicSMin:
+      case SPIRV::OpAtomicSMax: {
+        KnownElemTy = getAtomicElemTy(GR, I, Op);
+        if (!KnownElemTy)
+          return true;
+        Ops.push_back(std::make_pair(Op, 0));
+      } break;
+      }
+    }
+  }
+  return true;
+}
+
+// Try to deduce element type for a function pointer.
+static void deduceOperandElementTypeFunctionPointer(
+    SPIRVGlobalRegistry *GR, Instruction *I, CallInst *CI,
+    SmallVector<std::pair<Value *, unsigned>> &Ops, Type *&KnownElemTy) {
+  Value *Op = CI->getCalledOperand();
+  if (!Op || !isPointerTy(Op->getType()))
+    return;
+  Ops.push_back(std::make_pair(Op, std::numeric_limits<unsigned>::max()));
+  FunctionType *FTy = CI->getFunctionType();
+  bool IsNewFTy = false;
+  SmallVector<Type *, 4> ArgTys;
+  for (Value *Arg : CI->args()) {
+    Type *ArgTy = Arg->getType();
+    if (ArgTy->isPointerTy())
+      if (Type *ElemTy = GR->findDeducedElementType(Arg)) {
+        IsNewFTy = true;
+        ArgTy = TypedPointerType::get(ElemTy, getPointerAddressSpace(ArgTy));
+      }
+    ArgTys.push_back(ArgTy);
+  }
+  Type *RetTy = FTy->getReturnType();
+  if (I->getType()->isPointerTy())
+    if (Type *ElemTy = GR->findDeducedElementType(I)) {
+      IsNewFTy = true;
+      RetTy =
+          TypedPointerType::get(ElemTy, getPointerAddressSpace(I->getType()));
+    }
+  KnownElemTy =
+      IsNewFTy ? FunctionType::get(RetTy, ArgTys, FTy->isVarArg()) : FTy;
+}
+
 // If the Instruction has Pointer operands with unresolved types, this function
 // tries to deduce them. If the Instruction has Pointer operands with known
 // types which differ from expected, this function tries to insert a bitcast to
@@ -747,54 +851,12 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I,
       KnownElemTy = ElemTy1;
       Ops.push_back(std::make_pair(Op0, 0));
     }
-  } else if (auto *CI = dyn_cast<CallInst>(I)) {
-    if (Function *CalledF = CI->getCalledFunction()) {
-      std::string DemangledName =
-          getOclOrSpirvBuiltinDemangledName(CalledF->getName());
-      if (DemangledName.length() > 0 &&
-          !StringRef(DemangledName).starts_with("llvm.")) {
-        auto [Grp, Opcode, ExtNo] =
-            SPIRV::mapBuiltinToOpcode(DemangledName, InstrSet);
-        if (Opcode == SPIRV::OpGroupAsyncCopy) {
-          for (unsigned i = 0, PtrCnt = 0; i < CI->arg_size() && PtrCnt < 2;
-               ++i) {
-            Value *Op = CI->getArgOperand(i);
-            if (!isPointerTy(Op->getType()))
-              continue;
-            ++PtrCnt;
-            if (Type *ElemTy = GR->findDeducedElementType(Op))
-              KnownElemTy = ElemTy; // src will rewrite dest if both are defined
-            Ops.push_back(std::make_pair(Op, i));
-          }
-        } else if (Grp == SPIRV::Atomic || Grp == SPIRV::AtomicFloating) {
-          if (CI->arg_size() < 2)
-            return;
-          Value *Op = CI->getArgOperand(0);
-          if (!isPointerTy(Op->getType()))
-            return;
-          switch (Opcode) {
-          case SPIRV::OpAtomicLoad:
-          case SPIRV::OpAtomicCompareExchangeWeak:
-          case SPIRV::OpAtomicCompareExchange:
-          case SPIRV::OpAtomicExchange:
-          case SPIRV::OpAtomicIAdd:
-          case SPIRV::OpAtomicISub:
-          case SPIRV::OpAtomicOr:
-          case SPIRV::OpAtomicXor:
-          case SPIRV::OpAtomicAnd:
-          case SPIRV::OpAtomicUMin:
-          case SPIRV::OpAtomicUMax:
-          case SPIRV::OpAtomicSMin:
-          case SPIRV::OpAtomicSMax: {
-            KnownElemTy = getAtomicElemTy(GR, I, Op);
-            if (!KnownElemTy)
-              return;
-            Ops.push_back(std::make_pair(Op, 0));
-          } break;
-          }
-        }
-      }
-    }
+  } else if (CallInst *CI = dyn_cast<CallInst>(I)) {
+    if (!CI->isIndirectCall())
+      deduceOperandElementTypeCalledFunction(GR, I, InstrSet, CI, Ops,
+                                             KnownElemTy);
+    else if (HaveFunPtrs)
+      deduceOperandElementTypeFunctionPointer(GR, I, CI, Ops, KnownElemTy);
   }
 
   // There is no enough info to deduce types or all is valid.
@@ -844,7 +906,10 @@ void SPIRVEmitIntrinsics::deduceOperandElementType(Instruction *I,
                                       B.getInt32(getPointerAddressSpace(OpTy))};
       CallInst *PtrCastI =
           B.CreateIntrinsic(Intrinsic::spv_ptrcast, {Types}, Args);
-      I->setOperand(OpIt.second, PtrCastI);
+      if (OpIt.second == std::numeric_limits<unsigned>::max())
+        dyn_cast<CallInst>(I)->setCalledOperand(PtrCastI);
+      else
+        I->setOperand(OpIt.second, PtrCastI);
       buildAssignPtr(B, KnownElemTy, PtrCastI);
     }
   }
@@ -1671,6 +1736,82 @@ void SPIRVEmitIntrinsics::processParamTypes(Function *F, IRBuilder<> &B) {
   }
 }
 
+static FunctionType *getFunctionPointerElemType(Function *F,
+                                                SPIRVGlobalRegistry *GR) {
+  FunctionType *FTy = F->getFunctionType();
+  bool IsNewFTy = false;
+  SmallVector<Type *, 4> ArgTys;
+  for (Argument &Arg : F->args()) {
+    Type *ArgTy = Arg.getType();
+    if (ArgTy->isPointerTy())
+      if (Type *ElemTy = GR->findDeducedElementType(&Arg)) {
+        IsNewFTy = true;
+        ArgTy = TypedPointerType::get(ElemTy, getPointerAddressSpace(ArgTy));
+      }
+    ArgTys.push_back(ArgTy);
+  }
+  return IsNewFTy
+             ? FunctionType::get(FTy->getReturnType(), ArgTys, FTy->isVarArg())
+             : FTy;
+}
+
+bool SPIRVEmitIntrinsics::processFunctionPointers(Module &M) {
+  SmallVector<Function *> Worklist;
+  for (auto &F : M) {
+    if (F.isIntrinsic())
+      continue;
+    if (F.isDeclaration()) {
+      for (User *U : F.users()) {
+        CallInst *CI = dyn_cast<CallInst>(U);
+        if (!CI || CI->getCalledFunction() != &F) {
+          Worklist.push_back(&F);
+          break;
+        }
+      }
+    } else {
+      if (F.user_empty())
+        continue;
+      Type *FPElemTy = GR->findDeducedElementType(&F);
+      if (!FPElemTy)
+        FPElemTy = getFunctionPointerElemType(&F, GR);
+      for (User *U : F.users()) {
+        IntrinsicInst *II = dyn_cast<IntrinsicInst>(U);
+        if (!II || II->arg_size() != 3 || II->getOperand(0) != &F)
+          continue;
+        if (II->getIntrinsicID() == Intrinsic::spv_assign_ptr_type ||
+            II->getIntrinsicID() == Intrinsic::spv_ptrcast) {
+          updateAssignType(II, &F, PoisonValue::get(FPElemTy));
+          break;
+        }
+      }
+    }
+  }
+  if (Worklist.empty())
+    return false;
+
+  std::string ServiceFunName = SPIRV_BACKEND_SERVICE_FUN_NAME;
+  if (!getVacantFunctionName(M, ServiceFunName))
+    report_fatal_error(
+        "cannot allocate a name for the internal service function");
+  LLVMContext &Ctx = M.getContext();
+  Function *SF =
+      Function::Create(FunctionType::get(Type::getVoidTy(Ctx), {}, false),
+                       GlobalValue::PrivateLinkage, ServiceFunName, M);
+  SF->addFnAttr(SPIRV_BACKEND_SERVICE_FUN_NAME, "");
+  BasicBlock *BB = BasicBlock::Create(Ctx, "entry", SF);
+  IRBuilder<> IRB(BB);
+
+  for (Function *F : Worklist) {
+    SmallVector<Value *> Args;
+    for (const auto &Arg : F->args())
+      Args.push_back(PoisonValue::get(Arg.getType()));
+    IRB.CreateCall(F, Args);
+  }
+  IRB.CreateRetVoid();
+
+  return true;
+}
+
 bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
   if (Func.isDeclaration())
     return false;
@@ -1680,6 +1821,10 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
   InstrSet = ST.isOpenCLEnv() ? SPIRV::InstructionSet::OpenCL_std
                               : SPIRV::InstructionSet::GLSL_std_450;
 
+  if (!F)
+    HaveFunPtrs =
+        ST.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers);
+
   F = &Func;
   IRBuilder<> B(Func.getContext());
   AggrConsts.clear();
@@ -1825,6 +1970,8 @@ bool SPIRVEmitIntrinsics::runOnModule(Module &M) {
   }
 
   Changed |= postprocessTypes();
+  if (HaveFunPtrs)
+    Changed |= processFunctionPointers(M);
 
   return Changed;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index d204a8ac7975d8..dff33b16b9cfcf 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -598,4 +598,18 @@ MachineInstr *getVRegDef(MachineRegisterInfo &MRI, Register Reg) {
   return MaybeDef;
 }
 
+bool getVacantFunctionName(Module &M, std::string &Name) {
+  // It's a bit of paranoia, but still we don't want to have even a chance that
+  // the loop will work for too long.
+  constexpr unsigned MaxIters = 1024;
+  for (unsigned I = 0; I < MaxIters; ++I) {
+    std::string OrdName = Name + Twine(I).str();
+    if (!M.getFunction(OrdName)) {
+      Name = OrdName;
+      return true;
+    }
+  }
+  return false;
+}
+
 } // namespace llvm
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index f7e8a827c2767f..83e717e6ea58fd 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -341,5 +341,8 @@ inline const Type *unifyPtrType(const Type *Ty) {
 
 MachineInstr *getVRegDef(MachineRegisterInfo &MRI, Register Reg);
 
+#define SPIRV_BACKEND_SERVICE_FUN_NAME "__spirv_backend_service_fun"
+bool getVacantFunctionName(Module &M, std::string &Name);
+
 } // namespace llvm
 #endif // LLVM_LIB_TARGET_SPIRV_SPIRVUTILS_H
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-simple-hierarchy.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-simple-hierarchy.ll
new file mode 100644
index 00000000000000..d5a8fb3e7baafa
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp-simple-hierarchy.ll
@@ -0,0 +1,88 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o - | FileCheck %s
+; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpName %[[I9:.*]] "_ZN13BaseIncrement9incrementEPi"
+; CHECK-DAG: OpName %[[I29:.*]] "_ZN12IncrementBy29incrementEPi"
+; CHECK-DAG: OpName %[[I49:.*]] "_ZN12IncrementBy49incrementEPi"
+; CHECK-DAG: OpName %[[I89:.*]] "_ZN12IncrementBy89incrementEPi"
+
+; CHECK-DAG: %[[TyVoid:.*]] = OpTypeVoid
+; CHECK-DAG: %[[TyArr:.*]] = OpTypeArray
+; CHECK-DAG: %[[TyStruct1:.*]] = OpTypeStruct %[[TyArr]]
+; CHECK-DAG: %[[TyStruct2:.*]] = OpTypeStruct %[[TyStruct1]]
+; CHECK-DAG: %[[TyPtrStruct2:.*]] = OpTypePointer Generic %[[TyStruct2]]
+; CHECK-DAG: %[[TyFun:.*]] = OpTypeFunction %[[TyVoid]] %[[TyPtrStruct2]] %[[#]]
+; CHECK-DAG: %[[TyPtrFun:.*]] = OpTypePointer Generic %[[TyFun]]
+; CHECK-DAG: %[[TyPtrPtrFun:.*]] = OpTypePointer Generic %[[TyPtrFun]]
+
+; CHECK: %[[I9]] = OpFunction
+; CHECK: %[[I29]] = OpFunction
+; CHECK: %[[I49]] = OpFunction
+; CHECK: %[[I89]] = OpFunction
+
+; CHECK: %[[Arg1:.*]] = OpPhi %[[TyPtrStruct2]]
+; CHECK: %[[VTbl:.*]] = OpBitcast %[[TyPtrPtrFun]] %[[#]]
+; CHECK: %[[FP:.*]] = OpLoad %[[TyPtrFun]] %[[VTbl]]
+; CHECK: %[[#]] = OpFunctionPointerCallINTEL %[[TyVoid]] %[[FP]] %[[Arg1]] %[[#]]
+
+%"cls::id" = type { %"cls::detail::array" }
+%"cls::detail::array" = type { [1 x i64] }
+%struct.obj_storage_t = type { %"struct.aligned_storage<BaseIncrement, IncrementBy2, IncrementBy4, IncrementBy8>::type" }
+%"struct.aligned_storage<BaseIncrement, IncrementBy2, IncrementBy4, IncrementBy8>::type" = type { [8 x i8] }
+
+@...
[truncated]

@VyacheslavLevytskyy VyacheslavLevytskyy merged commit 8d8996d into llvm:main Oct 15, 2024
9 checks passed
bricknerb pushed a commit to bricknerb/llvm-project that referenced this pull request Oct 16, 2024
…ns for indirect calls using SPV_INTEL_function_pointers (llvm#111159)

This PR improves implementation of SPV_INTEL_function_pointers and type
inference for phi-nodes and indirect calls.
DanielCChen pushed a commit to DanielCChen/llvm-project that referenced this pull request Oct 16, 2024
…ns for indirect calls using SPV_INTEL_function_pointers (llvm#111159)

This PR improves implementation of SPV_INTEL_function_pointers and type
inference for phi-nodes and indirect calls.
bricknerb pushed a commit to bricknerb/llvm-project that referenced this pull request Oct 17, 2024
…ns for indirect calls using SPV_INTEL_function_pointers (llvm#111159)

This PR improves implementation of SPV_INTEL_function_pointers and type
inference for phi-nodes and indirect calls.
EricWF pushed a commit to efcs/llvm-project that referenced this pull request Oct 22, 2024
…ns for indirect calls using SPV_INTEL_function_pointers (llvm#111159)

This PR improves implementation of SPV_INTEL_function_pointers and type
inference for phi-nodes and indirect calls.
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.

3 participants