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.saff...@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} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits