StructPackAlignCheck.cpp
5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//===--- StructPackAlignCheck.cpp - clang-tidy ----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "StructPackAlignCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include <math.h>
#include <sstream>
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace altera {
void StructPackAlignCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(recordDecl(isStruct(), isDefinition(),
unless(isExpansionInSystemHeader()))
.bind("struct"),
this);
}
CharUnits
StructPackAlignCheck::computeRecommendedAlignment(CharUnits MinByteSize) {
CharUnits NewAlign = CharUnits::fromQuantity(1);
if (!MinByteSize.isPowerOfTwo()) {
int MSB = (int)MinByteSize.getQuantity();
for (; MSB > 0; MSB /= 2) {
NewAlign = NewAlign.alignTo(
CharUnits::fromQuantity(((int)NewAlign.getQuantity()) * 2));
// Abort if the computed alignment meets the maximum configured alignment.
if (NewAlign.getQuantity() >= MaxConfiguredAlignment)
break;
}
} else {
NewAlign = MinByteSize;
}
return NewAlign;
}
void StructPackAlignCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Struct = Result.Nodes.getNodeAs<RecordDecl>("struct");
// Do not trigger on templated struct declarations because the packing and
// alignment requirements are unknown.
if (Struct->isTemplated())
return;
// Get sizing info for the struct.
llvm::SmallVector<std::pair<unsigned int, unsigned int>, 10> FieldSizes;
unsigned int TotalBitSize = 0;
for (const FieldDecl *StructField : Struct->fields()) {
// For each StructField, record how big it is (in bits).
// Would be good to use a pair of <offset, size> to advise a better
// packing order.
unsigned int StructFieldWidth =
(unsigned int)Result.Context
->getTypeInfo(StructField->getType().getTypePtr())
.Width;
FieldSizes.emplace_back(StructFieldWidth, StructField->getFieldIndex());
// FIXME: Recommend a reorganization of the struct (sort by StructField
// size, largest to smallest).
TotalBitSize += StructFieldWidth;
}
uint64_t CharSize = Result.Context->getCharWidth();
CharUnits CurrSize = Result.Context->getASTRecordLayout(Struct).getSize();
CharUnits MinByteSize =
CharUnits::fromQuantity(ceil((float)TotalBitSize / CharSize));
CharUnits MaxAlign = CharUnits::fromQuantity(
ceil((float)Struct->getMaxAlignment() / CharSize));
CharUnits CurrAlign =
Result.Context->getASTRecordLayout(Struct).getAlignment();
CharUnits NewAlign = computeRecommendedAlignment(MinByteSize);
bool IsPacked = Struct->hasAttr<PackedAttr>();
bool NeedsPacking = (MinByteSize < CurrSize) && (MaxAlign != NewAlign) &&
(CurrSize != NewAlign);
bool NeedsAlignment = CurrAlign.getQuantity() != NewAlign.getQuantity();
if (!NeedsAlignment && !NeedsPacking)
return;
// If it's using much more space than it needs, suggest packing.
// (Do not suggest packing if it is currently explicitly aligned to what the
// minimum byte size would suggest as the new alignment.)
if (NeedsPacking && !IsPacked) {
diag(Struct->getLocation(),
"accessing fields in struct %0 is inefficient due to padding; only "
"needs %1 bytes but is using %2 bytes")
<< Struct << (int)MinByteSize.getQuantity()
<< (int)CurrSize.getQuantity()
<< FixItHint::CreateInsertion(Struct->getEndLoc().getLocWithOffset(1),
" __attribute__((packed))");
diag(Struct->getLocation(),
"use \"__attribute__((packed))\" to reduce the amount of padding "
"applied to struct %0",
DiagnosticIDs::Note)
<< Struct;
}
FixItHint FixIt;
AlignedAttr *Attribute = Struct->getAttr<AlignedAttr>();
std::string NewAlignQuantity = std::to_string((int)NewAlign.getQuantity());
if (Attribute) {
std::ostringstream FixItString;
FixItString << "aligned(" << NewAlignQuantity << ")";
FixIt =
FixItHint::CreateReplacement(Attribute->getRange(), FixItString.str());
} else {
std::ostringstream FixItString;
FixItString << " __attribute__((aligned(" << NewAlignQuantity << ")))";
FixIt = FixItHint::CreateInsertion(Struct->getEndLoc().getLocWithOffset(1),
FixItString.str());
}
// And suggest the minimum power-of-two alignment for the struct as a whole
// (with and without packing).
if (NeedsAlignment) {
diag(Struct->getLocation(),
"accessing fields in struct %0 is inefficient due to poor alignment; "
"currently aligned to %1 bytes, but recommended alignment is %2 bytes")
<< Struct << (int)CurrAlign.getQuantity() << NewAlignQuantity << FixIt;
diag(Struct->getLocation(),
"use \"__attribute__((aligned(%0)))\" to align struct %1 to %0 bytes",
DiagnosticIDs::Note)
<< NewAlignQuantity << Struct;
}
}
void StructPackAlignCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "MaxConfiguredAlignment", MaxConfiguredAlignment);
}
} // namespace altera
} // namespace tidy
} // namespace clang