Skip to content

Commit c81e6c8

Browse files
s-perronDebadri Basak
authored andcommitted
[SPIRV] Expand spv_bitcast intrinsic during instruction selection (llvm#164884)
The spv_bitcast intrinsic is currently replaced by an OpBitcast during prelegalization. This will cause a problem when we need to legalize the OpBitcast. The legalizer assumes that instruction already lowered to a target specific opcode is legal. We cannot lower it to a G_BITCAST because the bitcasts sometimes the LLT type will be the same, causing an error in the verifier, even if the SPIR-V types will be different. This commit keeps the intrinsic around until instruction selection. We can create rules to legalize a G_INTRINISIC* instruction, and it does not create problem for the verifier. No tests are updated because this change should be invisible to users.
1 parent b46f767 commit c81e6c8

File tree

2 files changed

+42
-27
lines changed

2 files changed

+42
-27
lines changed

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3151,6 +3151,14 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
31513151
return selectInsertElt(ResVReg, ResType, I);
31523152
case Intrinsic::spv_gep:
31533153
return selectGEP(ResVReg, ResType, I);
3154+
case Intrinsic::spv_bitcast: {
3155+
Register OpReg = I.getOperand(2).getReg();
3156+
SPIRVType *OpType =
3157+
OpReg.isValid() ? GR.getSPIRVTypeForVReg(OpReg) : nullptr;
3158+
if (!GR.isBitcastCompatible(ResType, OpType))
3159+
report_fatal_error("incompatible result and operand types in a bitcast");
3160+
return selectOpWithSrcs(ResVReg, ResType, I, {OpReg}, SPIRV::OpBitcast);
3161+
}
31543162
case Intrinsic::spv_unref_global:
31553163
case Intrinsic::spv_init_global: {
31563164
MachineInstr *MI = MRI->getVRegDef(I.getOperand(1).getReg());

llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -192,31 +192,43 @@ static void buildOpBitcast(SPIRVGlobalRegistry *GR, MachineIRBuilder &MIB,
192192
.addUse(OpReg);
193193
}
194194

195-
// We do instruction selections early instead of calling MIB.buildBitcast()
196-
// generating the general op code G_BITCAST. When MachineVerifier validates
197-
// G_BITCAST we see a check of a kind: if Source Type is equal to Destination
198-
// Type then report error "bitcast must change the type". This doesn't take into
199-
// account the notion of a typed pointer that is important for SPIR-V where a
200-
// user may and should use bitcast between pointers with different pointee types
201-
// (https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpBitcast).
202-
// It's important for correct lowering in SPIR-V, because interpretation of the
203-
// data type is not left to instructions that utilize the pointer, but encoded
204-
// by the pointer declaration, and the SPIRV target can and must handle the
205-
// declaration and use of pointers that specify the type of data they point to.
206-
// It's not feasible to improve validation of G_BITCAST using just information
207-
// provided by low level types of source and destination. Therefore we don't
208-
// produce G_BITCAST as the general op code with semantics different from
209-
// OpBitcast, but rather lower to OpBitcast immediately. As for now, the only
210-
// difference would be that CombinerHelper couldn't transform known patterns
211-
// around G_BUILD_VECTOR. See discussion
212-
// in https://github.com/llvm/llvm-project/pull/110270 for even more context.
213-
static void selectOpBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
214-
MachineIRBuilder MIB) {
195+
// We lower G_BITCAST to OpBitcast here to avoid a MachineVerifier error.
196+
// The verifier checks if the source and destination LLTs of a G_BITCAST are
197+
// different, but this check is too strict for SPIR-V's typed pointers, which
198+
// may have the same LLT but different SPIRVType (e.g. pointers to different
199+
// pointee types). By lowering to OpBitcast here, we bypass the verifier's
200+
// check. See discussion in https://github.com/llvm/llvm-project/pull/110270
201+
// for more context.
202+
//
203+
// We also handle the llvm.spv.bitcast intrinsic here. If the source and
204+
// destination SPIR-V types are the same, we lower it to a COPY to enable
205+
// further optimizations like copy propagation.
206+
static void lowerBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
207+
MachineIRBuilder MIB) {
215208
SmallVector<MachineInstr *, 16> ToErase;
216209
for (MachineBasicBlock &MBB : MF) {
217210
for (MachineInstr &MI : MBB) {
211+
if (isSpvIntrinsic(MI, Intrinsic::spv_bitcast)) {
212+
Register DstReg = MI.getOperand(0).getReg();
213+
Register SrcReg = MI.getOperand(2).getReg();
214+
SPIRVType *DstType = GR->getSPIRVTypeForVReg(DstReg);
215+
assert(
216+
DstType &&
217+
"Expected destination SPIR-V type to have been assigned already.");
218+
SPIRVType *SrcType = GR->getSPIRVTypeForVReg(SrcReg);
219+
assert(SrcType &&
220+
"Expected source SPIR-V type to have been assigned already.");
221+
if (DstType == SrcType) {
222+
MIB.setInsertPt(*MI.getParent(), MI);
223+
MIB.buildCopy(DstReg, SrcReg);
224+
ToErase.push_back(&MI);
225+
continue;
226+
}
227+
}
228+
218229
if (MI.getOpcode() != TargetOpcode::G_BITCAST)
219230
continue;
231+
220232
MIB.setInsertPt(*MI.getParent(), MI);
221233
buildOpBitcast(GR, MIB, MI.getOperand(0).getReg(),
222234
MI.getOperand(1).getReg());
@@ -237,16 +249,11 @@ static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
237249
SmallVector<MachineInstr *, 10> ToErase;
238250
for (MachineBasicBlock &MBB : MF) {
239251
for (MachineInstr &MI : MBB) {
240-
if (!isSpvIntrinsic(MI, Intrinsic::spv_bitcast) &&
241-
!isSpvIntrinsic(MI, Intrinsic::spv_ptrcast))
252+
if (!isSpvIntrinsic(MI, Intrinsic::spv_ptrcast))
242253
continue;
243254
assert(MI.getOperand(2).isReg());
244255
MIB.setInsertPt(*MI.getParent(), MI);
245256
ToErase.push_back(&MI);
246-
if (isSpvIntrinsic(MI, Intrinsic::spv_bitcast)) {
247-
MIB.buildBitcast(MI.getOperand(0).getReg(), MI.getOperand(2).getReg());
248-
continue;
249-
}
250257
Register Def = MI.getOperand(0).getReg();
251258
Register Source = MI.getOperand(2).getReg();
252259
Type *ElemTy = getMDOperandAsType(MI.getOperand(3).getMetadata(), 0);
@@ -1089,7 +1096,7 @@ bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
10891096
removeImplicitFallthroughs(MF, MIB);
10901097
insertSpirvDecorations(MF, GR, MIB);
10911098
insertInlineAsm(MF, GR, ST, MIB);
1092-
selectOpBitcasts(MF, GR, MIB);
1099+
lowerBitcasts(MF, GR, MIB);
10931100

10941101
return true;
10951102
}

0 commit comments

Comments
 (0)