llvm-project

Форк
0
/
StructPackAlignCheck.cpp 
144 строки · 5.6 Кб
1
//===--- StructPackAlignCheck.cpp - clang-tidy ----------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8

9
#include "StructPackAlignCheck.h"
10
#include "clang/AST/ASTContext.h"
11
#include "clang/AST/RecordLayout.h"
12
#include "clang/ASTMatchers/ASTMatchFinder.h"
13
#include <cmath>
14

15
using namespace clang::ast_matchers;
16

17
namespace clang::tidy::altera {
18

19
void StructPackAlignCheck::registerMatchers(MatchFinder *Finder) {
20
  Finder->addMatcher(recordDecl(isStruct(), isDefinition(),
21
                                unless(isExpansionInSystemHeader()))
22
                         .bind("struct"),
23
                     this);
24
}
25

26
CharUnits
27
StructPackAlignCheck::computeRecommendedAlignment(CharUnits MinByteSize) const {
28
  CharUnits NewAlign = CharUnits::fromQuantity(1);
29
  if (!MinByteSize.isPowerOfTwo()) {
30
    CharUnits::QuantityType MSB = MinByteSize.getQuantity();
31
    for (; MSB > 0; MSB /= 2) {
32
      NewAlign =
33
          NewAlign.alignTo(CharUnits::fromQuantity(NewAlign.getQuantity() * 2));
34
      // Abort if the computed alignment meets the maximum configured alignment.
35
      if (NewAlign.getQuantity() >= MaxConfiguredAlignment)
36
        break;
37
    }
38
  } else {
39
    NewAlign = MinByteSize;
40
  }
41
  return NewAlign;
42
}
43

44
void StructPackAlignCheck::check(const MatchFinder::MatchResult &Result) {
45
  const auto *Struct = Result.Nodes.getNodeAs<RecordDecl>("struct");
46

47
  // Do not trigger on templated struct declarations because the packing and
48
  // alignment requirements are unknown.
49
  if (Struct->isTemplated())
50
     return;
51

52
  // Packing and alignment requirements for invalid decls are meaningless.
53
  if (Struct->isInvalidDecl())
54
    return;
55

56
  // Get sizing info for the struct.
57
  llvm::SmallVector<std::pair<unsigned int, unsigned int>, 10> FieldSizes;
58
  unsigned int TotalBitSize = 0;
59
  for (const FieldDecl *StructField : Struct->fields()) {
60
    // For each StructField, record how big it is (in bits).
61
    // Would be good to use a pair of <offset, size> to advise a better
62
    // packing order.
63
    QualType StructFieldTy = StructField->getType();
64
    if (StructFieldTy->isIncompleteType())
65
      return;
66
    unsigned int StructFieldWidth =
67
        (unsigned int)Result.Context->getTypeInfo(StructFieldTy.getTypePtr())
68
            .Width;
69
    FieldSizes.emplace_back(StructFieldWidth, StructField->getFieldIndex());
70
    // FIXME: Recommend a reorganization of the struct (sort by StructField
71
    // size, largest to smallest).
72
    TotalBitSize += StructFieldWidth;
73
  }
74

75
  uint64_t CharSize = Result.Context->getCharWidth();
76
  CharUnits CurrSize = Result.Context->getASTRecordLayout(Struct).getSize();
77
  CharUnits MinByteSize =
78
      CharUnits::fromQuantity(std::max<clang::CharUnits::QuantityType>(
79
          ceil(static_cast<float>(TotalBitSize) / CharSize), 1));
80
  CharUnits MaxAlign = CharUnits::fromQuantity(
81
      ceil((float)Struct->getMaxAlignment() / CharSize));
82
  CharUnits CurrAlign =
83
      Result.Context->getASTRecordLayout(Struct).getAlignment();
84
  CharUnits NewAlign = computeRecommendedAlignment(MinByteSize);
85

86
  bool IsPacked = Struct->hasAttr<PackedAttr>();
87
  bool NeedsPacking = (MinByteSize < CurrSize) && (MaxAlign != NewAlign) &&
88
                      (CurrSize != NewAlign);
89
  bool NeedsAlignment = CurrAlign.getQuantity() != NewAlign.getQuantity();
90

91
  if (!NeedsAlignment && !NeedsPacking)
92
    return;
93

94
  // If it's using much more space than it needs, suggest packing.
95
  // (Do not suggest packing if it is currently explicitly aligned to what the
96
  // minimum byte size would suggest as the new alignment.)
97
  if (NeedsPacking && !IsPacked) {
98
    diag(Struct->getLocation(),
99
         "accessing fields in struct %0 is inefficient due to padding; only "
100
         "needs %1 bytes but is using %2 bytes")
101
        << Struct << (int)MinByteSize.getQuantity()
102
        << (int)CurrSize.getQuantity()
103
        << FixItHint::CreateInsertion(Struct->getEndLoc().getLocWithOffset(1),
104
                                      " __attribute__((packed))");
105
    diag(Struct->getLocation(),
106
         "use \"__attribute__((packed))\" to reduce the amount of padding "
107
         "applied to struct %0",
108
         DiagnosticIDs::Note)
109
        << Struct;
110
  }
111

112
  FixItHint FixIt;
113
  auto *Attribute = Struct->getAttr<AlignedAttr>();
114
  std::string NewAlignQuantity = std::to_string((int)NewAlign.getQuantity());
115
  if (Attribute) {
116
    FixIt = FixItHint::CreateReplacement(
117
        Attribute->getRange(),
118
        (Twine("aligned(") + NewAlignQuantity + ")").str());
119
  } else {
120
    FixIt = FixItHint::CreateInsertion(
121
        Struct->getEndLoc().getLocWithOffset(1),
122
        (Twine(" __attribute__((aligned(") + NewAlignQuantity + ")))").str());
123
  }
124

125
  // And suggest the minimum power-of-two alignment for the struct as a whole
126
  // (with and without packing).
127
  if (NeedsAlignment) {
128
    diag(Struct->getLocation(),
129
         "accessing fields in struct %0 is inefficient due to poor alignment; "
130
         "currently aligned to %1 bytes, but recommended alignment is %2 bytes")
131
        << Struct << (int)CurrAlign.getQuantity() << NewAlignQuantity << FixIt;
132

133
    diag(Struct->getLocation(),
134
         "use \"__attribute__((aligned(%0)))\" to align struct %1 to %0 bytes",
135
         DiagnosticIDs::Note)
136
        << NewAlignQuantity << Struct;
137
  }
138
}
139

140
void StructPackAlignCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
141
  Options.store(Opts, "MaxConfiguredAlignment", MaxConfiguredAlignment);
142
}
143

144
} // namespace clang::tidy::altera
145

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.