Skip to content

Commit c4c8d08

Browse files
authored
[Clang] Fix incorrect handling of #pragma {GCC} unroll N in dependent context (#90240)
PR #89567 fix the `#pragma unroll N` crash issue in dependent context, but it's introduce an new issue: Since #89567, if `N` is value dependent, 'option' and 'state' were ` (LoopHintAttr::Unroll, LoopHintAttr::Enable)`. Therefor, clang's code generator generated incorrect IR metadata. For the situation `#pragma {GCC} unroll {0|1}`, before template instantiation, this PR tweak the 'option' to `LoopHintAttr::UnrollCount` and 'state' to `LoopHintAttr::Numeric`. During template instantiation and if unroll count is 0 or 1 this PR tweak 'option' to `LoopHintAttr::Unroll` and 'state' to `LoopHintAttr::Disable`. We don't use `LoopHintAttr::UnrollCount` here because it's will emit an redundant LLVM IR metadata `!{!"llvm.loop.unroll.count", i32 1}` when unroll count is 1. --------- Signed-off-by: yronglin <[email protected]>
1 parent e718403 commit c4c8d08

File tree

7 files changed

+165
-15
lines changed

7 files changed

+165
-15
lines changed

clang/lib/CodeGen/CGLoopInfo.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,8 +673,6 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
673673
setPipelineDisabled(true);
674674
break;
675675
case LoopHintAttr::UnrollCount:
676-
setUnrollState(LoopAttributes::Disable);
677-
break;
678676
case LoopHintAttr::UnrollAndJamCount:
679677
case LoopHintAttr::VectorizeWidth:
680678
case LoopHintAttr::InterleaveCount:

clang/lib/Sema/SemaStmtAttr.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,14 @@ static Attr *handleLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
109109
SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);
110110
} else if (PragmaName == "unroll") {
111111
// #pragma unroll N
112-
if (ValueExpr && !ValueExpr->isValueDependent()) {
113-
llvm::APSInt ValueAPS;
114-
ExprResult R = S.VerifyIntegerConstantExpression(ValueExpr, &ValueAPS);
115-
assert(!R.isInvalid() && "unroll count value must be a valid value, it's "
116-
"should be checked in Sema::CheckLoopHintExpr");
117-
(void)R;
118-
// The values of 0 and 1 block any unrolling of the loop.
119-
if (ValueAPS.isZero() || ValueAPS.isOne())
120-
SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Disable);
121-
else
112+
if (ValueExpr) {
113+
if (!ValueExpr->isValueDependent()) {
114+
auto Value = ValueExpr->EvaluateKnownConstInt(S.getASTContext());
115+
if (Value.isZero() || Value.isOne())
116+
SetHints(LoopHintAttr::Unroll, LoopHintAttr::Disable);
117+
else
118+
SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);
119+
} else
122120
SetHints(LoopHintAttr::UnrollCount, LoopHintAttr::Numeric);
123121
} else
124122
SetHints(LoopHintAttr::Unroll, LoopHintAttr::Enable);

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,13 +2151,25 @@ TemplateInstantiator::TransformLoopHintAttr(const LoopHintAttr *LH) {
21512151

21522152
// Generate error if there is a problem with the value.
21532153
if (getSema().CheckLoopHintExpr(TransformedExpr, LH->getLocation(),
2154-
LH->getOption() == LoopHintAttr::UnrollCount))
2154+
LH->getSemanticSpelling() ==
2155+
LoopHintAttr::Pragma_unroll))
21552156
return LH;
21562157

2158+
LoopHintAttr::OptionType Option = LH->getOption();
2159+
LoopHintAttr::LoopHintState State = LH->getState();
2160+
2161+
llvm::APSInt ValueAPS =
2162+
TransformedExpr->EvaluateKnownConstInt(getSema().getASTContext());
2163+
// The values of 0 and 1 block any unrolling of the loop.
2164+
if (ValueAPS.isZero() || ValueAPS.isOne()) {
2165+
Option = LoopHintAttr::Unroll;
2166+
State = LoopHintAttr::Disable;
2167+
}
2168+
21572169
// Create new LoopHintValueAttr with integral expression in place of the
21582170
// non-type template parameter.
2159-
return LoopHintAttr::CreateImplicit(getSema().Context, LH->getOption(),
2160-
LH->getState(), TransformedExpr, *LH);
2171+
return LoopHintAttr::CreateImplicit(getSema().Context, Option, State,
2172+
TransformedExpr, *LH);
21612173
}
21622174
const NoInlineAttr *TemplateInstantiator::TransformStmtNoInlineAttr(
21632175
const Stmt *OrigS, const Stmt *InstS, const NoInlineAttr *A) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ast-dump %s | FileCheck %s
2+
3+
using size_t = unsigned long long;
4+
5+
// CHECK: LoopHintAttr {{.*}} Implicit unroll UnrollCount Numeric
6+
// CHECK: LoopHintAttr {{.*}} Implicit unroll UnrollCount Numeric
7+
// CHECK: LoopHintAttr {{.*}} Implicit unroll Unroll Disable
8+
// CHECK: LoopHintAttr {{.*}} Implicit unroll Unroll Disable
9+
template <bool Flag>
10+
int value_dependent(int n) {
11+
constexpr int N = 100;
12+
auto init = [=]() { return Flag ? n : 0UL; };
13+
auto cond = [=](size_t ix) { return Flag ? ix != 0 : ix < 10; };
14+
auto iter = [=](size_t ix) {
15+
return Flag ? ix & ~(1ULL << __builtin_clzll(ix)) : ix + 1;
16+
};
17+
18+
#pragma unroll Flag ? 1 : N
19+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
20+
n *= n;
21+
}
22+
#pragma unroll Flag ? 0 : N
23+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
24+
n *= n;
25+
}
26+
return n;
27+
}
28+
29+
void test_value_dependent(int n) {
30+
value_dependent<true>(n);
31+
}

clang/test/CodeGenCXX/pragma-gcc-unroll.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,34 @@ void while_unroll_zero_test(int *List, int Length) {
116116
}
117117
}
118118

119+
using size_t = unsigned long long;
120+
121+
template <bool Flag>
122+
int value_dependent(int n) {
123+
// CHECK: define {{.*}} @_Z15value_dependentILb1EEii
124+
constexpr int N = 100;
125+
auto init = [=]() { return Flag ? n : 0UL; };
126+
auto cond = [=](size_t ix) { return Flag ? ix != 0 : ix < 10; };
127+
auto iter = [=](size_t ix) {
128+
return Flag ? ix & ~(1ULL << __builtin_clzll(ix)) : ix + 1;
129+
};
130+
#pragma GCC unroll Flag ? 1 : N
131+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
132+
// CHECK: br label {{.*}}, !llvm.loop ![[LOOP_16:.*]]
133+
n *= n;
134+
}
135+
#pragma GCC unroll Flag ? 0 : N
136+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
137+
// CHECK: br label {{.*}}, !llvm.loop ![[LOOP_17:.*]]
138+
n *= n;
139+
}
140+
return n;
141+
}
142+
143+
void test_value_dependent(int n) {
144+
value_dependent<true>(n);
145+
}
146+
119147
// CHECK: ![[LOOP_1]] = distinct !{![[LOOP_1]], [[MP:![0-9]+]], ![[UNROLL_ENABLE:.*]]}
120148
// CHECK: ![[UNROLL_ENABLE]] = !{!"llvm.loop.unroll.enable"}
121149
// CHECK: ![[LOOP_2]] = distinct !{![[LOOP_2:.*]], ![[UNROLL_DISABLE:.*]]}
@@ -129,3 +157,5 @@ void while_unroll_zero_test(int *List, int Length) {
129157
// CHECK: ![[LOOP_7]] = distinct !{![[LOOP_7]], ![[UNROLL_8:.*]]}
130158
// CHECK: ![[LOOP_14]] = distinct !{![[LOOP_14]], [[MP]], ![[UNROLL_DISABLE:.*]]}
131159
// CHECK: ![[LOOP_15]] = distinct !{![[LOOP_15]], [[MP]], ![[UNROLL_DISABLE:.*]]}
160+
// CHECK: ![[LOOP_16]] = distinct !{![[LOOP_16]], [[MP]], ![[UNROLL_DISABLE:.*]]}
161+
// CHECK: ![[LOOP_17]] = distinct !{![[LOOP_17]], [[MP]], ![[UNROLL_DISABLE:.*]]}

clang/test/CodeGenCXX/pragma-unroll.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,54 @@ void template_test(double *List, int Length) {
9696
for_template_define_test<double>(List, Length, Value);
9797
}
9898

99+
void for_unroll_zero_test(int *List, int Length) {
100+
// CHECK: define {{.*}} @_Z20for_unroll_zero_testPii
101+
#pragma unroll 0
102+
for (int i = 0; i < Length; i++) {
103+
// CHECK: br label {{.*}}, !llvm.loop ![[LOOP_14:.*]]
104+
List[i] = i * 2;
105+
}
106+
}
107+
108+
void while_unroll_zero_test(int *List, int Length) {
109+
// CHECK: define {{.*}} @_Z22while_unroll_zero_testPii
110+
int i = 0;
111+
#pragma unroll(0)
112+
while (i < Length) {
113+
// CHECK: br label {{.*}}, !llvm.loop ![[LOOP_15:.*]]
114+
List[i] = i * 2;
115+
i++;
116+
}
117+
}
118+
119+
using size_t = unsigned long long;
120+
121+
template <bool Flag>
122+
int value_dependent(int n) {
123+
// CHECK: define {{.*}} @_Z15value_dependentILb1EEii
124+
constexpr int N = 100;
125+
auto init = [=]() { return Flag ? n : 0UL; };
126+
auto cond = [=](size_t ix) { return Flag ? ix != 0 : ix < 10; };
127+
auto iter = [=](size_t ix) {
128+
return Flag ? ix & ~(1ULL << __builtin_clzll(ix)) : ix + 1;
129+
};
130+
#pragma unroll Flag ? 1 : N
131+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
132+
// CHECK: br label {{.*}}, !llvm.loop ![[LOOP_16:.*]]
133+
n *= n;
134+
}
135+
#pragma unroll Flag ? 0 : N
136+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
137+
// CHECK: br label {{.*}}, !llvm.loop ![[LOOP_17:.*]]
138+
n *= n;
139+
}
140+
return n;
141+
}
142+
143+
void test_value_dependent(int n) {
144+
value_dependent<true>(n);
145+
}
146+
99147
// CHECK: ![[LOOP_1]] = distinct !{![[LOOP_1]], [[MP:![0-9]+]], ![[UNROLL_ENABLE:.*]]}
100148
// CHECK: ![[UNROLL_ENABLE]] = !{!"llvm.loop.unroll.enable"}
101149
// CHECK: ![[LOOP_2]] = distinct !{![[LOOP_2:.*]], ![[UNROLL_DISABLE:.*]]}
@@ -107,3 +155,7 @@ void template_test(double *List, int Length) {
107155
// CHECK: ![[LOOP_5]] = distinct !{![[LOOP_5]], ![[UNROLL_8:.*]]}
108156
// CHECK: ![[LOOP_6]] = distinct !{![[LOOP_6]], ![[UNROLL_8:.*]]}
109157
// CHECK: ![[LOOP_7]] = distinct !{![[LOOP_7]], ![[UNROLL_8:.*]]}
158+
// CHECK: ![[LOOP_14]] = distinct !{![[LOOP_14]], [[MP]], ![[UNROLL_DISABLE:.*]]}
159+
// CHECK: ![[LOOP_15]] = distinct !{![[LOOP_15]], [[MP]], ![[UNROLL_DISABLE:.*]]}
160+
// CHECK: ![[LOOP_16]] = distinct !{![[LOOP_16]], [[MP]], ![[UNROLL_DISABLE:.*]]}
161+
// CHECK: ![[LOOP_17]] = distinct !{![[LOOP_17]], [[MP]], ![[UNROLL_DISABLE:.*]]}

clang/test/Parser/pragma-unroll.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,32 @@ void test(int *List, int Length) {
124124

125125
#pragma unroll
126126
/* expected-error {{expected statement}} */ }
127+
128+
using size_t = unsigned long long;
129+
130+
template <bool Flag>
131+
int FailToBuild(int n) {
132+
constexpr int N = 100;
133+
auto init = [=]() { return Flag ? n : 0UL; };
134+
auto cond = [=](size_t ix) { return Flag ? ix != 0 : ix < 10; };
135+
auto iter = [=](size_t ix) {
136+
return Flag ? ix & ~(1ULL << __builtin_clzll(ix)) : ix + 1;
137+
};
138+
#pragma unroll Flag ? 0 : N // Ok, allow 0.
139+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
140+
n *= n;
141+
}
142+
#pragma GCC unroll Flag ? 0 : N // Ok, allow 0.
143+
for (size_t ix = init(); cond(ix); ix = iter(ix)) {
144+
n *= n;
145+
}
146+
return n;
147+
}
148+
149+
int foo(int n) {
150+
return FailToBuild<true>(n);
151+
}
152+
153+
int bar(int n) {
154+
return FailToBuild<false>(n);
155+
}

0 commit comments

Comments
 (0)