Skip to content

Commit 01c0a84

Browse files
authored
[DirectX] Make dx.RawBuffer an op that can't be replaced (llvm#154620)
fixes llvm#152348 SimplifyCFG collapses raw buffer store from a if\else load into a select. This change prevents the TargetExtType dx.Rawbuffer from being replace thus preserving the if\else blocks. A further change was needed to eliminate the phi node before we process Intrinsic::dx_resource_getpointer in DXILResourceAccess.cpp
1 parent 7fc5838 commit 01c0a84

File tree

15 files changed

+450
-19
lines changed

15 files changed

+450
-19
lines changed

clang/test/CodeGenHLSL/builtins/hlsl_resource_t.hlsl

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,28 @@
22

33
using handle_float_t = __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::contained_type(float)]];
44

5+
struct CustomResource {
6+
handle_float_t h;
7+
};
8+
9+
// CHECK: %struct.CustomResource = type { target("dx.TypedBuffer", float, 1, 0, 0) }
510
// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
611
// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", %struct.MyStruct, 0, 0)
712
// CHECK: %struct.MyStruct = type <{ <4 x float>, <2 x i32> }>
813

9-
// CHECK: define hidden void @_Z2faU9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0) %a)
10-
// CHECK: call void @_Z4foo1U9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0) %0)
11-
// CHECK: declare hidden void @_Z4foo1U9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0))
14+
// CHECK: define hidden void @_Z2fa14CustomResource(ptr noundef byval(%struct.CustomResource) align 1 %a)
15+
// CHECK: call void @_Z4foo114CustomResource(ptr noundef byval(%struct.CustomResource) align 1 %agg.tmp)
16+
// CHECK: declare hidden void @_Z4foo114CustomResource(ptr noundef byval(%struct.CustomResource) align 1)
1217

13-
void foo1(handle_float_t res);
18+
void foo1(CustomResource res);
1419

15-
void fa(handle_float_t a) {
20+
void fa(CustomResource a) {
1621
foo1(a);
1722
}
1823

19-
// CHECK: define hidden void @_Z2fbU9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0) %a)
20-
void fb(handle_float_t a) {
21-
handle_float_t b = a;
24+
// CHECK: define hidden void @_Z2fb14CustomResource(ptr noundef byval(%struct.CustomResource) align 1 %a)
25+
void fb(CustomResource a) {
26+
CustomResource b = a;
2227
}
2328

2429
// CHECK: define hidden void @_Z2fcN4hlsl8RWBufferIDv4_fEE(ptr noundef byval(%"class.hlsl::RWBuffer") align 4 %a)

llvm/include/llvm/IR/DerivedTypes.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,8 +845,11 @@ class TargetExtType : public Type {
845845
/// This type may be allocated on the stack, either as the allocated type
846846
/// of an alloca instruction or as a byval function parameter.
847847
CanBeLocal = 1U << 2,
848-
// This type may be used as an element in a vector.
848+
/// This type may be used as an element in a vector.
849849
CanBeVectorElement = 1U << 3,
850+
// This type can only be used in intrinsic arguments and return values.
851+
/// In particular, it cannot be used in select and phi instructions.
852+
IsTokenLike = 1U << 4,
850853
};
851854

852855
/// Returns true if the target extension type contains the given property.

llvm/include/llvm/IR/Type.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ class Type {
233233
/// Return true if this is 'token'.
234234
bool isTokenTy() const { return getTypeID() == TokenTyID; }
235235

236+
/// Returns true if this is 'token' or a token-like target type.s
237+
bool isTokenLikeTy() const;
238+
236239
/// True if this is an instance of IntegerType.
237240
bool isIntegerTy() const { return getTypeID() == IntegerTyID; }
238241

llvm/lib/IR/Type.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,8 @@ static TargetTypeInfo getTargetTypeInfo(const TargetExtType *Ty) {
10361036
// DirectX resources
10371037
if (Name.starts_with("dx."))
10381038
return TargetTypeInfo(PointerType::get(C, 0), TargetExtType::CanBeGlobal,
1039-
TargetExtType::CanBeLocal);
1039+
TargetExtType::CanBeLocal,
1040+
TargetExtType::IsTokenLike);
10401041

10411042
// Opaque types in the AMDGPU name space.
10421043
if (Name == "amdgcn.named.barrier") {
@@ -1054,6 +1055,14 @@ static TargetTypeInfo getTargetTypeInfo(const TargetExtType *Ty) {
10541055
return TargetTypeInfo(Type::getVoidTy(C));
10551056
}
10561057

1058+
bool Type::isTokenLikeTy() const {
1059+
if (isTokenTy())
1060+
return true;
1061+
if (auto *TT = dyn_cast<TargetExtType>(this))
1062+
return TT->hasProperty(TargetExtType::Property::IsTokenLike);
1063+
return false;
1064+
}
1065+
10571066
Type *TargetExtType::getLayoutType() const {
10581067
return getTargetTypeInfo(this).LayoutType;
10591068
}

llvm/lib/IR/Verifier.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3008,7 +3008,7 @@ void Verifier::visitFunction(const Function &F) {
30083008
if (!IsIntrinsic) {
30093009
Check(!Arg.getType()->isMetadataTy(),
30103010
"Function takes metadata but isn't an intrinsic", &Arg, &F);
3011-
Check(!Arg.getType()->isTokenTy(),
3011+
Check(!Arg.getType()->isTokenLikeTy(),
30123012
"Function takes token but isn't an intrinsic", &Arg, &F);
30133013
Check(!Arg.getType()->isX86_AMXTy(),
30143014
"Function takes x86_amx but isn't an intrinsic", &Arg, &F);
@@ -3022,7 +3022,7 @@ void Verifier::visitFunction(const Function &F) {
30223022
}
30233023

30243024
if (!IsIntrinsic) {
3025-
Check(!F.getReturnType()->isTokenTy(),
3025+
Check(!F.getReturnType()->isTokenLikeTy(),
30263026
"Function returns a token but isn't an intrinsic", &F);
30273027
Check(!F.getReturnType()->isX86_AMXTy(),
30283028
"Function returns a x86_amx but isn't an intrinsic", &F);
@@ -3636,7 +3636,7 @@ void Verifier::visitPHINode(PHINode &PN) {
36363636
"PHI nodes not grouped at top of basic block!", &PN, PN.getParent());
36373637

36383638
// Check that a PHI doesn't yield a Token.
3639-
Check(!PN.getType()->isTokenTy(), "PHI nodes cannot have token type!");
3639+
Check(!PN.getType()->isTokenLikeTy(), "PHI nodes cannot have token type!");
36403640

36413641
// Check that all of the values of the PHI node have the same type as the
36423642
// result.
@@ -3841,14 +3841,14 @@ void Verifier::visitCallBase(CallBase &Call) {
38413841
for (Type *ParamTy : FTy->params()) {
38423842
Check(!ParamTy->isMetadataTy(),
38433843
"Function has metadata parameter but isn't an intrinsic", Call);
3844-
Check(!ParamTy->isTokenTy(),
3844+
Check(!ParamTy->isTokenLikeTy(),
38453845
"Function has token parameter but isn't an intrinsic", Call);
38463846
}
38473847
}
38483848

38493849
// Verify that indirect calls don't return tokens.
38503850
if (!Call.getCalledFunction()) {
3851-
Check(!FTy->getReturnType()->isTokenTy(),
3851+
Check(!FTy->getReturnType()->isTokenLikeTy(),
38523852
"Return type cannot be token for indirect call!");
38533853
Check(!FTy->getReturnType()->isX86_AMXTy(),
38543854
"Return type cannot be x86_amx for indirect call!");

llvm/lib/Target/DirectX/DXILResourceAccess.cpp

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@
88

99
#include "DXILResourceAccess.h"
1010
#include "DirectX.h"
11+
#include "llvm/ADT/SetVector.h"
1112
#include "llvm/Analysis/DXILResource.h"
13+
#include "llvm/IR/BasicBlock.h"
1214
#include "llvm/IR/Dominators.h"
1315
#include "llvm/IR/IRBuilder.h"
16+
#include "llvm/IR/Instruction.h"
1417
#include "llvm/IR/Instructions.h"
1518
#include "llvm/IR/IntrinsicInst.h"
1619
#include "llvm/IR/Intrinsics.h"
1720
#include "llvm/IR/IntrinsicsDirectX.h"
21+
#include "llvm/IR/User.h"
1822
#include "llvm/InitializePasses.h"
23+
#include "llvm/Transforms/Utils/ValueMapper.h"
1924

2025
#define DEBUG_TYPE "dxil-resource-access"
2126

@@ -198,6 +203,112 @@ static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,
198203
llvm_unreachable("Unhandled case in switch");
199204
}
200205

206+
static SmallVector<Instruction *> collectBlockUseDef(Instruction *Start) {
207+
SmallPtrSet<Instruction *, 32> Visited;
208+
SmallVector<Instruction *, 32> Worklist;
209+
SmallVector<Instruction *> Out;
210+
auto *BB = Start->getParent();
211+
212+
// Seed with direct users in this block.
213+
for (User *U : Start->users()) {
214+
if (auto *I = dyn_cast<Instruction>(U)) {
215+
if (I->getParent() == BB)
216+
Worklist.push_back(I);
217+
}
218+
}
219+
220+
// BFS over transitive users, constrained to the same block.
221+
while (!Worklist.empty()) {
222+
Instruction *I = Worklist.pop_back_val();
223+
if (!Visited.insert(I).second)
224+
continue;
225+
Out.push_back(I);
226+
227+
for (User *U : I->users()) {
228+
if (auto *J = dyn_cast<Instruction>(U)) {
229+
if (J->getParent() == BB)
230+
Worklist.push_back(J);
231+
}
232+
}
233+
for (Use &V : I->operands()) {
234+
if (auto *J = dyn_cast<Instruction>(V)) {
235+
if (J->getParent() == BB && V != Start)
236+
Worklist.push_back(J);
237+
}
238+
}
239+
}
240+
241+
// Order results in program order.
242+
DenseMap<const Instruction *, unsigned> Ord;
243+
unsigned Idx = 0;
244+
for (Instruction &I : *BB)
245+
Ord[&I] = Idx++;
246+
247+
llvm::sort(Out, [&](Instruction *A, Instruction *B) {
248+
return Ord.lookup(A) < Ord.lookup(B);
249+
});
250+
251+
return Out;
252+
}
253+
254+
static void phiNodeRemapHelper(PHINode *Phi, BasicBlock *BB,
255+
IRBuilder<> &Builder,
256+
SmallVector<Instruction *> &UsesInBlock) {
257+
258+
ValueToValueMapTy VMap;
259+
Value *Val = Phi->getIncomingValueForBlock(BB);
260+
VMap[Phi] = Val;
261+
Builder.SetInsertPoint(&BB->back());
262+
for (Instruction *I : UsesInBlock) {
263+
// don't clone over the Phi just remap them
264+
if (auto *PhiNested = dyn_cast<PHINode>(I)) {
265+
VMap[PhiNested] = PhiNested->getIncomingValueForBlock(BB);
266+
continue;
267+
}
268+
Instruction *Clone = I->clone();
269+
RemapInstruction(Clone, VMap,
270+
RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
271+
Builder.Insert(Clone);
272+
VMap[I] = Clone;
273+
}
274+
}
275+
276+
static void phiNodeReplacement(IntrinsicInst *II,
277+
SmallVectorImpl<Instruction *> &PrevBBDeadInsts,
278+
SetVector<BasicBlock *> &DeadBB) {
279+
SmallVector<Instruction *> CurrBBDeadInsts;
280+
for (User *U : II->users()) {
281+
auto *Phi = dyn_cast<PHINode>(U);
282+
if (!Phi)
283+
continue;
284+
285+
IRBuilder<> Builder(Phi);
286+
SmallVector<Instruction *> UsesInBlock = collectBlockUseDef(Phi);
287+
bool HasReturnUse = isa<ReturnInst>(UsesInBlock.back());
288+
289+
for (unsigned I = 0, E = Phi->getNumIncomingValues(); I < E; I++) {
290+
auto *CurrIncomingBB = Phi->getIncomingBlock(I);
291+
phiNodeRemapHelper(Phi, CurrIncomingBB, Builder, UsesInBlock);
292+
if (HasReturnUse)
293+
PrevBBDeadInsts.push_back(&CurrIncomingBB->back());
294+
}
295+
296+
CurrBBDeadInsts.push_back(Phi);
297+
298+
for (Instruction *I : UsesInBlock) {
299+
CurrBBDeadInsts.push_back(I);
300+
}
301+
if (HasReturnUse) {
302+
BasicBlock *PhiBB = Phi->getParent();
303+
DeadBB.insert(PhiBB);
304+
}
305+
}
306+
// Traverse the now-dead instructions in RPO and remove them.
307+
for (Instruction *Dead : llvm::reverse(CurrBBDeadInsts))
308+
Dead->eraseFromParent();
309+
CurrBBDeadInsts.clear();
310+
}
311+
201312
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
202313
// Process users keeping track of indexing accumulated from GEPs.
203314
struct AccessAndOffset {
@@ -229,7 +340,6 @@ static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
229340
} else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {
230341
createLoadIntrinsic(II, LI, Current.Offset, RTI);
231342
DeadInsts.push_back(LI);
232-
233343
} else
234344
llvm_unreachable("Unhandled instruction - pointer escaped?");
235345
}
@@ -242,13 +352,27 @@ static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
242352

243353
static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
244354
SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources;
245-
for (BasicBlock &BB : F)
355+
SetVector<BasicBlock *> DeadBB;
356+
SmallVector<Instruction *> PrevBBDeadInsts;
357+
for (BasicBlock &BB : make_early_inc_range(F)) {
358+
for (Instruction &I : make_early_inc_range(BB))
359+
if (auto *II = dyn_cast<IntrinsicInst>(&I))
360+
if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer)
361+
phiNodeReplacement(II, PrevBBDeadInsts, DeadBB);
362+
246363
for (Instruction &I : BB)
247364
if (auto *II = dyn_cast<IntrinsicInst>(&I))
248365
if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
249366
auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
250367
Resources.emplace_back(II, DRTM[HandleTy]);
251368
}
369+
}
370+
for (auto *Dead : PrevBBDeadInsts)
371+
Dead->eraseFromParent();
372+
PrevBBDeadInsts.clear();
373+
for (auto *Dead : DeadBB)
374+
Dead->eraseFromParent();
375+
DeadBB.clear();
252376

253377
for (auto &[II, RI] : Resources)
254378
replaceAccess(II, RI);
@@ -279,7 +403,6 @@ class DXILResourceAccessLegacy : public FunctionPass {
279403
bool runOnFunction(Function &F) override {
280404
DXILResourceTypeMap &DRTM =
281405
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
282-
283406
return transformResourcePointers(F, DRTM);
284407
}
285408
StringRef getPassName() const override { return "DXIL Resource Access"; }

llvm/lib/Transforms/Utils/Local.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3839,7 +3839,7 @@ void llvm::maybeMarkSanitizerLibraryCallNoBuiltin(
38393839
bool llvm::canReplaceOperandWithVariable(const Instruction *I, unsigned OpIdx) {
38403840
const auto *Op = I->getOperand(OpIdx);
38413841
// We can't have a PHI with a metadata or token type.
3842-
if (Op->getType()->isMetadataTy() || Op->getType()->isTokenTy())
3842+
if (Op->getType()->isMetadataTy() || Op->getType()->isTokenLikeTy())
38433843
return false;
38443844

38453845
// swifterror pointers can only be used by a load, store, or as a swifterror

0 commit comments

Comments
 (0)