[llvm] 380bb51 - [HLSL] Adding Flatten and Branch if attributes with test fixes (#122157)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 13 10:31:33 PST 2025
Author: joaosaffran
Date: 2025-01-13T10:31:25-08:00
New Revision: 380bb51b70b6d9f3da07a87f56fc3fe44bc78691
URL: https://github.com/llvm/llvm-project/commit/380bb51b70b6d9f3da07a87f56fc3fe44bc78691
DIFF: https://github.com/llvm/llvm-project/commit/380bb51b70b6d9f3da07a87f56fc3fe44bc78691.diff
LOG: [HLSL] Adding Flatten and Branch if attributes with test fixes (#122157)
- Adding the changes from PRs:
- #116331
- #121852
- Fixes test `tools/dxil-dis/debug-info.ll`
- Address some missed comments in the previous PR
---------
Co-authored-by: joaosaffran <joao.saffran at microsoft.com>
Added:
clang/test/AST/HLSL/HLSLControlFlowHint.hlsl
clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl
llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll
llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll
llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll
Modified:
clang/include/clang/Basic/Attr.td
clang/lib/CodeGen/CGStmt.cpp
clang/lib/CodeGen/CodeGenFunction.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/Sema/SemaStmtAttr.cpp
llvm/include/llvm/IR/IntrinsicsSPIRV.td
llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c0632aaa516255..a752d94b06fad6 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4353,6 +4353,16 @@ def HLSLLoopHint: StmtAttr {
let Documentation = [HLSLLoopHintDocs, HLSLUnrollHintDocs];
}
+def HLSLControlFlowHint: StmtAttr {
+ /// [branch]
+ /// [flatten]
+ let Spellings = [Microsoft<"branch">, Microsoft<"flatten">];
+ let Subjects = SubjectList<[IfStmt],
+ ErrorDiag, "'if' statements">;
+ let LangOpts = [HLSL];
+ let Documentation = [InternalOnly];
+}
+
def CapturedRecord : InheritableAttr {
// This attribute has no spellings as it is only ever created implicitly.
let Spellings = [];
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index f9258a396b7d07..4ba8ee1ca17d4e 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -760,6 +760,8 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
bool noinline = false;
bool alwaysinline = false;
bool noconvergent = false;
+ HLSLControlFlowHintAttr::Spelling flattenOrBranch =
+ HLSLControlFlowHintAttr::SpellingNotCalculated;
const CallExpr *musttail = nullptr;
for (const auto *A : S.getAttrs()) {
@@ -791,6 +793,9 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
Builder.CreateAssumption(AssumptionVal);
}
} break;
+ case attr::HLSLControlFlowHint: {
+ flattenOrBranch = cast<HLSLControlFlowHintAttr>(A)->getSemanticSpelling();
+ } break;
}
}
SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge);
@@ -798,6 +803,7 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
SaveAndRestore save_alwaysinline(InAlwaysInlineAttributedStmt, alwaysinline);
SaveAndRestore save_noconvergent(InNoConvergentAttributedStmt, noconvergent);
SaveAndRestore save_musttail(MustTailCall, musttail);
+ SaveAndRestore save_flattenOrBranch(HLSLControlFlowAttr, flattenOrBranch);
EmitStmt(S.getSubStmt(), S.getAttrs());
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index d6f3716afabdf0..11fdddba1144bb 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -40,6 +40,7 @@
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/FPEnv.h"
+#include "llvm/IR/Instruction.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/MDBuilder.h"
@@ -2086,7 +2087,30 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
Weights = createProfileWeights(TrueCount, CurrentCount - TrueCount);
}
- Builder.CreateCondBr(CondV, TrueBlock, FalseBlock, Weights, Unpredictable);
+ llvm::Instruction *BrInst = Builder.CreateCondBr(CondV, TrueBlock, FalseBlock,
+ Weights, Unpredictable);
+ switch (HLSLControlFlowAttr) {
+ case HLSLControlFlowHintAttr::Microsoft_branch:
+ case HLSLControlFlowHintAttr::Microsoft_flatten: {
+ llvm::MDBuilder MDHelper(CGM.getLLVMContext());
+
+ llvm::ConstantInt *BranchHintConstant =
+ HLSLControlFlowAttr ==
+ HLSLControlFlowHintAttr::Spelling::Microsoft_branch
+ ? llvm::ConstantInt::get(CGM.Int32Ty, 1)
+ : llvm::ConstantInt::get(CGM.Int32Ty, 2);
+
+ SmallVector<llvm::Metadata *, 2> Vals(
+ {MDHelper.createString("hlsl.controlflow.hint"),
+ MDHelper.createConstant(BranchHintConstant)});
+ BrInst->setMetadata("hlsl.controlflow.hint",
+ llvm::MDNode::get(CGM.getLLVMContext(), Vals));
+ break;
+ }
+ // This is required to avoid warnings during compilation
+ case HLSLControlFlowHintAttr::SpellingNotCalculated:
+ break;
+ }
}
/// ErrorUnsupported - Print out an error that codegen doesn't support the
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 311f2ae94d0463..b115c15bf01a95 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -615,6 +615,10 @@ class CodeGenFunction : public CodeGenTypeCache {
/// True if the current statement has noconvergent attribute.
bool InNoConvergentAttributedStmt = false;
+ /// HLSL Branch attribute.
+ HLSLControlFlowHintAttr::Spelling HLSLControlFlowAttr =
+ HLSLControlFlowHintAttr::SpellingNotCalculated;
+
// The CallExpr within the current statement that the musttail attribute
// applies to. nullptr if there is no 'musttail' on the current statement.
const CallExpr *MustTailCall = nullptr;
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 106e2430de901e..422d8abc1028aa 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -619,6 +619,12 @@ static Attr *handleHLSLLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
return ::new (S.Context) HLSLLoopHintAttr(S.Context, A, UnrollFactor);
}
+static Attr *handleHLSLControlFlowHint(Sema &S, Stmt *St, const ParsedAttr &A,
+ SourceRange Range) {
+
+ return ::new (S.Context) HLSLControlFlowHintAttr(S.Context, A);
+}
+
static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
SourceRange Range) {
if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute)
@@ -655,6 +661,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
return handleLoopHintAttr(S, St, A, Range);
case ParsedAttr::AT_HLSLLoopHint:
return handleHLSLLoopHintAttr(S, St, A, Range);
+ case ParsedAttr::AT_HLSLControlFlowHint:
+ return handleHLSLControlFlowHint(S, St, A, Range);
case ParsedAttr::AT_OpenCLUnrollHint:
return handleOpenCLUnrollHint(S, St, A, Range);
case ParsedAttr::AT_Suppress:
diff --git a/clang/test/AST/HLSL/HLSLControlFlowHint.hlsl b/clang/test/AST/HLSL/HLSLControlFlowHint.hlsl
new file mode 100644
index 00000000000000..a36779c05fbc93
--- /dev/null
+++ b/clang/test/AST/HLSL/HLSLControlFlowHint.hlsl
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -ast-dump %s | FileCheck %s
+
+// CHECK: FunctionDecl 0x{{[0-9A-Fa-f]+}} <{{.*}}> {{.*}} used branch 'int (int)'
+// CHECK: AttributedStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>
+// CHECK-NEXT: -HLSLControlFlowHintAttr 0x{{[0-9A-Fa-f]+}} <{{.*}}> branch
+export int branch(int X){
+ int resp;
+ [branch] if (X > 0) {
+ resp = -X;
+ } else {
+ resp = X * 2;
+ }
+
+ return resp;
+}
+
+// CHECK: FunctionDecl 0x{{[0-9A-Fa-f]+}} <{{.*}}> {{.*}} used flatten 'int (int)'
+// CHECK: AttributedStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>
+// CHECK-NEXT: -HLSLControlFlowHintAttr 0x{{[0-9A-Fa-f]+}} <{{.*}}> flatten
+export int flatten(int X){
+ int resp;
+ [flatten] if (X > 0) {
+ resp = -X;
+ } else {
+ resp = X * 2;
+ }
+
+ return resp;
+}
+
+// CHECK: FunctionDecl 0x{{[0-9A-Fa-f]+}} <{{.*}}> {{.*}} used no_attr 'int (int)'
+// CHECK-NOT: AttributedStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>
+// CHECK-NOT: -HLSLControlFlowHintAttr
+export int no_attr(int X){
+ int resp;
+ if (X > 0) {
+ resp = -X;
+ } else {
+ resp = X * 2;
+ }
+
+ return resp;
+}
diff --git a/clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl b/clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl
new file mode 100644
index 00000000000000..aa13b275818502
--- /dev/null
+++ b/clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s -fnative-half-type -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple spirv-vulkan-library %s -fnative-half-type -emit-llvm -o - | FileCheck %s
+
+// CHECK: define {{.*}} i32 {{.*}}test_branch{{.*}}(i32 {{.*}} [[VALD:%.*]])
+// CHECK: [[PARAM:%.*]] = load i32, ptr [[VALD]].addr, align 4
+// CHECK: [[CMP:%.*]] = icmp sgt i32 [[PARAM]], 0
+// CHECK: br i1 [[CMP]], label %if.then, label %if.else, !hlsl.controlflow.hint [[HINT_BRANCH:![0-9]+]]
+export int test_branch(int X){
+ int resp;
+ [branch] if (X > 0) {
+ resp = -X;
+ } else {
+ resp = X * 2;
+ }
+
+ return resp;
+}
+
+// CHECK: define {{.*}} i32 {{.*}}test_flatten{{.*}}(i32 {{.*}} [[VALD:%.*]])
+// CHECK: [[PARAM:%.*]] = load i32, ptr [[VALD]].addr, align 4
+// CHECK: [[CMP:%.*]] = icmp sgt i32 [[PARAM]], 0
+// CHECK: br i1 [[CMP]], label %if.then, label %if.else, !hlsl.controlflow.hint [[HINT_FLATTEN:![0-9]+]]
+export int test_flatten(int X){
+ int resp;
+ [flatten] if (X > 0) {
+ resp = -X;
+ } else {
+ resp = X * 2;
+ }
+
+ return resp;
+}
+
+// CHECK: define {{.*}} i32 {{.*}}test_no_attr{{.*}}(i32 {{.*}} [[VALD:%.*]])
+// CHECK-NOT: !hlsl.controlflow.hint
+export int test_no_attr(int X){
+ int resp;
+ if (X > 0) {
+ resp = -X;
+ } else {
+ resp = X * 2;
+ }
+
+ return resp;
+}
+
+//CHECK: [[HINT_BRANCH]] = !{!"hlsl.controlflow.hint", i32 1}
+//CHECK: [[HINT_FLATTEN]] = !{!"hlsl.controlflow.hint", i32 2}
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index b4d2dce66a6f0b..37057271b6c284 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -33,7 +33,7 @@ let TargetPrefix = "spv" in {
def int_spv_ptrcast : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty, llvm_i32_ty], [ImmArg<ArgIndex<2>>]>;
def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
def int_spv_loop_merge : Intrinsic<[], [llvm_vararg_ty]>;
- def int_spv_selection_merge : Intrinsic<[], [llvm_vararg_ty]>;
+ def int_spv_selection_merge : Intrinsic<[], [llvm_any_ty, llvm_i32_ty], [ImmArg<ArgIndex<1>>]>;
def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>;
def int_spv_unreachable : Intrinsic<[], []>;
def int_spv_alloca : Intrinsic<[llvm_any_ty], [llvm_i8_ty], [ImmArg<ArgIndex<0>>]>;
diff --git a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
index 5afe6b2d2883db..5fd5c226eef894 100644
--- a/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
+++ b/llvm/lib/Target/DirectX/DXILTranslateMetadata.cpp
@@ -15,12 +15,14 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Analysis/DXILMetadataAnalysis.h"
#include "llvm/Analysis/DXILResource.h"
+#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
@@ -300,6 +302,38 @@ static MDTuple *emitTopLevelLibraryNode(Module &M, MDNode *RMD,
return constructEntryMetadata(nullptr, nullptr, RMD, Properties, Ctx);
}
+// TODO: We might need to refactor this to be more generic,
+// in case we need more metadata to be replaced.
+static void translateBranchMetadata(Module &M) {
+ for (Function &F : M) {
+ for (BasicBlock &BB : F) {
+ Instruction *BBTerminatorInst = BB.getTerminator();
+
+ MDNode *HlslControlFlowMD =
+ BBTerminatorInst->getMetadata("hlsl.controlflow.hint");
+
+ if (!HlslControlFlowMD)
+ continue;
+
+ assert(HlslControlFlowMD->getNumOperands() == 2 &&
+ "invalid operands for hlsl.controlflow.hint");
+
+ MDBuilder MDHelper(M.getContext());
+ ConstantInt *Op1 =
+ mdconst::extract<ConstantInt>(HlslControlFlowMD->getOperand(1));
+
+ SmallVector<llvm::Metadata *, 2> Vals(
+ ArrayRef<Metadata *>{MDHelper.createString("dx.controlflow.hints"),
+ MDHelper.createConstant(Op1)});
+
+ MDNode *MDNode = llvm::MDNode::get(M.getContext(), Vals);
+
+ BBTerminatorInst->setMetadata("dx.controlflow.hints", MDNode);
+ BBTerminatorInst->setMetadata("hlsl.controlflow.hint", nullptr);
+ }
+ }
+}
+
static void translateMetadata(Module &M, DXILBindingMap &DBM,
DXILResourceTypeMap &DRTM,
const Resources &MDResources,
@@ -372,6 +406,7 @@ PreservedAnalyses DXILTranslateMetadata::run(Module &M,
const dxil::ModuleMetadataInfo MMDI = MAM.getResult<DXILMetadataAnalysis>(M);
translateMetadata(M, DBM, DRTM, MDResources, ShaderFlags, MMDI);
+ translateBranchMetadata(M);
return PreservedAnalyses::all();
}
@@ -409,6 +444,7 @@ class DXILTranslateMetadataLegacy : public ModulePass {
getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
translateMetadata(M, DBM, DRTM, MDResources, ShaderFlags, MMDI);
+ translateBranchMetadata(M);
return true;
}
};
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index b7b32dd0d626c6..1d6be7619ecf4b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -33,6 +33,7 @@
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/IR/IntrinsicsSPIRV.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
#define DEBUG_TYPE "spirv-isel"
@@ -45,6 +46,17 @@ using ExtInstList =
namespace {
+llvm::SPIRV::SelectionControl::SelectionControl
+getSelectionOperandForImm(int Imm) {
+ if (Imm == 2)
+ return SPIRV::SelectionControl::Flatten;
+ if (Imm == 1)
+ return SPIRV::SelectionControl::DontFlatten;
+ if (Imm == 0)
+ return SPIRV::SelectionControl::None;
+ llvm_unreachable("Invalid immediate");
+}
+
#define GET_GLOBALISEL_PREDICATE_BITSET
#include "SPIRVGenGlobalISel.inc"
#undef GET_GLOBALISEL_PREDICATE_BITSET
@@ -2818,12 +2830,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
}
return MIB.constrainAllUses(TII, TRI, RBI);
}
- case Intrinsic::spv_loop_merge:
- case Intrinsic::spv_selection_merge: {
- const auto Opcode = IID == Intrinsic::spv_selection_merge
- ? SPIRV::OpSelectionMerge
- : SPIRV::OpLoopMerge;
- auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode));
+ case Intrinsic::spv_loop_merge: {
+ auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLoopMerge));
for (unsigned i = 1; i < I.getNumExplicitOperands(); ++i) {
assert(I.getOperand(i).isMBB());
MIB.addMBB(I.getOperand(i).getMBB());
@@ -2831,6 +2839,15 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
MIB.addImm(SPIRV::SelectionControl::None);
return MIB.constrainAllUses(TII, TRI, RBI);
}
+ case Intrinsic::spv_selection_merge: {
+ auto MIB =
+ BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpSelectionMerge));
+ assert(I.getOperand(1).isMBB() &&
+ "operand 1 to spv_selection_merge must be a basic block");
+ MIB.addMBB(I.getOperand(1).getMBB());
+ MIB.addImm(getSelectionOperandForImm(I.getOperand(2).getImm()));
+ return MIB.constrainAllUses(TII, TRI, RBI);
+ }
case Intrinsic::spv_cmpxchg:
return selectAtomicCmpXchg(ResVReg, ResType, I);
case Intrinsic::spv_unreachable:
diff --git a/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp b/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp
index 336cde4e782246..2e4343c7922f1c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp
@@ -18,14 +18,16 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/CodeGen/IntrinsicLowering.h"
-#include "llvm/IR/Analysis.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/IR/LegacyPassManager.h"
#include "llvm/InitializePasses.h"
+#include "llvm/PassRegistry.h"
+#include "llvm/Transforms/Utils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/LoopSimplify.h"
#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
@@ -646,8 +648,7 @@ class SPIRVStructurizer : public FunctionPass {
Builder.SetInsertPoint(Header->getTerminator());
auto MergeAddress = BlockAddress::get(BB.getParent(), &BB);
- SmallVector<Value *, 1> Args = {MergeAddress};
- Builder.CreateIntrinsic(Intrinsic::spv_selection_merge, {}, {Args});
+ createOpSelectMerge(&Builder, MergeAddress);
Modified = true;
}
@@ -769,10 +770,9 @@ class SPIRVStructurizer : public FunctionPass {
BasicBlock *Merge = Candidates[0];
auto MergeAddress = BlockAddress::get(Merge->getParent(), Merge);
- SmallVector<Value *, 1> Args = {MergeAddress};
IRBuilder<> Builder(&BB);
Builder.SetInsertPoint(BB.getTerminator());
- Builder.CreateIntrinsic(Intrinsic::spv_selection_merge, {}, {Args});
+ createOpSelectMerge(&Builder, MergeAddress);
}
return Modified;
@@ -1105,8 +1105,7 @@ class SPIRVStructurizer : public FunctionPass {
Builder.SetInsertPoint(Header->getTerminator());
auto MergeAddress = BlockAddress::get(Merge->getParent(), Merge);
- SmallVector<Value *, 1> Args = {MergeAddress};
- Builder.CreateIntrinsic(Intrinsic::spv_selection_merge, {}, {Args});
+ createOpSelectMerge(&Builder, MergeAddress);
continue;
}
@@ -1120,8 +1119,7 @@ class SPIRVStructurizer : public FunctionPass {
Builder.SetInsertPoint(Header->getTerminator());
auto MergeAddress = BlockAddress::get(NewMerge->getParent(), NewMerge);
- SmallVector<Value *, 1> Args = {MergeAddress};
- Builder.CreateIntrinsic(Intrinsic::spv_selection_merge, {}, {Args});
+ createOpSelectMerge(&Builder, MergeAddress);
}
return Modified;
@@ -1208,6 +1206,27 @@ class SPIRVStructurizer : public FunctionPass {
AU.addPreserved<SPIRVConvergenceRegionAnalysisWrapperPass>();
FunctionPass::getAnalysisUsage(AU);
}
+
+ void createOpSelectMerge(IRBuilder<> *Builder, BlockAddress *MergeAddress) {
+ Instruction *BBTerminatorInst = Builder->GetInsertBlock()->getTerminator();
+
+ MDNode *MDNode = BBTerminatorInst->getMetadata("hlsl.controlflow.hint");
+
+ ConstantInt *BranchHint = llvm::ConstantInt::get(Builder->getInt32Ty(), 0);
+
+ if (MDNode) {
+ assert(MDNode->getNumOperands() == 2 &&
+ "invalid metadata hlsl.controlflow.hint");
+ BranchHint = mdconst::extract<ConstantInt>(MDNode->getOperand(1));
+
+ assert(BranchHint && "invalid metadata value for hlsl.controlflow.hint");
+ }
+
+ llvm::SmallVector<llvm::Value *, 2> Args = {MergeAddress, BranchHint};
+
+ Builder->CreateIntrinsic(Intrinsic::spv_selection_merge,
+ {MergeAddress->getType()}, {Args});
+ }
};
} // namespace llvm
@@ -1229,8 +1248,11 @@ FunctionPass *llvm::createSPIRVStructurizerPass() {
PreservedAnalyses SPIRVStructurizerWrapper::run(Function &F,
FunctionAnalysisManager &AF) {
- FunctionPass *StructurizerPass = createSPIRVStructurizerPass();
- if (!StructurizerPass->runOnFunction(F))
+
+ auto FPM = legacy::FunctionPassManager(F.getParent());
+ FPM.add(createSPIRVStructurizerPass());
+
+ if (!FPM.run(F))
return PreservedAnalyses::all();
PreservedAnalyses PA;
PA.preserveSet<CFGAnalyses>();
diff --git a/llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll b/llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll
new file mode 100644
index 00000000000000..6a5274429930ea
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/HLSLControlFlowHint.ll
@@ -0,0 +1,98 @@
+; RUN: opt -S -dxil-op-lower -dxil-translate-metadata -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+; This test make sure LLVM metadata is being translated into DXIL.
+
+
+; CHECK: define i32 @test_branch(i32 %X)
+; CHECK-NOT: hlsl.controlflow.hint
+; CHECK: br i1 %cmp, label %if.then, label %if.else, !dx.controlflow.hints [[HINT_BRANCH:![0-9]+]]
+define i32 @test_branch(i32 %X) {
+entry:
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !0
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+
+
+; CHECK: define i32 @test_flatten(i32 %X)
+; CHECK-NOT: hlsl.controlflow.hint
+; CHECK: br i1 %cmp, label %if.then, label %if.else, !dx.controlflow.hints [[HINT_FLATTEN:![0-9]+]]
+define i32 @test_flatten(i32 %X) {
+entry:
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !1
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+
+
+; CHECK: define i32 @test_no_attr(i32 %X)
+; CHECK-NOT: hlsl.controlflow.hint
+; CHECK-NOT: !dx.controlflow.hints
+define i32 @test_no_attr(i32 %X) {
+entry:
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+; CHECK-NOT: hlsl.controlflow.hint
+; CHECK: [[HINT_BRANCH]] = !{!"dx.controlflow.hints", i32 1}
+; CHECK: [[HINT_FLATTEN]] = !{!"dx.controlflow.hints", i32 2}
+!0 = !{!"hlsl.controlflow.hint", i32 1}
+!1 = !{!"hlsl.controlflow.hint", i32 2}
diff --git a/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll
new file mode 100644
index 00000000000000..9911b3119ce52a
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint-pass-check.ll
@@ -0,0 +1,90 @@
+; RUN: opt -passes='spirv-structurizer' -S -mtriple=spirv-unknown-unknown %s | FileCheck %s
+
+; CHECK-LABEL: define spir_func noundef i32 @test_branch
+; CHECK: call void @llvm.spv.selection.merge.p0(ptr blockaddress(@test_branch, %if.end), i32 1)
+; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !{{[0-9]+}}
+define spir_func noundef i32 @test_branch(i32 noundef %X) {
+entry:
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !0
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+
+; CHECK-LABEL: define spir_func noundef i32 @test_flatten
+; CHECK: call void @llvm.spv.selection.merge.p0(ptr blockaddress(@test_flatten, %if.end), i32 2)
+; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !{{[0-9]+}}
+define spir_func noundef i32 @test_flatten(i32 noundef %X) {
+entry:
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !1
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+; CHECK-LABEL: define spir_func noundef i32 @test_no_attr
+; CHECK: call void @llvm.spv.selection.merge.p0(ptr blockaddress(@test_no_attr, %if.end), i32 0)
+; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.else
+define spir_func noundef i32 @test_no_attr(i32 noundef %X) {
+entry:
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+
+!0 = !{!"hlsl.controlflow.hint", i32 1}
+!1 = !{!"hlsl.controlflow.hint", i32 2}
diff --git a/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll
new file mode 100644
index 00000000000000..848eaf70f5a199
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/structurizer/HLSLControlFlowHint.ll
@@ -0,0 +1,91 @@
+; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+
+define spir_func noundef i32 @test_branch(i32 noundef %X) {
+entry:
+; CHECK-LABEL: ; -- Begin function test_branch
+; OpSelectionMerge %[[#]] DontFlatten
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !0
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+
+
+define spir_func noundef i32 @test_flatten(i32 noundef %X) {
+entry:
+; CHECK-LABEL: ; -- Begin function test_flatten
+; OpSelectionMerge %[[#]] Flatten
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else, !hlsl.controlflow.hint !1
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+
+define spir_func noundef i32 @test_no_attr(i32 noundef %X) {
+entry:
+; CHECK-LABEL: ; -- Begin function test_no_attr
+; OpSelectionMerge %[[#]] None
+ %X.addr = alloca i32, align 4
+ %resp = alloca i32, align 4
+ store i32 %X, ptr %X.addr, align 4
+ %0 = load i32, ptr %X.addr, align 4
+ %cmp = icmp sgt i32 %0, 0
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %1 = load i32, ptr %X.addr, align 4
+ %sub = sub nsw i32 0, %1
+ store i32 %sub, ptr %resp, align 4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %2 = load i32, ptr %X.addr, align 4
+ %mul = mul nsw i32 %2, 2
+ store i32 %mul, ptr %resp, align 4
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %3 = load i32, ptr %resp, align 4
+ ret i32 %3
+}
+
+!0 = !{!"hlsl.controlflow.hint", i32 1}
+!1 = !{!"hlsl.controlflow.hint", i32 2}
More information about the llvm-commits
mailing list