Skip to content

Commit ff8cf4e

Browse files
author
Anton Bikineev
committed
[clang] Support per-function [[clang::code_align(N)]] attribute.
The existing attribute works only for loop headers. This can unfortunately be quite limiting, especially as a protection against the "Jump Conditional Code Erratum" [0] (for example, if there is an unaligned check in a hot loop). The command line option -malign-branch-boundary can help with that, but it's too coarse-grained and can significantly increase the binary size. This change introduces a per-function [[clang::code_align(N)]] attribute that aligns all the basic blocks of a function by the given N. [0] https://www.intel.com/content/dam/support/us/en/documents/processors/mitigations-jump-conditional-code-erratum.pdf
1 parent a7bc9cb commit ff8cf4e

File tree

8 files changed

+135
-16
lines changed

8 files changed

+135
-16
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4435,10 +4435,11 @@ def PreferredType: InheritableAttr {
44354435
let Documentation = [PreferredTypeDocumentation];
44364436
}
44374437

4438-
def CodeAlign: StmtAttr {
4438+
def CodeAlign : InheritableAttr {
44394439
let Spellings = [Clang<"code_align">];
4440-
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
4441-
ErrorDiag, "'for', 'while', and 'do' statements">;
4440+
let Subjects =
4441+
SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt, Function],
4442+
ErrorDiag, "'for', 'while', 'do' statements and functions">;
44424443
let Args = [ExprArgument<"Alignment">];
44434444
let Documentation = [CodeAlignAttrDocs];
44444445
let AdditionalMembers = [{

clang/include/clang/Basic/AttrDocs.td

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7704,11 +7704,12 @@ def CodeAlignAttrDocs : Documentation {
77047704
let Category = DocCatVariable;
77057705
let Heading = "clang::code_align";
77067706
let Content = [{
7707-
The ``clang::code_align(N)`` attribute applies to a loop and specifies the byte
7708-
alignment for a loop. The attribute accepts a positive integer constant
7709-
initialization expression indicating the number of bytes for the minimum
7710-
alignment boundary. Its value must be a power of 2, between 1 and 4096
7711-
(inclusive).
7707+
The ``clang::code_align(N)`` attribute applies to a loop or a function. When
7708+
applied to a loop it specifies the byte alignment for the loop header. When
7709+
applied to a function it aligns all the basic blocks of the function. The
7710+
attribute accepts a positive integer constant initialization expression
7711+
indicating the number of bytes for the minimum alignment boundary. Its value
7712+
must be a power of 2, between 1 and 4096 (inclusive).
77127713

77137714
.. code-block:: c++
77147715

@@ -7738,6 +7739,11 @@ alignment boundary. Its value must be a power of 2, between 1 and 4096
77387739
[[clang::code_align(A)]] for(;;) { }
77397740
}
77407741

7742+
[[clang:code_align(16)]] int foo(bool b) {
7743+
if (b) return 2;
7744+
return 3;
7745+
}
7746+
77417747
}];
77427748
}
77437749

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,6 +2515,12 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
25152515
F->setAlignment(std::max(llvm::Align(2), F->getAlign().valueOrOne()));
25162516
}
25172517

2518+
if (const auto *CodeAlign = D->getAttr<CodeAlignAttr>()) {
2519+
const auto *CE = cast<ConstantExpr>(CodeAlign->getAlignment());
2520+
std::string ArgValStr = llvm::toString(CE->getResultAsAPSInt(), 10);
2521+
F->addFnAttr("align-basic-blocks", ArgValStr);
2522+
}
2523+
25182524
// In the cross-dso CFI mode with canonical jump tables, we want !type
25192525
// attributes on definitions only.
25202526
if (CodeGenOpts.SanitizeCfiCrossDso &&

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9036,6 +9036,12 @@ static void handleArmNewAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
90369036
ArmNewAttr(S.Context, AL, NewState.data(), NewState.size()));
90379037
}
90389038

9039+
static void handleCodeAlignAttr(Sema &S, Decl *D, const ParsedAttr &A) {
9040+
Expr *E = A.getArgAsExpr(0);
9041+
if (Attr *CodeAlignAttr = S.BuildCodeAlignAttr(A, E))
9042+
D->addAttr(CodeAlignAttr);
9043+
}
9044+
90399045
/// ProcessDeclAttribute - Apply the specific attribute to the specified decl if
90409046
/// the attribute applies to decls. If the attribute is a type attribute, just
90419047
/// silently ignore it if a GNU attribute.
@@ -9855,6 +9861,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
98559861
case ParsedAttr::AT_UsingIfExists:
98569862
handleSimpleAttribute<UsingIfExistsAttr>(S, D, AL);
98579863
break;
9864+
9865+
case ParsedAttr::AT_CodeAlign:
9866+
handleCodeAlignAttr(S, D, AL);
9867+
break;
98589868
}
98599869
}
98609870

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -x c %s %s -o - | FileCheck -check-prefix=CHECK-C %s
2+
// RUN: %clang_cc1 -fsyntax-only -emit-llvm -x c++ -std=c++11 %s -o - | FileCheck %s --check-prefixes CHECK-CPP
3+
4+
// CHECK-C: define dso_local i32 @code_align_function(i32 noundef %a) #[[A0:[0-9]+]]
5+
// CHECK-C: define dso_local i32 @code_align_function_with_aligned_loop(i32 noundef %a) #[[A1:[0-9]+]]
6+
7+
// CHECK-C: attributes #[[A0]] = {{.*}} "align-basic-blocks"="32"
8+
[[clang::code_align(32)]] int code_align_function(int a) {
9+
if (a) {
10+
return 2;
11+
}
12+
return 3;
13+
}
14+
15+
// CHECK-C: attributes #[[A1]] = {{.*}} "align-basic-blocks"="64"
16+
[[clang::code_align(64)]] int code_align_function_with_aligned_loop(int a) {
17+
if (a) {
18+
return 2;
19+
}
20+
int c = 0;
21+
// CHECK-C: !{!"llvm.loop.align", i32 128}
22+
[[clang::code_align(128)]] for (int i = 0; i < a; ++i) {
23+
c += i;
24+
}
25+
return c;
26+
}
27+
28+
#if __cplusplus >= 201103L
29+
struct S {
30+
// CHECK-CPP: @_ZN1S19code_align_functionILi1EEEii({{.*}}) #[[A2:[0-9]+]]
31+
// CHECK-CPP: attributes #[[A2]] = {{.*}} "align-basic-blocks"="16"
32+
template <int A>
33+
[[clang::code_align(16)]] int code_align_function(int a) {
34+
if (a) {
35+
return 2;
36+
}
37+
return 3;
38+
}
39+
};
40+
41+
template int S::code_align_function<1>(int);
42+
#endif

clang/test/Sema/code_align.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ void foo() {
99
for (i = 0; i < 10; ++i) { // this is OK
1010
a[i] = b[i] = 0;
1111
}
12-
// expected-error@+1{{'code_align' attribute only applies to 'for', 'while', and 'do' statements}}
12+
// expected-error@+1{{'code_align' attribute only applies to 'for', 'while', 'do' statements and functions}}
1313
[[clang::code_align(4)]]
1414
i = 7;
1515
for (i = 0; i < 10; ++i) {
1616
a[i] = b[i] = 0;
1717
}
1818

19-
// expected-error@+1{{'code_align' attribute cannot be applied to a declaration}}
19+
// expected-error@+1{{'code_align' attribute only applies to 'for', 'while', 'do' statements and functions}}
2020
[[clang::code_align(12)]] int n[10];
2121
}
2222

@@ -121,6 +121,11 @@ void check_code_align_expression() {
121121
#endif
122122
}
123123

124+
[[clang::code_align(32)]] int function_attribute();
125+
126+
// expected-error@+1{{'code_align' attribute requires an integer argument which is a constant power of two}}
127+
[[clang::code_align(31)]] int function_attribute_misaligned();
128+
124129
#if __cplusplus >= 201103L
125130
template <int A, int B, int C, int D, int E>
126131
void code_align_dependent() {
@@ -161,6 +166,11 @@ void bar4() {
161166
for(int I=0; I<128; ++I) { bar(I); }
162167
}
163168

169+
struct S {
170+
template <int A>
171+
[[clang::code_align(32)]] int foo();
172+
};
173+
164174
int main() {
165175
code_align_dependent<8, 23, 32, -10, 64>(); // #neg-instantiation
166176
bar3<4>(); // #temp-instantiation

llvm/lib/CodeGen/MachineBlockPlacement.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2940,8 +2940,12 @@ void MachineBlockPlacement::alignBlocks() {
29402940
}
29412941
}
29422942

2943-
// Use max of the TLIAlign and MDAlign
2944-
const Align LoopAlign = std::max(TLIAlign, Align(MDAlign));
2943+
unsigned FunctionMBBAlign =
2944+
F->getFunction().getFnAttributeAsParsedInteger("align-basic-blocks", 1);
2945+
2946+
// Use max of the TLIAlign, MDAlign or the function-level alignment.
2947+
const Align LoopAlign =
2948+
std::max(std::max(TLIAlign, Align(MDAlign)), Align(FunctionMBBAlign));
29452949
if (LoopAlign == 1)
29462950
continue; // Don't care about loop alignment.
29472951

@@ -3475,28 +3479,39 @@ bool MachineBlockPlacement::runOnMachineFunction(MachineFunction &MF) {
34753479
bool HasMaxBytesOverride =
34763480
MaxBytesForAlignmentOverride.getNumOccurrences() > 0;
34773481

3482+
unsigned long long MBBAlignment =
3483+
MF.getFunction().getFnAttributeAsParsedInteger("align-basic-blocks", 1);
3484+
3485+
// Respect BB alignment that could already be set by llvm.loop.align in
3486+
// alignBlocks() above.
34783487
if (AlignAllBlock)
34793488
// Align all of the blocks in the function to a specific alignment.
34803489
for (MachineBasicBlock &MBB : MF) {
3490+
unsigned MaxAlignment = std::max(1ULL << AlignAllBlock, MBBAlignment);
34813491
if (HasMaxBytesOverride)
3482-
MBB.setAlignment(Align(1ULL << AlignAllBlock),
3492+
MBB.setAlignment(std::max(Align(MaxAlignment), MBB.getAlignment()),
34833493
MaxBytesForAlignmentOverride);
34843494
else
3485-
MBB.setAlignment(Align(1ULL << AlignAllBlock));
3495+
MBB.setAlignment(std::max(Align(MaxAlignment), MBB.getAlignment()));
34863496
}
34873497
else if (AlignAllNonFallThruBlocks) {
34883498
// Align all of the blocks that have no fall-through predecessors to a
34893499
// specific alignment.
34903500
for (auto MBI = std::next(MF.begin()), MBE = MF.end(); MBI != MBE; ++MBI) {
34913501
auto LayoutPred = std::prev(MBI);
3502+
unsigned MaxAlignment = std::max(1ULL << AlignAllNonFallThruBlocks, MBBAlignment);
34923503
if (!LayoutPred->isSuccessor(&*MBI)) {
34933504
if (HasMaxBytesOverride)
3494-
MBI->setAlignment(Align(1ULL << AlignAllNonFallThruBlocks),
3505+
MBI->setAlignment(std::max(Align(MaxAlignment), MBI->getAlignment()),
34953506
MaxBytesForAlignmentOverride);
34963507
else
3497-
MBI->setAlignment(Align(1ULL << AlignAllNonFallThruBlocks));
3508+
MBI->setAlignment(std::max(Align(MaxAlignment), MBI->getAlignment()));
34983509
}
34993510
}
3511+
} else if (MBBAlignment != 1) {
3512+
for (MachineBasicBlock &MBB : MF) {
3513+
MBB.setAlignment(std::max(Align(MBBAlignment), MBB.getAlignment()));
3514+
}
35003515
}
35013516
if (ViewBlockLayoutWithBFI != GVDT_None &&
35023517
(ViewBlockFreqFuncName.empty() ||
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
; RUN: llc < %s -mtriple=x86_64-pc-linux-gnu | FileCheck %s -check-prefixes=CHECK
2+
3+
declare void @bar();
4+
5+
; CHECK-LABEL: foo:
6+
; CHECK-LABEL: # %bb.0:
7+
; CHECK: .p2align 5, 0x90
8+
; CHECK-LABEL: # %bb.1:
9+
; CHECK: .p2align 10, 0x90
10+
; CHECK-LABEL: .LBB0_2:
11+
; CHECK: .p2align 5, 0x90
12+
; CHECK-LABEL: # %bb.3:
13+
; CHECK: .p2align 5, 0x90
14+
15+
define i32 @foo(i1 %1) "align-basic-blocks"="32" {
16+
br i1 %1, label %7, label %3
17+
3:
18+
%4 = phi i32 [ %5, %3 ], [ 0, %2 ]
19+
call void @bar()
20+
%5 = add nuw nsw i32 %4, 1
21+
%6 = icmp eq i32 %5, 90
22+
br i1 %6, label %7, label %3, !llvm.loop !0
23+
7:
24+
%8 = phi i32 [ 2, %2 ], [ 3, %3 ]
25+
ret i32 %8
26+
}
27+
28+
!0 = distinct !{!0, !1}
29+
!1 = !{!"llvm.loop.align", i32 1024}

0 commit comments

Comments
 (0)