[llvm] 01c0a84 - [DirectX] Make dx.RawBuffer an op that can't be replaced (#154620)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 29 13:09:06 PDT 2025
Author: Farzon Lotfi
Date: 2025-08-29T16:09:03-04:00
New Revision: 01c0a8409a21344c822deba9467bd9d547f6e5d8
URL: https://github.com/llvm/llvm-project/commit/01c0a8409a21344c822deba9467bd9d547f6e5d8
DIFF: https://github.com/llvm/llvm-project/commit/01c0a8409a21344c822deba9467bd9d547f6e5d8.diff
LOG: [DirectX] Make dx.RawBuffer an op that can't be replaced (#154620)
fixes #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
Added:
llvm/test/CodeGen/DirectX/issue-152348.ll
llvm/test/CodeGen/DirectX/phi-node-replacement.ll
llvm/test/Transforms/SimplifyCFG/token_like_type.ll
llvm/test/Verifier/tokenlike1-without-asserts.ll
llvm/test/Verifier/tokenlike5.ll
llvm/test/Verifier/tokenlike6.ll
llvm/test/Verifier/tokenlike7.ll
Modified:
clang/test/CodeGenHLSL/builtins/hlsl_resource_t.hlsl
llvm/include/llvm/IR/DerivedTypes.h
llvm/include/llvm/IR/Type.h
llvm/lib/IR/Type.cpp
llvm/lib/IR/Verifier.cpp
llvm/lib/Target/DirectX/DXILResourceAccess.cpp
llvm/lib/Transforms/Utils/Local.cpp
llvm/unittests/Transforms/Utils/LocalTest.cpp
Removed:
################################################################################
diff --git a/clang/test/CodeGenHLSL/builtins/hlsl_resource_t.hlsl b/clang/test/CodeGenHLSL/builtins/hlsl_resource_t.hlsl
index 24114b11c7602..75d9fb8582b3d 100644
--- a/clang/test/CodeGenHLSL/builtins/hlsl_resource_t.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/hlsl_resource_t.hlsl
@@ -2,23 +2,28 @@
using handle_float_t = __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::contained_type(float)]];
+struct CustomResource {
+ handle_float_t h;
+};
+
+// CHECK: %struct.CustomResource = type { target("dx.TypedBuffer", float, 1, 0, 0) }
// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", <4 x float>, 1, 0, 0)
// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", %struct.MyStruct, 0, 0)
// CHECK: %struct.MyStruct = type <{ <4 x float>, <2 x i32> }>
-// CHECK: define hidden void @_Z2faU9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0) %a)
-// CHECK: call void @_Z4foo1U9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0) %0)
-// CHECK: declare hidden void @_Z4foo1U9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0))
+// CHECK: define hidden void @_Z2fa14CustomResource(ptr noundef byval(%struct.CustomResource) align 1 %a)
+// CHECK: call void @_Z4foo114CustomResource(ptr noundef byval(%struct.CustomResource) align 1 %agg.tmp)
+// CHECK: declare hidden void @_Z4foo114CustomResource(ptr noundef byval(%struct.CustomResource) align 1)
-void foo1(handle_float_t res);
+void foo1(CustomResource res);
-void fa(handle_float_t a) {
+void fa(CustomResource a) {
foo1(a);
}
-// CHECK: define hidden void @_Z2fbU9_Res_u_CTfu17__hlsl_resource_t(target("dx.TypedBuffer", float, 1, 0, 0) %a)
-void fb(handle_float_t a) {
- handle_float_t b = a;
+// CHECK: define hidden void @_Z2fb14CustomResource(ptr noundef byval(%struct.CustomResource) align 1 %a)
+void fb(CustomResource a) {
+ CustomResource b = a;
}
// CHECK: define hidden void @_Z2fcN4hlsl8RWBufferIDv4_fEE(ptr noundef byval(%"class.hlsl::RWBuffer") align 4 %a)
diff --git a/llvm/include/llvm/IR/DerivedTypes.h b/llvm/include/llvm/IR/DerivedTypes.h
index fa62bc09b61a3..29b34cfe33964 100644
--- a/llvm/include/llvm/IR/DerivedTypes.h
+++ b/llvm/include/llvm/IR/DerivedTypes.h
@@ -845,8 +845,11 @@ class TargetExtType : public Type {
/// This type may be allocated on the stack, either as the allocated type
/// of an alloca instruction or as a byval function parameter.
CanBeLocal = 1U << 2,
- // This type may be used as an element in a vector.
+ /// This type may be used as an element in a vector.
CanBeVectorElement = 1U << 3,
+ // This type can only be used in intrinsic arguments and return values.
+ /// In particular, it cannot be used in select and phi instructions.
+ IsTokenLike = 1U << 4,
};
/// Returns true if the target extension type contains the given property.
diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h
index 74dd490729741..1c042500ba8ec 100644
--- a/llvm/include/llvm/IR/Type.h
+++ b/llvm/include/llvm/IR/Type.h
@@ -233,6 +233,9 @@ class Type {
/// Return true if this is 'token'.
bool isTokenTy() const { return getTypeID() == TokenTyID; }
+ /// Returns true if this is 'token' or a token-like target type.s
+ bool isTokenLikeTy() const;
+
/// True if this is an instance of IntegerType.
bool isIntegerTy() const { return getTypeID() == IntegerTyID; }
diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp
index 9c34662340352..9db48e8f6a96b 100644
--- a/llvm/lib/IR/Type.cpp
+++ b/llvm/lib/IR/Type.cpp
@@ -1036,7 +1036,8 @@ static TargetTypeInfo getTargetTypeInfo(const TargetExtType *Ty) {
// DirectX resources
if (Name.starts_with("dx."))
return TargetTypeInfo(PointerType::get(C, 0), TargetExtType::CanBeGlobal,
- TargetExtType::CanBeLocal);
+ TargetExtType::CanBeLocal,
+ TargetExtType::IsTokenLike);
// Opaque types in the AMDGPU name space.
if (Name == "amdgcn.named.barrier") {
@@ -1054,6 +1055,14 @@ static TargetTypeInfo getTargetTypeInfo(const TargetExtType *Ty) {
return TargetTypeInfo(Type::getVoidTy(C));
}
+bool Type::isTokenLikeTy() const {
+ if (isTokenTy())
+ return true;
+ if (auto *TT = dyn_cast<TargetExtType>(this))
+ return TT->hasProperty(TargetExtType::Property::IsTokenLike);
+ return false;
+}
+
Type *TargetExtType::getLayoutType() const {
return getTargetTypeInfo(this).LayoutType;
}
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 6e11d6a77cbf7..da05ff166122f 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -3008,7 +3008,7 @@ void Verifier::visitFunction(const Function &F) {
if (!IsIntrinsic) {
Check(!Arg.getType()->isMetadataTy(),
"Function takes metadata but isn't an intrinsic", &Arg, &F);
- Check(!Arg.getType()->isTokenTy(),
+ Check(!Arg.getType()->isTokenLikeTy(),
"Function takes token but isn't an intrinsic", &Arg, &F);
Check(!Arg.getType()->isX86_AMXTy(),
"Function takes x86_amx but isn't an intrinsic", &Arg, &F);
@@ -3022,7 +3022,7 @@ void Verifier::visitFunction(const Function &F) {
}
if (!IsIntrinsic) {
- Check(!F.getReturnType()->isTokenTy(),
+ Check(!F.getReturnType()->isTokenLikeTy(),
"Function returns a token but isn't an intrinsic", &F);
Check(!F.getReturnType()->isX86_AMXTy(),
"Function returns a x86_amx but isn't an intrinsic", &F);
@@ -3636,7 +3636,7 @@ void Verifier::visitPHINode(PHINode &PN) {
"PHI nodes not grouped at top of basic block!", &PN, PN.getParent());
// Check that a PHI doesn't yield a Token.
- Check(!PN.getType()->isTokenTy(), "PHI nodes cannot have token type!");
+ Check(!PN.getType()->isTokenLikeTy(), "PHI nodes cannot have token type!");
// Check that all of the values of the PHI node have the same type as the
// result.
@@ -3841,14 +3841,14 @@ void Verifier::visitCallBase(CallBase &Call) {
for (Type *ParamTy : FTy->params()) {
Check(!ParamTy->isMetadataTy(),
"Function has metadata parameter but isn't an intrinsic", Call);
- Check(!ParamTy->isTokenTy(),
+ Check(!ParamTy->isTokenLikeTy(),
"Function has token parameter but isn't an intrinsic", Call);
}
}
// Verify that indirect calls don't return tokens.
if (!Call.getCalledFunction()) {
- Check(!FTy->getReturnType()->isTokenTy(),
+ Check(!FTy->getReturnType()->isTokenLikeTy(),
"Return type cannot be token for indirect call!");
Check(!FTy->getReturnType()->isX86_AMXTy(),
"Return type cannot be x86_amx for indirect call!");
diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index c33ec0efd73ca..6579d3405cf39 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -8,14 +8,19 @@
#include "DXILResourceAccess.h"
#include "DirectX.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/DXILResource.h"
+#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsDirectX.h"
+#include "llvm/IR/User.h"
#include "llvm/InitializePasses.h"
+#include "llvm/Transforms/Utils/ValueMapper.h"
#define DEBUG_TYPE "dxil-resource-access"
@@ -198,6 +203,112 @@ static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,
llvm_unreachable("Unhandled case in switch");
}
+static SmallVector<Instruction *> collectBlockUseDef(Instruction *Start) {
+ SmallPtrSet<Instruction *, 32> Visited;
+ SmallVector<Instruction *, 32> Worklist;
+ SmallVector<Instruction *> Out;
+ auto *BB = Start->getParent();
+
+ // Seed with direct users in this block.
+ for (User *U : Start->users()) {
+ if (auto *I = dyn_cast<Instruction>(U)) {
+ if (I->getParent() == BB)
+ Worklist.push_back(I);
+ }
+ }
+
+ // BFS over transitive users, constrained to the same block.
+ while (!Worklist.empty()) {
+ Instruction *I = Worklist.pop_back_val();
+ if (!Visited.insert(I).second)
+ continue;
+ Out.push_back(I);
+
+ for (User *U : I->users()) {
+ if (auto *J = dyn_cast<Instruction>(U)) {
+ if (J->getParent() == BB)
+ Worklist.push_back(J);
+ }
+ }
+ for (Use &V : I->operands()) {
+ if (auto *J = dyn_cast<Instruction>(V)) {
+ if (J->getParent() == BB && V != Start)
+ Worklist.push_back(J);
+ }
+ }
+ }
+
+ // Order results in program order.
+ DenseMap<const Instruction *, unsigned> Ord;
+ unsigned Idx = 0;
+ for (Instruction &I : *BB)
+ Ord[&I] = Idx++;
+
+ llvm::sort(Out, [&](Instruction *A, Instruction *B) {
+ return Ord.lookup(A) < Ord.lookup(B);
+ });
+
+ return Out;
+}
+
+static void phiNodeRemapHelper(PHINode *Phi, BasicBlock *BB,
+ IRBuilder<> &Builder,
+ SmallVector<Instruction *> &UsesInBlock) {
+
+ ValueToValueMapTy VMap;
+ Value *Val = Phi->getIncomingValueForBlock(BB);
+ VMap[Phi] = Val;
+ Builder.SetInsertPoint(&BB->back());
+ for (Instruction *I : UsesInBlock) {
+ // don't clone over the Phi just remap them
+ if (auto *PhiNested = dyn_cast<PHINode>(I)) {
+ VMap[PhiNested] = PhiNested->getIncomingValueForBlock(BB);
+ continue;
+ }
+ Instruction *Clone = I->clone();
+ RemapInstruction(Clone, VMap,
+ RF_NoModuleLevelChanges | RF_IgnoreMissingLocals);
+ Builder.Insert(Clone);
+ VMap[I] = Clone;
+ }
+}
+
+static void phiNodeReplacement(IntrinsicInst *II,
+ SmallVectorImpl<Instruction *> &PrevBBDeadInsts,
+ SetVector<BasicBlock *> &DeadBB) {
+ SmallVector<Instruction *> CurrBBDeadInsts;
+ for (User *U : II->users()) {
+ auto *Phi = dyn_cast<PHINode>(U);
+ if (!Phi)
+ continue;
+
+ IRBuilder<> Builder(Phi);
+ SmallVector<Instruction *> UsesInBlock = collectBlockUseDef(Phi);
+ bool HasReturnUse = isa<ReturnInst>(UsesInBlock.back());
+
+ for (unsigned I = 0, E = Phi->getNumIncomingValues(); I < E; I++) {
+ auto *CurrIncomingBB = Phi->getIncomingBlock(I);
+ phiNodeRemapHelper(Phi, CurrIncomingBB, Builder, UsesInBlock);
+ if (HasReturnUse)
+ PrevBBDeadInsts.push_back(&CurrIncomingBB->back());
+ }
+
+ CurrBBDeadInsts.push_back(Phi);
+
+ for (Instruction *I : UsesInBlock) {
+ CurrBBDeadInsts.push_back(I);
+ }
+ if (HasReturnUse) {
+ BasicBlock *PhiBB = Phi->getParent();
+ DeadBB.insert(PhiBB);
+ }
+ }
+ // Traverse the now-dead instructions in RPO and remove them.
+ for (Instruction *Dead : llvm::reverse(CurrBBDeadInsts))
+ Dead->eraseFromParent();
+ CurrBBDeadInsts.clear();
+}
+
static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
// Process users keeping track of indexing accumulated from GEPs.
struct AccessAndOffset {
@@ -229,7 +340,6 @@ static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
} else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {
createLoadIntrinsic(II, LI, Current.Offset, RTI);
DeadInsts.push_back(LI);
-
} else
llvm_unreachable("Unhandled instruction - pointer escaped?");
}
@@ -242,13 +352,27 @@ static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
SmallVector<std::pair<IntrinsicInst *, dxil::ResourceTypeInfo>> Resources;
- for (BasicBlock &BB : F)
+ SetVector<BasicBlock *> DeadBB;
+ SmallVector<Instruction *> PrevBBDeadInsts;
+ for (BasicBlock &BB : make_early_inc_range(F)) {
+ for (Instruction &I : make_early_inc_range(BB))
+ if (auto *II = dyn_cast<IntrinsicInst>(&I))
+ if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer)
+ phiNodeReplacement(II, PrevBBDeadInsts, DeadBB);
+
for (Instruction &I : BB)
if (auto *II = dyn_cast<IntrinsicInst>(&I))
if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
Resources.emplace_back(II, DRTM[HandleTy]);
}
+ }
+ for (auto *Dead : PrevBBDeadInsts)
+ Dead->eraseFromParent();
+ PrevBBDeadInsts.clear();
+ for (auto *Dead : DeadBB)
+ Dead->eraseFromParent();
+ DeadBB.clear();
for (auto &[II, RI] : Resources)
replaceAccess(II, RI);
@@ -279,7 +403,6 @@ class DXILResourceAccessLegacy : public FunctionPass {
bool runOnFunction(Function &F) override {
DXILResourceTypeMap &DRTM =
getAnalysis<DXILResourceTypeWrapperPass>().getResourceTypeMap();
-
return transformResourcePointers(F, DRTM);
}
StringRef getPassName() const override { return "DXIL Resource Access"; }
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 9d759bc244e3d..b94ed7db91580 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -3839,7 +3839,7 @@ void llvm::maybeMarkSanitizerLibraryCallNoBuiltin(
bool llvm::canReplaceOperandWithVariable(const Instruction *I, unsigned OpIdx) {
const auto *Op = I->getOperand(OpIdx);
// We can't have a PHI with a metadata or token type.
- if (Op->getType()->isMetadataTy() || Op->getType()->isTokenTy())
+ if (Op->getType()->isMetadataTy() || Op->getType()->isTokenLikeTy())
return false;
// swifterror pointers can only be used by a load, store, or as a swifterror
diff --git a/llvm/test/CodeGen/DirectX/issue-152348.ll b/llvm/test/CodeGen/DirectX/issue-152348.ll
new file mode 100644
index 0000000000000..aa0179d82b09e
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/issue-152348.ll
@@ -0,0 +1,158 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -dxil-resource-type -dxil-resource-access -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; NOTE: The two LLVM IR functions below are simplified versions of this HLSL
+; RWStructuredBuffer<float16_t> a;
+; RWStructuredBuffer<float16_t> b;
+; RWStructuredBuffer<float16_t> c;
+; cbuffer d {
+; uint e;
+; uint f;
+; uint g;
+; uint h;
+; }
+; [numthreads(6, 8, 1)] void CSMain() {
+; if (h) {
+; float16_t i = b[f];
+; c[g] = i;
+; } else if(h == g) {
+; float16_t i = b[g];
+; c[h] = i;
+; } else {
+; float16_t i = a[e];
+; c[g] = i;
+; }
+; }
+
+%__cblayout_d = type <{ i32, i32, i32, i32 }>
+
+ at .str = internal unnamed_addr constant [2 x i8] c"a\00", align 1
+ at d.cb = local_unnamed_addr global target("dx.CBuffer", target("dx.Layout", %__cblayout_d, 16, 0, 4, 8, 12)) poison
+ at e = external hidden local_unnamed_addr addrspace(2) global i32, align 4
+ at d.str = internal unnamed_addr constant [2 x i8] c"d\00", align 1
+
+define void @CSMain() local_unnamed_addr {
+; CHECK-LABEL: define void @CSMain() local_unnamed_addr {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALLRAWBUFFERBINDING:%.*]] = tail call target("dx.CBuffer", target("dx.Layout", [[__CBLAYOUT_D:%.*]], 16, 0, 4, 8, 12)) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_tdx.Layout_s___cblayout_ds_16_0_4_8_12tt(i32 3, i32 0, i32 1, i32 0, ptr nonnull @d.str)
+; CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", [[__CBLAYOUT_D]], 16, 0, 4, 8, 12)) [[CALLRAWBUFFERBINDING]], ptr @d.cb, align 4
+; CHECK-NEXT: [[LOADE:%.*]] = load i32, ptr addrspace(2) @e, align 4
+; CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[LOADE]], 0
+; CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_ELSE_I:.*]], label %[[IF_THEN_I:.*]]
+; CHECK: [[IF_THEN_I]]:
+; CHECK-NEXT: [[IFSTMTCALLRAWBUFFERBINDING:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP0:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP1:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[IFSTMTCALLRAWBUFFERBINDING]], i32 [[LOADE]], i32 0)
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { half, i1 } [[TMP1]], 0
+; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_f16_1_0t.f16(target("dx.RawBuffer", half, 1, 0) [[TMP0]], i32 [[LOADE]], i32 0, half [[TMP2]])
+; CHECK-NEXT: br label %[[_Z6CSMAINV_EXIT:.*]]
+; CHECK: [[IF_ELSE_I]]:
+; CHECK-NEXT: [[CALL2NDRAWBUFFERBINDING:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP3:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP4:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[CALL2NDRAWBUFFERBINDING]], i32 [[LOADE]], i32 0)
+; CHECK-NEXT: [[TMP5:%.*]] = extractvalue { half, i1 } [[TMP4]], 0
+; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_f16_1_0t.f16(target("dx.RawBuffer", half, 1, 0) [[TMP3]], i32 [[LOADE]], i32 0, half [[TMP5]])
+; CHECK-NEXT: br label %[[_Z6CSMAINV_EXIT]]
+; CHECK: [[_Z6CSMAINV_EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %callCBufferBinding = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_d, 16, 0, 4, 8, 12)) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_tdx.Layout_s___cblayout_ds_16_0_4_8_12tt(i32 3, i32 0, i32 1, i32 0, ptr nonnull @d.str)
+ store target("dx.CBuffer", target("dx.Layout", %__cblayout_d, 16, 0, 4, 8, 12)) %callCBufferBinding, ptr @d.cb, align 4
+ %loadE = load i32, ptr addrspace(2) @e, align 4
+ %tobool.not.i = icmp eq i32 %loadE, 0
+ br i1 %tobool.not.i, label %if.else.i, label %if.then.i
+
+if.then.i: ; preds = %entry
+ %ifStmtcallRawBufferBinding = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %ifStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %ifStmtcallRawBufferBinding, i32 %loadE)
+ br label %_Z6CSMainv.exit
+
+if.else.i: ; preds = %entry
+ %call2ndRawBufferBinding = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %elseStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %call2ndRawBufferBinding, i32 %loadE)
+ br label %_Z6CSMainv.exit
+
+_Z6CSMainv.exit: ; preds = %if.else.i, %if.then.i
+ %.sink1 = phi ptr [ %ifStmtCallResourceGEP, %if.then.i ], [ %elseStmtCallResourceGEP, %if.else.i ]
+ %call3rdRawBufferBinding = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %sinkCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %call3rdRawBufferBinding, i32 %loadE)
+ %loadSink = load half, ptr %.sink1, align 2
+ store half %loadSink, ptr %sinkCallResourceGEP, align 2
+ ret void
+}
+
+define void @Main() local_unnamed_addr {
+; CHECK-LABEL: define void @Main() local_unnamed_addr {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALLRAWBUFFERBINDING1:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[CALLRAWBUFFERBINDING0:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[CALLRAWBUFFERBINDING:%.*]] = tail call target("dx.CBuffer", target("dx.Layout", [[__CBLAYOUT_D:%.*]], 16, 0, 4, 8, 12)) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_tdx.Layout_s___cblayout_ds_16_0_4_8_12tt(i32 3, i32 0, i32 1, i32 0, ptr nonnull @d.str)
+; CHECK-NEXT: store target("dx.CBuffer", target("dx.Layout", [[__CBLAYOUT_D]], 16, 0, 4, 8, 12)) [[CALLRAWBUFFERBINDING]], ptr @d.cb, align 4
+; CHECK-NEXT: [[LOADE:%.*]] = load i32, ptr addrspace(2) @e, align 4
+; CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[LOADE]], 0
+; CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_ELSE_I:.*]], label %[[IF_THEN_I:.*]]
+; CHECK: [[IF_THEN_I]]:
+; CHECK-NEXT: [[IFSTMTLOADE:%.*]] = load i32, ptr addrspace(2) @e, align 4
+; CHECK-NEXT: [[TMP0:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP1:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[CALLRAWBUFFERBINDING0]], i32 [[LOADE]], i32 0)
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { half, i1 } [[TMP1]], 0
+; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_f16_1_0t.f16(target("dx.RawBuffer", half, 1, 0) [[TMP0]], i32 [[IFSTMTLOADE]], i32 0, half [[TMP2]])
+; CHECK-NEXT: br label %[[_Z6MAINV_EXIT:.*]]
+; CHECK: [[IF_ELSE_I]]:
+; CHECK-NEXT: [[ELSESTMTLOADE:%.*]] = load i32, ptr addrspace(2) @e, align 4
+; CHECK-NEXT: [[CMP_I:%.*]] = icmp eq i32 [[ELSESTMTLOADE]], 0
+; CHECK-NEXT: br i1 [[CMP_I]], label %[[IF_THEN2_I:.*]], label %[[IF_ELSE6_I:.*]]
+; CHECK: [[IF_THEN2_I]]:
+; CHECK-NEXT: [[TMP3:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP4:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[CALLRAWBUFFERBINDING0]], i32 0, i32 0)
+; CHECK-NEXT: [[TMP5:%.*]] = extractvalue { half, i1 } [[TMP4]], 0
+; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_f16_1_0t.f16(target("dx.RawBuffer", half, 1, 0) [[TMP3]], i32 0, i32 0, half [[TMP5]])
+; CHECK-NEXT: br label %[[_Z6MAINV_EXIT]]
+; CHECK: [[IF_ELSE6_I]]:
+; CHECK-NEXT: [[ELSESTMTLOADE2:%.*]] = load i32, ptr addrspace(2) @e, align 4
+; CHECK-NEXT: [[TMP6:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP7:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[CALLRAWBUFFERBINDING1]], i32 [[ELSESTMTLOADE2]], i32 0)
+; CHECK-NEXT: [[TMP8:%.*]] = extractvalue { half, i1 } [[TMP7]], 0
+; CHECK-NEXT: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_f16_1_0t.f16(target("dx.RawBuffer", half, 1, 0) [[TMP6]], i32 [[ELSESTMTLOADE]], i32 0, half [[TMP8]])
+; CHECK-NEXT: br label %[[_Z6MAINV_EXIT]]
+; CHECK: [[_Z6MAINV_EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %callRawBufferBinding1 = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %callRawBufferBinding0 = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %callCBufferBinding = tail call target("dx.CBuffer", target("dx.Layout", %__cblayout_d, 16, 0, 4, 8, 12)) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_tdx.Layout_s___cblayout_ds_16_0_4_8_12tt(i32 3, i32 0, i32 1, i32 0, ptr nonnull @d.str)
+ store target("dx.CBuffer", target("dx.Layout", %__cblayout_d, 16, 0, 4, 8, 12)) %callCBufferBinding, ptr @d.cb, align 4
+ %loadE = load i32, ptr addrspace(2) @e, align 4
+ %tobool.not.i = icmp eq i32 %loadE, 0
+ br i1 %tobool.not.i, label %if.else.i, label %if.then.i
+
+if.then.i: ; preds = %entry
+ %ifStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %callRawBufferBinding0, i32 %loadE)
+ %ifStmtLoadE = load i32, ptr addrspace(2) @e, align 4
+ br label %_Z6Mainv.exit
+
+if.else.i: ; preds = %entry
+ %elseStmtLoadE = load i32, ptr addrspace(2) @e, align 4
+ %cmp.i = icmp eq i32 %elseStmtLoadE, 0
+ br i1 %cmp.i, label %if.then2.i, label %if.else6.i
+
+if.then2.i: ; preds = %if.else.i
+ %elseifStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %callRawBufferBinding0, i32 0)
+ br label %_Z6Mainv.exit
+
+if.else6.i: ; preds = %if.else.i
+ %elseStmtLoadE2 = load i32, ptr addrspace(2) @e, align 4
+ %elseStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %callRawBufferBinding1, i32 %elseStmtLoadE2)
+ br label %_Z6Mainv.exit
+
+_Z6Mainv.exit: ; preds = %if.else6.i, %if.then2.i, %if.then.i
+ %.sink2 = phi i32 [ %ifStmtLoadE, %if.then.i ], [ 0, %if.then2.i ], [ %elseStmtLoadE, %if.else6.i ]
+ %.sink.in = phi ptr [ %ifStmtCallResourceGEP, %if.then.i ], [ %elseifStmtCallResourceGEP, %if.then2.i ], [ %elseStmtCallResourceGEP, %if.else6.i ]
+ %callRawBufferBindingSink = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 2, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %.sink = load half, ptr %.sink.in, align 2
+ %i11 = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %callRawBufferBindingSink, i32 %.sink2)
+ store half %.sink, ptr %i11, align 2
+ ret void
+}
diff --git a/llvm/test/CodeGen/DirectX/phi-node-replacement.ll b/llvm/test/CodeGen/DirectX/phi-node-replacement.ll
new file mode 100644
index 0000000000000..6aef126cb5ecd
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/phi-node-replacement.ll
@@ -0,0 +1,42 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -dxil-resource-type -dxil-resource-access -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+%"$Globals" = type { i32 }
+ at CBV = external constant %"$Globals"
+ at .str = internal unnamed_addr constant [2 x i8] c"a\00", align 1
+
+define half @CSMain() local_unnamed_addr {
+; CHECK-LABEL: define half @CSMain() local_unnamed_addr {
+; CHECK-NEXT: [[LOADGLOBAL:%.*]] = load i32, ptr @CBV, align 4
+; CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[LOADGLOBAL]], 0
+; CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_ELSE_I:.*]], label %[[IF_THEN_I:.*]]
+; CHECK: [[IF_THEN_I]]:
+; CHECK-NEXT: [[IFSTMTCALLRAWBUFFERBINDING:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP1:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[IFSTMTCALLRAWBUFFERBINDING]], i32 [[LOADGLOBAL]], i32 0)
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { half, i1 } [[TMP1]], 0
+; CHECK-NEXT: ret half [[TMP2]]
+; CHECK: [[IF_ELSE_I]]:
+; CHECK-NEXT: [[CALL2NDRAWBUFFERBINDING:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[TMP3:%.*]] = call { half, i1 } @llvm.dx.resource.load.rawbuffer.f16.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[CALL2NDRAWBUFFERBINDING]], i32 [[LOADGLOBAL]], i32 0)
+; CHECK-NEXT: [[TMP4:%.*]] = extractvalue { half, i1 } [[TMP3]], 0
+; CHECK-NEXT: ret half [[TMP4]]
+;
+ %loadGlobal = load i32, ptr @CBV, align 4
+ %tobool.not.i = icmp eq i32 %loadGlobal, 0
+ br i1 %tobool.not.i, label %if.else.i, label %if.then.i
+
+ if.then.i: ; preds = %entry
+ %ifStmtcallRawBufferBinding = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %ifStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %ifStmtcallRawBufferBinding, i32 %loadGlobal)
+ br label %_Z6CSMainv.exit
+
+ if.else.i: ; preds = %entry
+ %call2ndRawBufferBinding = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %elseStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %call2ndRawBufferBinding, i32 %loadGlobal)
+ br label %_Z6CSMainv.exit
+
+ _Z6CSMainv.exit: ; preds = %if.else.i, %if.then.i
+ %.sink1 = phi ptr [ %ifStmtCallResourceGEP, %if.then.i ], [ %elseStmtCallResourceGEP, %if.else.i ]
+ %loadSink = load half, ptr %.sink1, align 2
+ ret half %loadSink
+}
diff --git a/llvm/test/Transforms/SimplifyCFG/token_like_type.ll b/llvm/test/Transforms/SimplifyCFG/token_like_type.ll
new file mode 100644
index 0000000000000..21a14ccebed86
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/token_like_type.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=simplifycfg %s | FileCheck %s
+
+; NOTE: The Check lines should exactly match the IR.
+
+%"$Globals" = type { i32 }
+ at CBV = external constant %"$Globals"
+ at .str = internal unnamed_addr constant [2 x i8] c"a\00", align 1
+
+define half @CSMain() local_unnamed_addr {
+; CHECK-LABEL: define half @CSMain() local_unnamed_addr {
+; CHECK-NEXT: [[LOADGLOBAL:%.*]] = load i32, ptr @CBV, align 4
+; CHECK-NEXT: [[TOBOOL_NOT_I:%.*]] = icmp eq i32 [[LOADGLOBAL]], 0
+; CHECK-NEXT: br i1 [[TOBOOL_NOT_I]], label %[[IF_ELSE_I:.*]], label %[[IF_THEN_I:.*]]
+; CHECK: [[IF_THEN_I]]:
+; CHECK-NEXT: [[IFSTMTCALLRAWBUFFERBINDING:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[IFSTMTCALLRESOURCEGEP:%.*]] = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[IFSTMTCALLRAWBUFFERBINDING]], i32 [[LOADGLOBAL]])
+; CHECK-NEXT: br label %[[_Z6CSMAINV_EXIT:.*]]
+; CHECK: [[IF_ELSE_I]]:
+; CHECK-NEXT: [[CALL2NDRAWBUFFERBINDING:%.*]] = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+; CHECK-NEXT: [[ELSESTMTCALLRESOURCEGEP:%.*]] = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) [[CALL2NDRAWBUFFERBINDING]], i32 [[LOADGLOBAL]])
+; CHECK-NEXT: br label %[[_Z6CSMAINV_EXIT]]
+; CHECK: [[_Z6CSMAINV_EXIT]]:
+; CHECK-NEXT: [[DOTSINK1:%.*]] = phi ptr [ [[IFSTMTCALLRESOURCEGEP]], %[[IF_THEN_I]] ], [ [[ELSESTMTCALLRESOURCEGEP]], %[[IF_ELSE_I]] ]
+; CHECK-NEXT: [[LOADSINK:%.*]] = load half, ptr [[DOTSINK1]], align 2
+; CHECK-NEXT: ret half [[LOADSINK]]
+;
+ %loadGlobal = load i32, ptr @CBV, align 4
+ %tobool.not.i = icmp eq i32 %loadGlobal, 0
+ br i1 %tobool.not.i, label %if.else.i, label %if.then.i
+
+ if.then.i: ; preds = %entry
+ %ifStmtcallRawBufferBinding = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 1, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %ifStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %ifStmtcallRawBufferBinding, i32 %loadGlobal)
+ br label %_Z6CSMainv.exit
+
+ if.else.i: ; preds = %entry
+ %call2ndRawBufferBinding = tail call target("dx.RawBuffer", half, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f16_1_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str)
+ %elseStmtCallResourceGEP = tail call noundef nonnull align 2 dereferenceable(2) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f16_1_0t(target("dx.RawBuffer", half, 1, 0) %call2ndRawBufferBinding, i32 %loadGlobal)
+ br label %_Z6CSMainv.exit
+
+ _Z6CSMainv.exit: ; preds = %if.else.i, %if.then.i
+ %.sink1 = phi ptr [ %ifStmtCallResourceGEP, %if.then.i ], [ %elseStmtCallResourceGEP, %if.else.i ]
+ %loadSink = load half, ptr %.sink1, align 2
+ ret half %loadSink
+}
diff --git a/llvm/test/Verifier/tokenlike1-without-asserts.ll b/llvm/test/Verifier/tokenlike1-without-asserts.ll
new file mode 100644
index 0000000000000..ef7ac00576251
--- /dev/null
+++ b/llvm/test/Verifier/tokenlike1-without-asserts.ll
@@ -0,0 +1,12 @@
+; REQUIRES: !asserts
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+define void @f(target("dx.RawBuffer", half, 1, 0) %A, target("dx.RawBuffer", half, 1, 0) %B) {
+entry:
+ br label %bb
+
+bb:
+ %phi = phi target("dx.RawBuffer", half, 1, 0) [ %A, %bb ], [ %B, %entry]
+; CHECK: PHI nodes cannot have token type!
+ br label %bb
+}
diff --git a/llvm/test/Verifier/tokenlike5.ll b/llvm/test/Verifier/tokenlike5.ll
new file mode 100644
index 0000000000000..ea36f37b547ca
--- /dev/null
+++ b/llvm/test/Verifier/tokenlike5.ll
@@ -0,0 +1,7 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+define void @f(target("dx.RawBuffer", half, 1, 0) %A) {
+entry:
+ ret void
+}
+; CHECK: Function takes token but isn't an intrinsic
diff --git a/llvm/test/Verifier/tokenlike6.ll b/llvm/test/Verifier/tokenlike6.ll
new file mode 100644
index 0000000000000..f24a9427eb8cb
--- /dev/null
+++ b/llvm/test/Verifier/tokenlike6.ll
@@ -0,0 +1,7 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+define target("dx.RawBuffer", half, 1, 0) @f() {
+entry:
+ ret target("dx.RawBuffer", half, 1, 0) poison
+}
+; CHECK: Function returns a token but isn't an intrinsic
diff --git a/llvm/test/Verifier/tokenlike7.ll b/llvm/test/Verifier/tokenlike7.ll
new file mode 100644
index 0000000000000..c7454937bb7ef
--- /dev/null
+++ b/llvm/test/Verifier/tokenlike7.ll
@@ -0,0 +1,8 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+define void @f() {
+entry:
+ call target("dx.RawBuffer", half, 1, 0) () poison ()
+ ret void
+}
+; CHECK: Return type cannot be token for indirect call!
diff --git a/llvm/unittests/Transforms/Utils/LocalTest.cpp b/llvm/unittests/Transforms/Utils/LocalTest.cpp
index 0c70feb64e7e4..4b53cc3d6e516 100644
--- a/llvm/unittests/Transforms/Utils/LocalTest.cpp
+++ b/llvm/unittests/Transforms/Utils/LocalTest.cpp
@@ -1057,6 +1057,14 @@ TEST(Local, SimplifyCFGWithNullAC) {
RequireAndPreserveDomTree ? &DTU : nullptr, Options));
}
+TEST(LocalTest, TargetTypeInfoHasNoReplacementProperty) {
+ LLVMContext Ctx;
+ SmallVector<unsigned, 3> Ints = {};
+ auto *TT = llvm::TargetExtType::get(Ctx, "dx.RawBuffer", {}, Ints);
+
+ EXPECT_TRUE(TT->hasProperty(TargetExtType::Property::IsTokenLike));
+}
+
TEST(Local, CanReplaceOperandWithVariable) {
LLVMContext Ctx;
Module M("test_module", Ctx);
More information about the llvm-commits
mailing list