Skip to content

Commit a059b29

Browse files
[SPIR-V] Allow intrinsics with aggregate return type to reach GlobalISel (#108893)
Two main goals of this PR are: * to support "Arithmetic with Overflow" intrinsics, including the special case when those intrinsics are being generated by the CodeGenPrepare pass during translations with optimization; * to redirect intrinsics with aggregate return type to be lowered via GlobalISel operations instead of SPIRV-specific unfolding/lowering (see #95012). There is a new test case `llvm/test/CodeGen/SPIRV/passes/translate-aggregate-uaddo.ll` that describes and checks the general logics of the translation. This PR continues a series of PRs aimed to identify and fix flaws in code emission, to improve pass rates for the mode with expensive checks set on (see #101732, #104104, #106966), having in mind the ultimate goal of proceeding towards the non-experimental status of SPIR-V Backend. The reproducers are: 1) consider `llc -O3 -mtriple=spirv64-unknown-unknown ...` with: ``` define spir_func i32 @foo(i32 %a, ptr addrspace(4) %p) { entry: br label %l1 l1: %e = phi i32 [ %a, %entry ], [ %i, %body ] %i = add nsw i32 %e, 1 %fl = icmp eq i32 %i, 0 br i1 %fl, label %exit, label %body body: store i8 42, ptr addrspace(4) %p br label %l1 exit: ret i32 %i } ``` 2) consider `llc -O0 -mtriple=spirv64-unknown-unknown ...` with: ``` define spir_func i32 @foo(i32 %a, ptr addrspace(4) %p) { entry: br label %l1 l1: ; preds = %body, %entry %e = phi i32 [ %a, %entry ], [ %math, %body ] %0 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %e, i32 1) %math = extractvalue { i32, i1 } %0, 0 %ov = extractvalue { i32, i1 } %0, 1 br i1 %ov, label %exit, label %body body: ; preds = %l1 store i8 42, ptr addrspace(4) %p, align 1 br label %l1 exit: ; preds = %l1 ret i32 %math } ```
1 parent f661e69 commit a059b29

17 files changed

+645
-78
lines changed

llvm/docs/SPIRVUsage.rst

+4
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ SPIR-V backend, along with their descriptions and argument details.
275275
- None
276276
- `[Type, Vararg]`
277277
- Assigns names to types or values, enhancing readability and debuggability of SPIR-V code. Not emitted directly but used for metadata enrichment.
278+
* - `int_spv_value_md`
279+
- None
280+
- `[Metadata]`
281+
- Assigns a set of attributes (such as name and data type) to a value that is the argument of the associated `llvm.fake.use` intrinsic call. The latter is used as a mean to map virtual registers created by IRTranslator to the original value.
278282
* - `int_spv_assign_decoration`
279283
- None
280284
- `[Type, Metadata]`

llvm/include/llvm/IR/IntrinsicsSPIRV.td

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ let TargetPrefix = "spv" in {
1515
def int_spv_assign_ptr_type : Intrinsic<[], [llvm_any_ty, llvm_metadata_ty, llvm_i32_ty], [ImmArg<ArgIndex<2>>]>;
1616
def int_spv_assign_name : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
1717
def int_spv_assign_decoration : Intrinsic<[], [llvm_any_ty, llvm_metadata_ty]>;
18+
def int_spv_value_md : Intrinsic<[], [llvm_metadata_ty]>;
1819

1920
def int_spv_track_constant : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty]>;
2021
def int_spv_init_global : Intrinsic<[], [llvm_any_ty, llvm_any_ty]>;

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

+32-3
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,21 @@ bool isConvergenceIntrinsic(const Instruction *I) {
188188
II->getIntrinsicID() == Intrinsic::experimental_convergence_loop ||
189189
II->getIntrinsicID() == Intrinsic::experimental_convergence_anchor;
190190
}
191+
192+
bool allowEmitFakeUse(const Value *Arg) {
193+
if (const auto *II = dyn_cast<IntrinsicInst>(Arg))
194+
if (Function *F = II->getCalledFunction())
195+
if (F->getName().starts_with("llvm.spv."))
196+
return false;
197+
if (dyn_cast<AtomicCmpXchgInst>(Arg) || dyn_cast<InsertValueInst>(Arg) ||
198+
dyn_cast<UndefValue>(Arg))
199+
return false;
200+
if (const auto *LI = dyn_cast<LoadInst>(Arg))
201+
if (LI->getType()->isAggregateType())
202+
return false;
203+
return true;
204+
}
205+
191206
} // namespace
192207

193208
char SPIRVEmitIntrinsics::ID = 0;
@@ -283,8 +298,20 @@ static inline Type *reconstructType(SPIRVGlobalRegistry *GR, Value *Op) {
283298
void SPIRVEmitIntrinsics::buildAssignType(IRBuilder<> &B, Type *Ty,
284299
Value *Arg) {
285300
Value *OfType = PoisonValue::get(Ty);
286-
CallInst *AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type,
287-
{Arg->getType()}, OfType, Arg, {}, B);
301+
CallInst *AssignCI = nullptr;
302+
if (Arg->getType()->isAggregateType() && Ty->isAggregateType() &&
303+
allowEmitFakeUse(Arg)) {
304+
LLVMContext &Ctx = Arg->getContext();
305+
SmallVector<Metadata *, 2> ArgMDs{
306+
MDNode::get(Ctx, ValueAsMetadata::getConstant(OfType)),
307+
MDString::get(Ctx, Arg->getName())};
308+
B.CreateIntrinsic(Intrinsic::spv_value_md, {},
309+
{MetadataAsValue::get(Ctx, MDTuple::get(Ctx, ArgMDs))});
310+
AssignCI = B.CreateIntrinsic(Intrinsic::fake_use, {}, {Arg});
311+
} else {
312+
AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type, {Arg->getType()},
313+
OfType, Arg, {}, B);
314+
}
288315
GR->addAssignPtrTypeInstr(Arg, AssignCI);
289316
}
290317

@@ -1268,6 +1295,8 @@ Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &I) {
12681295
}
12691296

12701297
Instruction *SPIRVEmitIntrinsics::visitExtractValueInst(ExtractValueInst &I) {
1298+
if (I.getAggregateOperand()->getType()->isAggregateType())
1299+
return &I;
12711300
IRBuilder<> B(I.getParent());
12721301
B.SetInsertPoint(&I);
12731302
SmallVector<Value *> Args;
@@ -1534,7 +1563,7 @@ void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I,
15341563
I->setOperand(OpNo, NewOp);
15351564
}
15361565
}
1537-
if (I->hasName()) {
1566+
if (I->hasName() && !I->getType()->isAggregateType()) {
15381567
reportFatalOnTokenType(I);
15391568
setInsertPointAfterDef(B, I);
15401569
std::vector<Value *> Args = {I};

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h

+17
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ class SPIRVGlobalRegistry {
5555
// created during substitution of aggregate arguments
5656
// (see `SPIRVPrepareFunctions::removeAggregateTypesFromSignature()`)
5757
DenseMap<Value *, Type *> MutatedAggRet;
58+
// map an instruction to its value's attributes (type, name)
59+
DenseMap<MachineInstr *, std::pair<Type *, std::string>> ValueAttrs;
5860

5961
// Look for an equivalent of the newType in the map. Return the equivalent
6062
// if it's found, otherwise insert newType to the map and return the type.
@@ -188,6 +190,21 @@ class SPIRVGlobalRegistry {
188190
return It == MutatedAggRet.end() ? nullptr : It->second;
189191
}
190192

193+
// A registry of value's attributes (type, name)
194+
// - Add a record.
195+
void addValueAttrs(MachineInstr *Key, std::pair<Type *, std::string> Val) {
196+
ValueAttrs[Key] = Val;
197+
}
198+
// - Find a record.
199+
bool findValueAttrs(const MachineInstr *Key, Type *&Ty, StringRef &Name) {
200+
auto It = ValueAttrs.find(Key);
201+
if (It == ValueAttrs.end())
202+
return false;
203+
Ty = It->second.first;
204+
Name = It->second.second;
205+
return true;
206+
}
207+
191208
// Deduced element types of untyped pointers and composites:
192209
// - Add a record to the map of deduced element types.
193210
void addDeducedElementType(Value *Val, Type *Ty) { DeducedElTys[Val] = Ty; }

llvm/lib/Target/SPIRV/SPIRVInstrInfo.td

+2-2
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,8 @@ def OpMatrixTimesMatrix: BinOp<"OpMatrixTimesMatrix", 146>;
519519
def OpOuterProduct: BinOp<"OpOuterProduct", 147>;
520520
def OpDot: BinOp<"OpDot", 148>;
521521

522-
def OpIAddCarry: BinOpTyped<"OpIAddCarry", 149, iID, addc>;
523-
def OpISubBorrow: BinOpTyped<"OpISubBorrow", 150, iID, subc>;
522+
defm OpIAddCarry: BinOpTypedGen<"OpIAddCarry", 149, addc, 0, 1>;
523+
defm OpISubBorrow: BinOpTypedGen<"OpISubBorrow", 150, subc, 0, 1>;
524524
def OpUMulExtended: BinOp<"OpUMulExtended", 151>;
525525
def OpSMulExtended: BinOp<"OpSMulExtended", 152>;
526526

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

+98-1
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
169169
bool selectFloatDot(Register ResVReg, const SPIRVType *ResType,
170170
MachineInstr &I) const;
171171

172+
bool selectOverflowArith(Register ResVReg, const SPIRVType *ResType,
173+
MachineInstr &I, unsigned Opcode) const;
174+
172175
bool selectIntegerDot(Register ResVReg, const SPIRVType *ResType,
173176
MachineInstr &I) const;
174177

@@ -386,11 +389,22 @@ bool SPIRVInstructionSelector::select(MachineInstr &I) {
386389
return false;
387390
}
388391

392+
static bool mayApplyGenericSelection(unsigned Opcode) {
393+
switch (Opcode) {
394+
case TargetOpcode::G_CONSTANT:
395+
return false;
396+
case TargetOpcode::G_SADDO:
397+
case TargetOpcode::G_SSUBO:
398+
return true;
399+
}
400+
return isTypeFoldingSupported(Opcode);
401+
}
402+
389403
bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
390404
const SPIRVType *ResType,
391405
MachineInstr &I) const {
392406
const unsigned Opcode = I.getOpcode();
393-
if (isTypeFoldingSupported(Opcode) && Opcode != TargetOpcode::G_CONSTANT)
407+
if (mayApplyGenericSelection(Opcode))
394408
return selectImpl(I, *CoverageInfo);
395409
switch (Opcode) {
396410
case TargetOpcode::G_CONSTANT:
@@ -567,6 +581,21 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
567581
case TargetOpcode::G_USUBSAT:
568582
return selectExtInst(ResVReg, ResType, I, CL::u_sub_sat);
569583

584+
case TargetOpcode::G_UADDO:
585+
return selectOverflowArith(ResVReg, ResType, I,
586+
ResType->getOpcode() == SPIRV::OpTypeVector
587+
? SPIRV::OpIAddCarryV
588+
: SPIRV::OpIAddCarryS);
589+
case TargetOpcode::G_USUBO:
590+
return selectOverflowArith(ResVReg, ResType, I,
591+
ResType->getOpcode() == SPIRV::OpTypeVector
592+
? SPIRV::OpISubBorrowV
593+
: SPIRV::OpISubBorrowS);
594+
case TargetOpcode::G_UMULO:
595+
return selectOverflowArith(ResVReg, ResType, I, SPIRV::OpUMulExtended);
596+
case TargetOpcode::G_SMULO:
597+
return selectOverflowArith(ResVReg, ResType, I, SPIRV::OpSMulExtended);
598+
570599
case TargetOpcode::G_SEXT:
571600
return selectExt(ResVReg, ResType, I, true);
572601
case TargetOpcode::G_ANYEXT:
@@ -1056,6 +1085,71 @@ bool SPIRVInstructionSelector::selectFence(MachineInstr &I) const {
10561085
.constrainAllUses(TII, TRI, RBI);
10571086
}
10581087

1088+
bool SPIRVInstructionSelector::selectOverflowArith(Register ResVReg,
1089+
const SPIRVType *ResType,
1090+
MachineInstr &I,
1091+
unsigned Opcode) const {
1092+
Type *ResTy = nullptr;
1093+
StringRef ResName;
1094+
if (!GR.findValueAttrs(&I, ResTy, ResName))
1095+
report_fatal_error(
1096+
"Not enough info to select the arithmetic with overflow instruction");
1097+
if (!ResTy || !ResTy->isStructTy())
1098+
report_fatal_error("Expect struct type result for the arithmetic "
1099+
"with overflow instruction");
1100+
// "Result Type must be from OpTypeStruct. The struct must have two members,
1101+
// and the two members must be the same type."
1102+
Type *ResElemTy = cast<StructType>(ResTy)->getElementType(0);
1103+
ResTy = StructType::create(SmallVector<Type *, 2>{ResElemTy, ResElemTy});
1104+
// Build SPIR-V types and constant(s) if needed.
1105+
MachineIRBuilder MIRBuilder(I);
1106+
SPIRVType *StructType = GR.getOrCreateSPIRVType(
1107+
ResTy, MIRBuilder, SPIRV::AccessQualifier::ReadWrite, false);
1108+
assert(I.getNumDefs() > 1 && "Not enought operands");
1109+
SPIRVType *BoolType = GR.getOrCreateSPIRVBoolType(I, TII);
1110+
unsigned N = GR.getScalarOrVectorComponentCount(ResType);
1111+
if (N > 1)
1112+
BoolType = GR.getOrCreateSPIRVVectorType(BoolType, N, I, TII);
1113+
Register BoolTypeReg = GR.getSPIRVTypeID(BoolType);
1114+
Register ZeroReg = buildZerosVal(ResType, I);
1115+
// A new virtual register to store the result struct.
1116+
Register StructVReg = MRI->createGenericVirtualRegister(LLT::scalar(64));
1117+
MRI->setRegClass(StructVReg, &SPIRV::IDRegClass);
1118+
// Build the result name if needed.
1119+
if (ResName.size() > 0)
1120+
buildOpName(StructVReg, ResName, MIRBuilder);
1121+
// Build the arithmetic with overflow instruction.
1122+
MachineBasicBlock &BB = *I.getParent();
1123+
auto MIB =
1124+
BuildMI(BB, MIRBuilder.getInsertPt(), I.getDebugLoc(), TII.get(Opcode))
1125+
.addDef(StructVReg)
1126+
.addUse(GR.getSPIRVTypeID(StructType));
1127+
for (unsigned i = I.getNumDefs(); i < I.getNumOperands(); ++i)
1128+
MIB.addUse(I.getOperand(i).getReg());
1129+
bool Status = MIB.constrainAllUses(TII, TRI, RBI);
1130+
// Build instructions to extract fields of the instruction's result.
1131+
// A new virtual register to store the higher part of the result struct.
1132+
Register HigherVReg = MRI->createGenericVirtualRegister(LLT::scalar(64));
1133+
MRI->setRegClass(HigherVReg, &SPIRV::iIDRegClass);
1134+
for (unsigned i = 0; i < I.getNumDefs(); ++i) {
1135+
auto MIB =
1136+
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCompositeExtract))
1137+
.addDef(i == 1 ? HigherVReg : I.getOperand(i).getReg())
1138+
.addUse(GR.getSPIRVTypeID(ResType))
1139+
.addUse(StructVReg)
1140+
.addImm(i);
1141+
Status &= MIB.constrainAllUses(TII, TRI, RBI);
1142+
}
1143+
// Build boolean value from the higher part.
1144+
Status &= BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpINotEqual))
1145+
.addDef(I.getOperand(1).getReg())
1146+
.addUse(BoolTypeReg)
1147+
.addUse(HigherVReg)
1148+
.addUse(ZeroReg)
1149+
.constrainAllUses(TII, TRI, RBI);
1150+
return Status;
1151+
}
1152+
10591153
bool SPIRVInstructionSelector::selectAtomicCmpXchg(Register ResVReg,
10601154
const SPIRVType *ResType,
10611155
MachineInstr &I) const {
@@ -2460,6 +2554,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
24602554
}
24612555
case Intrinsic::spv_step:
24622556
return selectStep(ResVReg, ResType, I);
2557+
case Intrinsic::spv_value_md:
2558+
// ignore the intrinsic
2559+
break;
24632560
default: {
24642561
std::string DiagMsg;
24652562
raw_string_ostream OS(DiagMsg);

llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,8 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
287287
// TODO: add proper legalization rules.
288288
getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG).alwaysLegal();
289289

290-
getActionDefinitionsBuilder({G_UADDO, G_USUBO, G_SMULO, G_UMULO})
290+
getActionDefinitionsBuilder(
291+
{G_UADDO, G_SADDO, G_USUBO, G_SSUBO, G_UMULO, G_SMULO})
291292
.alwaysLegal();
292293

293294
// FP conversions.

llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp

+26-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,13 @@ Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpvType,
376376
.addUse(NewReg)
377377
.addUse(GR->getSPIRVTypeID(SpvType))
378378
.setMIFlags(Flags);
379-
Def->getOperand(0).setReg(NewReg);
379+
for (unsigned I = 0, E = Def->getNumDefs(); I != E; ++I) {
380+
MachineOperand &MO = Def->getOperand(I);
381+
if (MO.getReg() == Reg) {
382+
MO.setReg(NewReg);
383+
break;
384+
}
385+
}
380386
return NewReg;
381387
}
382388

@@ -460,6 +466,25 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
460466
Def->getOpcode() != SPIRV::ASSIGN_TYPE)
461467
insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MF.getRegInfo());
462468
ToErase.push_back(&MI);
469+
} else if (MIOp == TargetOpcode::FAKE_USE && MI.getNumOperands() > 0) {
470+
MachineInstr *MdMI = MI.getPrevNode();
471+
if (MdMI && isSpvIntrinsic(*MdMI, Intrinsic::spv_value_md)) {
472+
// It's an internal service info from before IRTranslator passes.
473+
MachineInstr *Def = getVRegDef(MRI, MI.getOperand(0).getReg());
474+
for (unsigned I = 1, E = MI.getNumOperands(); I != E && Def; ++I)
475+
if (getVRegDef(MRI, MI.getOperand(I).getReg()) != Def)
476+
Def = nullptr;
477+
if (Def) {
478+
const MDNode *MD = MdMI->getOperand(1).getMetadata();
479+
StringRef ValueName =
480+
cast<MDString>(MD->getOperand(1))->getString();
481+
const MDNode *TypeMD = cast<MDNode>(MD->getOperand(0));
482+
Type *ValueTy = getMDOperandAsType(TypeMD, 0);
483+
GR->addValueAttrs(Def, std::make_pair(ValueTy, ValueName.str()));
484+
}
485+
ToErase.push_back(MdMI);
486+
}
487+
ToErase.push_back(&MI);
463488
} else if (MIOp == TargetOpcode::G_CONSTANT ||
464489
MIOp == TargetOpcode::G_FCONSTANT ||
465490
MIOp == TargetOpcode::G_BUILD_VECTOR) {

llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp

+5-43
Original file line numberDiff line numberDiff line change
@@ -342,30 +342,6 @@ static void lowerFunnelShifts(IntrinsicInst *FSHIntrinsic) {
342342
FSHIntrinsic->setCalledFunction(FSHFunc);
343343
}
344344

345-
static void buildUMulWithOverflowFunc(Function *UMulFunc) {
346-
// The function body is already created.
347-
if (!UMulFunc->empty())
348-
return;
349-
350-
BasicBlock *EntryBB = BasicBlock::Create(UMulFunc->getParent()->getContext(),
351-
"entry", UMulFunc);
352-
IRBuilder<> IRB(EntryBB);
353-
// Build the actual unsigned multiplication logic with the overflow
354-
// indication. Do unsigned multiplication Mul = A * B. Then check
355-
// if unsigned division Div = Mul / A is not equal to B. If so,
356-
// then overflow has happened.
357-
Value *Mul = IRB.CreateNUWMul(UMulFunc->getArg(0), UMulFunc->getArg(1));
358-
Value *Div = IRB.CreateUDiv(Mul, UMulFunc->getArg(0));
359-
Value *Overflow = IRB.CreateICmpNE(UMulFunc->getArg(0), Div);
360-
361-
// umul.with.overflow intrinsic return a structure, where the first element
362-
// is the multiplication result, and the second is an overflow bit.
363-
Type *StructTy = UMulFunc->getReturnType();
364-
Value *Agg = IRB.CreateInsertValue(PoisonValue::get(StructTy), Mul, {0});
365-
Value *Res = IRB.CreateInsertValue(Agg, Overflow, {1});
366-
IRB.CreateRet(Res);
367-
}
368-
369345
static void lowerExpectAssume(IntrinsicInst *II) {
370346
// If we cannot use the SPV_KHR_expect_assume extension, then we need to
371347
// ignore the intrinsic and move on. It should be removed later on by LLVM.
@@ -407,20 +383,6 @@ static bool toSpvOverloadedIntrinsic(IntrinsicInst *II, Intrinsic::ID NewID,
407383
return true;
408384
}
409385

410-
static void lowerUMulWithOverflow(IntrinsicInst *UMulIntrinsic) {
411-
// Get a separate function - otherwise, we'd have to rework the CFG of the
412-
// current one. Then simply replace the intrinsic uses with a call to the new
413-
// function.
414-
Module *M = UMulIntrinsic->getModule();
415-
FunctionType *UMulFuncTy = UMulIntrinsic->getFunctionType();
416-
Type *FSHLRetTy = UMulFuncTy->getReturnType();
417-
const std::string FuncName = lowerLLVMIntrinsicName(UMulIntrinsic);
418-
Function *UMulFunc =
419-
getOrCreateFunction(M, FSHLRetTy, UMulFuncTy->params(), FuncName);
420-
buildUMulWithOverflowFunc(UMulFunc);
421-
UMulIntrinsic->setCalledFunction(UMulFunc);
422-
}
423-
424386
// Substitutes calls to LLVM intrinsics with either calls to SPIR-V intrinsics
425387
// or calls to proper generated functions. Returns True if F was modified.
426388
bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
@@ -444,10 +406,6 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
444406
lowerFunnelShifts(II);
445407
Changed = true;
446408
break;
447-
case Intrinsic::umul_with_overflow:
448-
lowerUMulWithOverflow(II);
449-
Changed = true;
450-
break;
451409
case Intrinsic::assume:
452410
case Intrinsic::expect: {
453411
const SPIRVSubtarget &STI = TM.getSubtarget<SPIRVSubtarget>(*F);
@@ -478,9 +436,13 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
478436
// noted in 'spv.cloned_funcs' metadata for later restoration.
479437
Function *
480438
SPIRVPrepareFunctions::removeAggregateTypesFromSignature(Function *F) {
439+
bool IsRetAggr = F->getReturnType()->isAggregateType();
440+
// Allow intrinsics with aggregate return type to reach GlobalISel
441+
if (F->isIntrinsic() && IsRetAggr)
442+
return F;
443+
481444
IRBuilder<> B(F->getContext());
482445

483-
bool IsRetAggr = F->getReturnType()->isAggregateType();
484446
bool HasAggrArg =
485447
std::any_of(F->arg_begin(), F->arg_end(), [](Argument &Arg) {
486448
return Arg.getType()->isAggregateType();

0 commit comments

Comments
 (0)