Skip to content

Commit 786aef0

Browse files
committed
[DirectX] add DXIL validation of "llvm.loop" metadata
1 parent dfa87b6 commit 786aef0

File tree

4 files changed

+262
-5
lines changed

4 files changed

+262
-5
lines changed

llvm/lib/Target/DirectX/DXILValidateMetadata.cpp

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@
99
#include "DXILValidateMetadata.h"
1010
#include "DXILTranslateMetadata.h"
1111
#include "DirectX.h"
12+
#include "llvm/ADT/STLExtras.h"
13+
#include "llvm/ADT/StringRef.h"
1214
#include "llvm/ADT/Twine.h"
1315
#include "llvm/Analysis/DXILMetadataAnalysis.h"
1416
#include "llvm/IR/BasicBlock.h"
17+
#include "llvm/IR/Constants.h"
1518
#include "llvm/IR/DiagnosticInfo.h"
1619
#include "llvm/IR/DiagnosticPrinter.h"
20+
#include "llvm/IR/LLVMContext.h"
1721
#include "llvm/IR/Metadata.h"
1822
#include "llvm/IR/Module.h"
1923
#include "llvm/InitializePasses.h"
24+
#include "llvm/Support/Casting.h"
2025
#include "llvm/Support/ErrorHandling.h"
2126

2227
using namespace llvm;
@@ -50,10 +55,96 @@ static bool reportError(Module &M, Twine Message,
5055
return true;
5156
}
5257

58+
static bool reportLoopError(Module &M, Twine Message,
59+
DiagnosticSeverity Severity = DS_Error) {
60+
return reportError(M, Twine("Invalid \"llvm.loop\" metadata: ") + Message,
61+
Severity);
62+
}
63+
5364
} // namespace
5465

66+
static void validateLoopMetadata(Module &M, MDNode *LoopMD) {
67+
// DXIL only accepts the following loop hints:
68+
// llvm.loop.unroll.disable, llvm.loop.unroll.full, llvm.loop.unroll.count
69+
std::array<StringLiteral, 3> ValidHintNames = {"llvm.loop.unroll.count",
70+
"llvm.loop.unroll.disable",
71+
"llvm.loop.unroll.full"};
72+
73+
// llvm.loop metadata must have it's first operand be a self-reference, so we
74+
// require at least 1 operand.
75+
//
76+
// It only makes sense to specify up to 1 of the hints on a branch, so we can
77+
// have at most 2 operands.
78+
79+
if (LoopMD->getNumOperands() != 1 && LoopMD->getNumOperands() != 2) {
80+
reportLoopError(M, "Requires exactly 1 or 2 operands");
81+
return;
82+
}
83+
84+
if (LoopMD != LoopMD->getOperand(0)) {
85+
reportLoopError(M, "First operand must be a self-reference");
86+
return;
87+
}
88+
89+
// A node only containing a self-reference is a valid use to denote a loop
90+
if (LoopMD->getNumOperands() == 1)
91+
return;
92+
93+
LoopMD = dyn_cast<MDNode>(LoopMD->getOperand(1));
94+
if (!LoopMD) {
95+
reportLoopError(M, "Second operand must be a metadata node");
96+
return;
97+
}
98+
99+
if (LoopMD->getNumOperands() != 1 && LoopMD->getNumOperands() != 2) {
100+
reportLoopError(M, "Requires exactly 1 or 2 operands");
101+
return;
102+
}
103+
104+
// It is valid to have a chain of self-referential loop metadata nodes so if
105+
// we have another self-reference, recurse.
106+
//
107+
// Eg:
108+
// !0 = !{!0, !1}
109+
// !1 = !{!1, !2}
110+
// !2 = !{"llvm.loop.unroll.disable"}
111+
if (LoopMD == LoopMD->getOperand(0))
112+
return validateLoopMetadata(M, LoopMD);
113+
114+
// Otherwise, we are at our base hint metadata node
115+
auto *HintStr = dyn_cast<MDString>(LoopMD->getOperand(0));
116+
if (!HintStr || !llvm::is_contained(ValidHintNames, HintStr->getString())) {
117+
reportLoopError(M,
118+
"First operand must be a valid \"llvm.loop.unroll\" hint");
119+
return;
120+
}
121+
122+
// Ensure count node is a constant integer value
123+
auto ValidCountNode = [](MDNode *HintMD) -> bool {
124+
if (HintMD->getNumOperands() == 2)
125+
if (auto *CountMD = dyn_cast<ConstantAsMetadata>(HintMD->getOperand(1)))
126+
if (isa<ConstantInt>(CountMD->getValue()))
127+
return true;
128+
return false;
129+
};
130+
131+
if (HintStr->getString() == "llvm.loop.unroll.count" &&
132+
!ValidCountNode(LoopMD)) {
133+
reportLoopError(M, "Second operand of \"llvm.loop.unroll.count\" "
134+
"must be a constant integer");
135+
return;
136+
}
137+
}
138+
55139
static void validateInstructionMetadata(Module &M) {
56-
llvm::errs() << "hello from new pass!\n";
140+
unsigned char MDLoopKind = M.getContext().getMDKindID("llvm.loop");
141+
142+
for (Function &F : M)
143+
for (BasicBlock &BB : F)
144+
for (Instruction &I : BB) {
145+
if (MDNode *LoopMD = I.getMetadata(MDLoopKind))
146+
validateLoopMetadata(M, LoopMD);
147+
}
57148
}
58149

59150
static void validateGlobalMetadata(Module &M,
@@ -104,6 +195,7 @@ class DXILValidateMetadataLegacy : public ModulePass {
104195
dxil::ModuleMetadataInfo MMDI =
105196
getAnalysis<DXILMetadataAnalysisWrapperPass>().getModuleMetadata();
106197
validateGlobalMetadata(M, MMDI);
198+
validateInstructionMetadata(M);
107199
return true;
108200
}
109201
};
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
; RUN: split-file %s %t
2+
; RUN: not opt -S --dxil-validate-metadata %t/args.ll 2>&1 | FileCheck %t/args.ll
3+
; RUN: not opt -S --dxil-validate-metadata %t/not-ref.ll 2>&1 | FileCheck %t/not-ref.ll
4+
; RUN: not opt -S --dxil-validate-metadata %t/not-md.ll 2>&1 | FileCheck %t/not-md.ll
5+
; RUN: not opt -S --dxil-validate-metadata %t/not-str.ll 2>&1 | FileCheck %t/not-str.ll
6+
; RUN: not opt -S --dxil-validate-metadata %t/bad-count.ll 2>&1 | FileCheck %t/bad-count.ll
7+
8+
; Test that loop metadata is validated as with the DXIL validator
9+
10+
;--- args.ll
11+
12+
; CHECK: Invalid "llvm.loop" metadata: Requires exactly 1 or 2 operands
13+
14+
target triple = "dxilv1.0-unknown-shadermodel6.0-library"
15+
16+
define void @example_loop(i32 %n) {
17+
entry:
18+
br label %loop.header
19+
20+
loop.header:
21+
%i = phi i32 [ 0, %entry ], [ %i.next, %loop.body ]
22+
%cmp = icmp slt i32 %i, %n
23+
br i1 %cmp, label %loop.body, label %exit
24+
25+
loop.body:
26+
%i.next = add nsw i32 %i, 1
27+
br label %loop.header, !llvm.loop !1
28+
29+
exit:
30+
ret void
31+
}
32+
33+
!1 = !{!1, !1, !1} ; too many args
34+
35+
;--- not-ref.ll
36+
37+
; CHECK: Invalid "llvm.loop" metadata: First operand must be a self-reference
38+
39+
target triple = "dxilv1.0-unknown-shadermodel6.0-library"
40+
41+
define void @example_loop(i32 %n) {
42+
entry:
43+
br label %loop.header
44+
45+
loop.header:
46+
%i = phi i32 [ 0, %entry ], [ %i.next, %loop.body ]
47+
%cmp = icmp slt i32 %i, %n
48+
br i1 %cmp, label %loop.body, label %exit
49+
50+
loop.body:
51+
%i.next = add nsw i32 %i, 1
52+
br label %loop.header, !llvm.loop !1
53+
54+
exit:
55+
ret void
56+
}
57+
58+
!1 = !{i32 0} ; not a self-reference
59+
60+
;--- not-md.ll
61+
62+
; CHECK: Invalid "llvm.loop" metadata: Second operand must be a metadata node
63+
64+
target triple = "dxilv1.0-unknown-shadermodel6.0-library"
65+
66+
define void @example_loop(i32 %n) {
67+
entry:
68+
br label %loop.header
69+
70+
loop.header:
71+
%i = phi i32 [ 0, %entry ], [ %i.next, %loop.body ]
72+
%cmp = icmp slt i32 %i, %n
73+
br i1 %cmp, label %loop.body, label %exit
74+
75+
loop.body:
76+
%i.next = add nsw i32 %i, 1
77+
br label %loop.header, !llvm.loop !1
78+
79+
exit:
80+
ret void
81+
}
82+
83+
!1 = !{!1, i32 0} ; not a metadata node
84+
85+
;--- not-str.ll
86+
87+
; CHECK: Invalid "llvm.loop" metadata: First operand must be a valid "llvm.loop.unroll" hint
88+
89+
target triple = "dxilv1.0-unknown-shadermodel6.0-library"
90+
91+
define void @example_loop(i32 %n) {
92+
entry:
93+
br label %loop.header
94+
95+
loop.header:
96+
%i = phi i32 [ 0, %entry ], [ %i.next, %loop.body ]
97+
%cmp = icmp slt i32 %i, %n
98+
br i1 %cmp, label %loop.body, label %exit
99+
100+
loop.body:
101+
%i.next = add nsw i32 %i, 1
102+
br label %loop.header, !llvm.loop !1
103+
104+
exit:
105+
ret void
106+
}
107+
108+
!1 = !{!1, !2}
109+
!2 = !{i32 0} ; not a hint name string
110+
111+
;--- bad-count.ll
112+
113+
; CHECK: Second operand of "llvm.loop.unroll.count" must be a constant integer
114+
115+
target triple = "dxilv1.0-unknown-shadermodel6.0-library"
116+
117+
define void @example_loop(i32 %n) {
118+
entry:
119+
br label %loop.header
120+
121+
loop.header:
122+
%i = phi i32 [ 0, %entry ], [ %i.next, %loop.body ]
123+
%cmp = icmp slt i32 %i, %n
124+
br i1 %cmp, label %loop.body, label %exit
125+
126+
loop.body:
127+
%i.next = add nsw i32 %i, 1
128+
br label %loop.header, !llvm.loop !1
129+
130+
exit:
131+
ret void
132+
}
133+
134+
!1 = !{!1, !2}
135+
!2 = !{!"llvm.loop.unroll.count", !"not an int"} ; invalid count parameters
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
; RUN: opt -S --dxil-validate-metadata %s | FileCheck %s
2+
3+
; Test that we allow a self-referential chain and a constant integer in count
4+
5+
target triple = "dxilv1.0-unknown-shadermodel6.0-library"
6+
7+
define void @example_loop(i32 %n) {
8+
entry:
9+
br label %loop.header
10+
11+
loop.header:
12+
%i = phi i32 [ 0, %entry ], [ %i.next, %loop.body ]
13+
%cmp = icmp slt i32 %i, %n
14+
br i1 %cmp, label %loop.body, label %exit
15+
16+
loop.body:
17+
%i.next = add nsw i32 %i, 1
18+
; CHECK: br label %loop.header, !llvm.loop ![[#LOOP_MD:]]
19+
br label %loop.header, !llvm.loop !0
20+
21+
exit:
22+
ret void
23+
}
24+
25+
; CHECK: ![[#LOOP_MD]] = distinct !{![[#LOOP_MD]], ![[#EXTRA_REF:]]}
26+
; CHECK: ![[#EXTRA_REF]] = distinct !{![[#EXTRA_REF]], ![[#COUNT:]]}
27+
; CHECK: ![[#COUNT]] = !{!"llvm.loop.unroll.count", i6 4}
28+
29+
!0 = !{!0, !1}
30+
!1 = !{!1, !2}
31+
!2 = !{!"llvm.loop.unroll.count", i6 4}

llvm/test/CodeGen/DirectX/metadata-stripping.ll

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ entry:
1414

1515
%cmp.i = icmp ult i32 1, 2
1616
; Ensure that the !llvm.loop metadata node gets dropped.
17-
; CHECK: br i1 %cmp.i, label %_Z4mainDv3_j.exit, label %_Z4mainDv3_j.exit{{$}}
17+
; CHECK: br i1 %cmp.i, label %_Z4mainDv3_j.exit, label %_Z4mainDv3_j.exit, !llvm.loop [[LOOPMD:![0-9]+]]
1818
br i1 %cmp.i, label %_Z4mainDv3_j.exit, label %_Z4mainDv3_j.exit, !llvm.loop !0
1919

2020
_Z4mainDv3_j.exit: ; preds = %for.body.i, %entry
@@ -24,9 +24,8 @@ _Z4mainDv3_j.exit: ; preds = %for.body.i, %entry
2424
; These next check lines check that only the range metadata remains
2525
; No more metadata should be necessary, the rest (the current 0 and 1)
2626
; should be removed.
27-
; CHECK-NOT: !{!"llvm.loop.mustprogress"}
28-
; CHECK: [[RANGEMD]] = !{i32 1, i32 5}
29-
; CHECK-NOT: !{!"llvm.loop.mustprogress"}
27+
; CHECK-DAG: [[RANGEMD]] = !{i32 1, i32 5}
28+
; CHECK-DAG: !{!"llvm.loop.mustprogress"}
3029
!0 = distinct !{!0, !1}
3130
!1 = !{!"llvm.loop.mustprogress"}
3231
!2 = !{i32 1, i32 5}

0 commit comments

Comments
 (0)