Skip to content

Commit 1ed1ec9

Browse files
[SPIRV] Improve builtins matching and type inference in SPIR-V Backend, fix target ext type constants (#89948)
This PR is to improve builtins matching and type inference in SPIR-V Backend. The model test case is printf call from OpenCL.std that has several features allowing for a wider look at builtins support/type inference: (1) call in a "spirv-friendly" style (prefixed by __spirv_ocl_) (2) restricted type of the 1st argument Attached test cases checks several possible inputs. Support of the extension SPV_EXT_relaxed_printf_string_address_space is to do (see: https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/EXT/SPV_EXT_relaxed_printf_string_address_space.asciidoc). This PR also fixes target ext type constants and OpGroupAsyncCopy/OpGroupWaitEvents generation. A new test case is attached.
1 parent 2e3e086 commit 1ed1ec9

File tree

7 files changed

+196
-41
lines changed

7 files changed

+196
-41
lines changed

llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ lookupBuiltin(StringRef DemangledCall,
189189
std::string BuiltinName =
190190
DemangledCall.substr(0, DemangledCall.find('(')).str();
191191

192+
// Account for possible "__spirv_ocl_" prefix in SPIR-V friendly LLVM IR
193+
if (BuiltinName.rfind("__spirv_ocl_", 0) == 0)
194+
BuiltinName = BuiltinName.substr(12);
195+
192196
// Check if the extracted name contains type information between angle
193197
// brackets. If so, the builtin is an instantiated template - needs to have
194198
// the information after angle brackets and return type removed.
@@ -2008,6 +2012,13 @@ static bool generateAsyncCopy(const SPIRV::IncomingCall *Call,
20082012
const SPIRV::DemangledBuiltin *Builtin = Call->Builtin;
20092013
unsigned Opcode =
20102014
SPIRV::lookupNativeBuiltin(Builtin->Name, Builtin->Set)->Opcode;
2015+
2016+
bool IsSet = Opcode == SPIRV::OpGroupAsyncCopy;
2017+
Register TypeReg = GR->getSPIRVTypeID(Call->ReturnType);
2018+
if (Call->isSpirvOp())
2019+
return buildOpFromWrapper(MIRBuilder, Opcode, Call,
2020+
IsSet ? TypeReg : Register(0));
2021+
20112022
auto Scope = buildConstantIntReg(SPIRV::Scope::Workgroup, MIRBuilder, GR);
20122023

20132024
switch (Opcode) {
@@ -2306,7 +2317,7 @@ Type *parseBuiltinCallArgumentBaseType(const StringRef DemangledCall,
23062317
// parseBuiltinCallArgumentBaseType(...) as this function only retrieves the
23072318
// base types.
23082319
if (TypeStr.ends_with("*"))
2309-
TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" "));
2320+
TypeStr = TypeStr.slice(0, TypeStr.find_first_of(" *"));
23102321

23112322
return parseBuiltinTypeNameToTargetExtType("opencl." + TypeStr.str() + "_t",
23122323
Ctx);

llvm/lib/Target/SPIRV/SPIRVBuiltins.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -585,9 +585,9 @@ defm : DemangledNativeBuiltin<"__spirv_SpecConstantComposite", OpenCL_std, SpecC
585585

586586
// Async Copy and Prefetch builtin records:
587587
defm : DemangledNativeBuiltin<"async_work_group_copy", OpenCL_std, AsyncCopy, 4, 4, OpGroupAsyncCopy>;
588-
defm : DemangledNativeBuiltin<"__spirv_GroupAsyncCopy", OpenCL_std, AsyncCopy, 4, 4, OpGroupAsyncCopy>;
588+
defm : DemangledNativeBuiltin<"__spirv_GroupAsyncCopy", OpenCL_std, AsyncCopy, 6, 6, OpGroupAsyncCopy>;
589589
defm : DemangledNativeBuiltin<"wait_group_events", OpenCL_std, AsyncCopy, 2, 2, OpGroupWaitEvents>;
590-
defm : DemangledNativeBuiltin<"__spirv_GroupWaitEvents", OpenCL_std, AsyncCopy, 2, 2, OpGroupWaitEvents>;
590+
defm : DemangledNativeBuiltin<"__spirv_GroupWaitEvents", OpenCL_std, AsyncCopy, 3, 3, OpGroupWaitEvents>;
591591

592592
// Load and store builtin records:
593593
defm : DemangledNativeBuiltin<"__spirv_Load", OpenCL_std, LoadStore, 1, 3, OpLoad>;

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class SPIRVEmitIntrinsics
9898
return B.CreateIntrinsic(IntrID, {Types}, Args);
9999
}
100100

101+
void buildAssignPtr(IRBuilder<> &B, Type *ElemTy, Value *Arg);
102+
101103
void replaceMemInstrUses(Instruction *Old, Instruction *New, IRBuilder<> &B);
102104
void processInstrAfterVisit(Instruction *I, IRBuilder<> &B);
103105
void insertAssignPtrTypeIntrs(Instruction *I, IRBuilder<> &B);
@@ -111,6 +113,7 @@ class SPIRVEmitIntrinsics
111113
void insertPtrCastOrAssignTypeInstr(Instruction *I, IRBuilder<> &B);
112114
void processGlobalValue(GlobalVariable &GV, IRBuilder<> &B);
113115
void processParamTypes(Function *F, IRBuilder<> &B);
116+
void processParamTypesByFunHeader(Function *F, IRBuilder<> &B);
114117
Type *deduceFunParamElementType(Function *F, unsigned OpIdx);
115118
Type *deduceFunParamElementType(Function *F, unsigned OpIdx,
116119
std::unordered_set<Function *> &FVisited);
@@ -194,6 +197,17 @@ static inline void reportFatalOnTokenType(const Instruction *I) {
194197
false);
195198
}
196199

200+
void SPIRVEmitIntrinsics::buildAssignPtr(IRBuilder<> &B, Type *ElemTy,
201+
Value *Arg) {
202+
CallInst *AssignPtrTyCI =
203+
buildIntrWithMD(Intrinsic::spv_assign_ptr_type, {Arg->getType()},
204+
Constant::getNullValue(ElemTy), Arg,
205+
{B.getInt32(getPointerAddressSpace(Arg->getType()))}, B);
206+
GR->addDeducedElementType(AssignPtrTyCI, ElemTy);
207+
GR->addDeducedElementType(Arg, ElemTy);
208+
AssignPtrTypeInstr[Arg] = AssignPtrTyCI;
209+
}
210+
197211
// Set element pointer type to the given value of ValueTy and tries to
198212
// specify this type further (recursively) by Operand value, if needed.
199213
Type *SPIRVEmitIntrinsics::deduceElementTypeByValueDeep(
@@ -232,6 +246,19 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeByUsersDeep(
232246
return nullptr;
233247
}
234248

249+
// Implements what we know in advance about intrinsics and builtin calls
250+
// TODO: consider feasibility of this particular case to be generalized by
251+
// encoding knowledge about intrinsics and builtin calls by corresponding
252+
// specification rules
253+
static Type *getPointeeTypeByCallInst(StringRef DemangledName,
254+
Function *CalledF, unsigned OpIdx) {
255+
if ((DemangledName.starts_with("__spirv_ocl_printf(") ||
256+
DemangledName.starts_with("printf(")) &&
257+
OpIdx == 0)
258+
return IntegerType::getInt8Ty(CalledF->getContext());
259+
return nullptr;
260+
}
261+
235262
// Deduce and return a successfully deduced Type of the Instruction,
236263
// or nullptr otherwise.
237264
Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(Value *I) {
@@ -795,6 +822,8 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
795822
return;
796823

797824
// collect information about formal parameter types
825+
std::string DemangledName =
826+
getOclOrSpirvBuiltinDemangledName(CI->getCalledFunction()->getName());
798827
Function *CalledF = CI->getCalledFunction();
799828
SmallVector<Type *, 4> CalledArgTys;
800829
bool HaveTypes = false;
@@ -811,10 +840,15 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
811840
if (!ElemTy && hasPointeeTypeAttr(CalledArg))
812841
ElemTy = getPointeeTypeByAttr(CalledArg);
813842
if (!ElemTy) {
814-
for (User *U : CalledArg->users()) {
815-
if (Instruction *Inst = dyn_cast<Instruction>(U)) {
816-
if ((ElemTy = deduceElementTypeHelper(Inst)) != nullptr)
817-
break;
843+
ElemTy = getPointeeTypeByCallInst(DemangledName, CalledF, OpIdx);
844+
if (ElemTy) {
845+
GR->addDeducedElementType(CalledArg, ElemTy);
846+
} else {
847+
for (User *U : CalledArg->users()) {
848+
if (Instruction *Inst = dyn_cast<Instruction>(U)) {
849+
if ((ElemTy = deduceElementTypeHelper(Inst)) != nullptr)
850+
break;
851+
}
818852
}
819853
}
820854
}
@@ -823,8 +857,6 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
823857
}
824858
}
825859

826-
std::string DemangledName =
827-
getOclOrSpirvBuiltinDemangledName(CI->getCalledFunction()->getName());
828860
if (DemangledName.empty() && !HaveTypes)
829861
return;
830862

@@ -835,8 +867,14 @@ void SPIRVEmitIntrinsics::insertPtrCastOrAssignTypeInstr(Instruction *I,
835867
continue;
836868

837869
// Constants (nulls/undefs) are handled in insertAssignPtrTypeIntrs()
838-
if (!isa<Instruction>(ArgOperand) && !isa<Argument>(ArgOperand))
839-
continue;
870+
if (!isa<Instruction>(ArgOperand) && !isa<Argument>(ArgOperand)) {
871+
// However, we may have assumptions about the formal argument's type and
872+
// may have a need to insert a ptr cast for the actual parameter of this
873+
// call.
874+
Argument *CalledArg = CalledF->getArg(OpIdx);
875+
if (!GR->findDeducedElementType(CalledArg))
876+
continue;
877+
}
840878

841879
Type *ExpectedType =
842880
OpIdx < CalledArgTys.size() ? CalledArgTys[OpIdx] : nullptr;
@@ -1102,9 +1140,13 @@ void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I,
11021140
(II->paramHasAttr(OpNo, Attribute::ImmArg))))
11031141
continue;
11041142
B.SetInsertPoint(I);
1105-
auto *NewOp =
1106-
buildIntrWithMD(Intrinsic::spv_track_constant,
1107-
{Op->getType(), Op->getType()}, Op, Op, {}, B);
1143+
Value *OpTyVal = Op;
1144+
if (Op->getType()->isTargetExtTy())
1145+
OpTyVal = Constant::getNullValue(
1146+
IntegerType::get(I->getContext(), GR->getPointerSize()));
1147+
auto *NewOp = buildIntrWithMD(Intrinsic::spv_track_constant,
1148+
{Op->getType(), OpTyVal->getType()}, Op,
1149+
OpTyVal, {}, B);
11081150
I->setOperand(OpNo, NewOp);
11091151
}
11101152
}
@@ -1179,28 +1221,29 @@ Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
11791221
return nullptr;
11801222
}
11811223

1182-
void SPIRVEmitIntrinsics::processParamTypes(Function *F, IRBuilder<> &B) {
1224+
void SPIRVEmitIntrinsics::processParamTypesByFunHeader(Function *F,
1225+
IRBuilder<> &B) {
11831226
B.SetInsertPointPastAllocas(F);
11841227
for (unsigned OpIdx = 0; OpIdx < F->arg_size(); ++OpIdx) {
11851228
Argument *Arg = F->getArg(OpIdx);
11861229
if (!isUntypedPointerTy(Arg->getType()))
11871230
continue;
1231+
Type *ElemTy = GR->findDeducedElementType(Arg);
1232+
if (!ElemTy && hasPointeeTypeAttr(Arg) &&
1233+
(ElemTy = getPointeeTypeByAttr(Arg)) != nullptr)
1234+
buildAssignPtr(B, ElemTy, Arg);
1235+
}
1236+
}
11881237

1238+
void SPIRVEmitIntrinsics::processParamTypes(Function *F, IRBuilder<> &B) {
1239+
B.SetInsertPointPastAllocas(F);
1240+
for (unsigned OpIdx = 0; OpIdx < F->arg_size(); ++OpIdx) {
1241+
Argument *Arg = F->getArg(OpIdx);
1242+
if (!isUntypedPointerTy(Arg->getType()))
1243+
continue;
11891244
Type *ElemTy = GR->findDeducedElementType(Arg);
1190-
if (!ElemTy) {
1191-
if (hasPointeeTypeAttr(Arg) &&
1192-
(ElemTy = getPointeeTypeByAttr(Arg)) != nullptr) {
1193-
GR->addDeducedElementType(Arg, ElemTy);
1194-
} else if ((ElemTy = deduceFunParamElementType(F, OpIdx)) != nullptr) {
1195-
CallInst *AssignPtrTyCI = buildIntrWithMD(
1196-
Intrinsic::spv_assign_ptr_type, {Arg->getType()},
1197-
Constant::getNullValue(ElemTy), Arg,
1198-
{B.getInt32(getPointerAddressSpace(Arg->getType()))}, B);
1199-
GR->addDeducedElementType(AssignPtrTyCI, ElemTy);
1200-
GR->addDeducedElementType(Arg, ElemTy);
1201-
AssignPtrTypeInstr[Arg] = AssignPtrTyCI;
1202-
}
1203-
}
1245+
if (!ElemTy && (ElemTy = deduceFunParamElementType(F, OpIdx)) != nullptr)
1246+
buildAssignPtr(B, ElemTy, Arg);
12041247
}
12051248
}
12061249

@@ -1217,6 +1260,8 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
12171260
AggrConstTypes.clear();
12181261
AggrStores.clear();
12191262

1263+
processParamTypesByFunHeader(F, B);
1264+
12201265
// StoreInst's operand type can be changed during the next transformations,
12211266
// so we need to store it in the set. Also store already transformed types.
12221267
for (auto &I : instructions(Func)) {

llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,16 @@ void SPIRVTargetLowering::finalizeLowering(MachineFunction &MF) const {
314314
SPIRV::OpTypeBool))
315315
MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpLogicalNotEqual));
316316
break;
317+
case SPIRV::OpConstantI: {
318+
SPIRVType *Type = GR.getSPIRVTypeForVReg(MI.getOperand(1).getReg());
319+
if (Type->getOpcode() != SPIRV::OpTypeInt && MI.getOperand(2).isImm() &&
320+
MI.getOperand(2).getImm() == 0) {
321+
// Validate the null constant of a target extension type
322+
MI.setDesc(STI.getInstrInfo()->get(SPIRV::OpConstantNull));
323+
for (unsigned i = MI.getNumOperands() - 1; i > 1; --i)
324+
MI.removeOperand(i);
325+
}
326+
} break;
317327
}
318328
}
319329
}

llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ class SPIRVPreLegalizer : public MachineFunctionPass {
3838
};
3939
} // namespace
4040

41-
static void addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR) {
41+
static void
42+
addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR,
43+
DenseMap<MachineInstr *, Type *> &TargetExtConstTypes) {
4244
MachineRegisterInfo &MRI = MF.getRegInfo();
4345
DenseMap<MachineInstr *, Register> RegsAlreadyAddedToDT;
4446
SmallVector<MachineInstr *, 10> ToErase, ToEraseComposites;
@@ -47,21 +49,22 @@ static void addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR) {
4749
if (!isSpvIntrinsic(MI, Intrinsic::spv_track_constant))
4850
continue;
4951
ToErase.push_back(&MI);
52+
Register SrcReg = MI.getOperand(2).getReg();
5053
auto *Const =
5154
cast<Constant>(cast<ConstantAsMetadata>(
5255
MI.getOperand(3).getMetadata()->getOperand(0))
5356
->getValue());
5457
if (auto *GV = dyn_cast<GlobalValue>(Const)) {
5558
Register Reg = GR->find(GV, &MF);
5659
if (!Reg.isValid())
57-
GR->add(GV, &MF, MI.getOperand(2).getReg());
60+
GR->add(GV, &MF, SrcReg);
5861
else
5962
RegsAlreadyAddedToDT[&MI] = Reg;
6063
} else {
6164
Register Reg = GR->find(Const, &MF);
6265
if (!Reg.isValid()) {
6366
if (auto *ConstVec = dyn_cast<ConstantDataVector>(Const)) {
64-
auto *BuildVec = MRI.getVRegDef(MI.getOperand(2).getReg());
67+
auto *BuildVec = MRI.getVRegDef(SrcReg);
6568
assert(BuildVec &&
6669
BuildVec->getOpcode() == TargetOpcode::G_BUILD_VECTOR);
6770
for (unsigned i = 0; i < ConstVec->getNumElements(); ++i) {
@@ -75,7 +78,13 @@ static void addConstantsToTrack(MachineFunction &MF, SPIRVGlobalRegistry *GR) {
7578
BuildVec->getOperand(1 + i).setReg(ElemReg);
7679
}
7780
}
78-
GR->add(Const, &MF, MI.getOperand(2).getReg());
81+
GR->add(Const, &MF, SrcReg);
82+
if (Const->getType()->isTargetExtTy()) {
83+
// remember association so that we can restore it when assign types
84+
MachineInstr *SrcMI = MRI.getVRegDef(SrcReg);
85+
if (SrcMI && SrcMI->getOpcode() == TargetOpcode::G_CONSTANT)
86+
TargetExtConstTypes[SrcMI] = Const->getType();
87+
}
7988
} else {
8089
RegsAlreadyAddedToDT[&MI] = Reg;
8190
// This MI is unused and will be removed. If the MI uses
@@ -364,8 +373,10 @@ void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,
364373
}
365374
} // namespace llvm
366375

367-
static void generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
368-
MachineIRBuilder MIB) {
376+
static void
377+
generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
378+
MachineIRBuilder MIB,
379+
DenseMap<MachineInstr *, Type *> &TargetExtConstTypes) {
369380
// Get access to information about available extensions
370381
const SPIRVSubtarget *ST =
371382
static_cast<const SPIRVSubtarget *>(&MIB.getMF().getSubtarget());
@@ -422,11 +433,14 @@ static void generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
422433
continue;
423434
}
424435
Type *Ty = nullptr;
425-
if (MI.getOpcode() == TargetOpcode::G_CONSTANT)
426-
Ty = MI.getOperand(1).getCImm()->getType();
427-
else if (MI.getOpcode() == TargetOpcode::G_FCONSTANT)
436+
if (MI.getOpcode() == TargetOpcode::G_CONSTANT) {
437+
auto TargetExtIt = TargetExtConstTypes.find(&MI);
438+
Ty = TargetExtIt == TargetExtConstTypes.end()
439+
? MI.getOperand(1).getCImm()->getType()
440+
: TargetExtIt->second;
441+
} else if (MI.getOpcode() == TargetOpcode::G_FCONSTANT) {
428442
Ty = MI.getOperand(1).getFPImm()->getType();
429-
else {
443+
} else {
430444
assert(MI.getOpcode() == TargetOpcode::G_BUILD_VECTOR);
431445
Type *ElemTy = nullptr;
432446
MachineInstr *ElemMI = MRI.getVRegDef(MI.getOperand(1).getReg());
@@ -616,10 +630,12 @@ bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
616630
SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry();
617631
GR->setCurrentFunc(MF);
618632
MachineIRBuilder MIB(MF);
619-
addConstantsToTrack(MF, GR);
633+
// a registry of target extension constants
634+
DenseMap<MachineInstr *, Type *> TargetExtConstTypes;
635+
addConstantsToTrack(MF, GR, TargetExtConstTypes);
620636
foldConstantsIntoIntrinsics(MF);
621637
insertBitcasts(MF, GR, MIB);
622-
generateAssignInstrs(MF, GR, MIB);
638+
generateAssignInstrs(MF, GR, MIB, TargetExtConstTypes);
623639
processSwitches(MF, GR, MIB);
624640
processInstrsWithTypeFolding(MF, GR, MIB);
625641
removeImplicitFallthroughs(MF, MIB);

llvm/test/CodeGen/SPIRV/printf.ll

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
2+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
3+
4+
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
5+
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
6+
7+
; CHECK: %[[#ExtImport:]] = OpExtInstImport "OpenCL.std"
8+
; CHECK: %[[#Char:]] = OpTypeInt 8 0
9+
; CHECK: %[[#CharPtr:]] = OpTypePointer UniformConstant %[[#Char]]
10+
; CHECK: %[[#GV:]] = OpVariable %[[#]] UniformConstant %[[#]]
11+
; CHECK: OpFunction
12+
; CHECK: %[[#Arg1:]] = OpFunctionParameter
13+
; CHECK: %[[#Arg2:]] = OpFunctionParameter
14+
; CHECK: %[[#CastedGV:]] = OpBitcast %[[#CharPtr]] %[[#GV]]
15+
; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#ArgConst:]]
16+
; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedGV]] %[[#ArgConst]]
17+
; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg1]] %[[#ArgConst:]]
18+
; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#Arg1]] %[[#ArgConst]]
19+
; CHECK-NEXT: %[[#CastedArg2:]] = OpBitcast %[[#CharPtr]] %[[#Arg2]]
20+
; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedArg2]] %[[#ArgConst]]
21+
; CHECK-NEXT: OpExtInst %[[#]] %[[#ExtImport]] printf %[[#CastedArg2]] %[[#ArgConst]]
22+
; CHECK: OpFunctionEnd
23+
24+
%struct = type { [6 x i8] }
25+
26+
@FmtStr = internal addrspace(2) constant [6 x i8] c"c=%c\0A\00", align 1
27+
28+
define spir_kernel void @foo(ptr addrspace(2) %_arg_fmt1, ptr addrspace(2) byval(%struct) %_arg_fmt2) {
29+
entry:
30+
%r1 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) @FmtStr, i8 signext 97)
31+
%r2 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) @FmtStr, i8 signext 97)
32+
%r3 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97)
33+
%r4 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt1, i8 signext 97)
34+
%r5 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z6printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97)
35+
%r6 = tail call spir_func i32 (ptr addrspace(2), ...) @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2) %_arg_fmt2, i8 signext 97)
36+
ret void
37+
}
38+
39+
declare dso_local spir_func i32 @_Z6printfPU3AS2Kcz(ptr addrspace(2), ...)
40+
declare dso_local spir_func i32 @_Z18__spirv_ocl_printfPU3AS2Kcz(ptr addrspace(2), ...)

0 commit comments

Comments
 (0)