brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.7 KiB · 2b1312c Raw
147 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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 15using namespace clang::ast_matchers;16 17namespace clang::tidy::altera {18 19void StructPackAlignCheck::registerMatchers(MatchFinder *Finder) {20  Finder->addMatcher(recordDecl(isStruct(), isDefinition(),21                                unless(isExpansionInSystemHeader()))22                         .bind("struct"),23                     this);24}25 26CharUnits27StructPackAlignCheck::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 44void 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 and48  // 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 better62    // packing order.63    const QualType StructFieldTy = StructField->getType();64    if (StructFieldTy->isIncompleteType())65      return;66    const 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 StructField71    // size, largest to smallest).72    TotalBitSize += StructFieldWidth;73  }74 75  const uint64_t CharSize = Result.Context->getCharWidth();76  const CharUnits CurrSize =77      Result.Context->getASTRecordLayout(Struct).getSize();78  const CharUnits MinByteSize =79      CharUnits::fromQuantity(std::max<clang::CharUnits::QuantityType>(80          std::ceil(static_cast<float>(TotalBitSize) / CharSize), 1));81  const CharUnits MaxAlign = CharUnits::fromQuantity(82      std::ceil((float)Struct->getMaxAlignment() / CharSize));83  const CharUnits CurrAlign =84      Result.Context->getASTRecordLayout(Struct).getAlignment();85  const CharUnits NewAlign = computeRecommendedAlignment(MinByteSize);86 87  const bool IsPacked = Struct->hasAttr<PackedAttr>();88  const bool NeedsPacking = (MinByteSize < CurrSize) &&89                            (MaxAlign != NewAlign) && (CurrSize != NewAlign);90  const bool NeedsAlignment = CurrAlign.getQuantity() != NewAlign.getQuantity();91 92  if (!NeedsAlignment && !NeedsPacking)93    return;94 95  // If it's using much more space than it needs, suggest packing.96  // (Do not suggest packing if it is currently explicitly aligned to what the97  // minimum byte size would suggest as the new alignment.)98  if (NeedsPacking && !IsPacked) {99    diag(Struct->getLocation(),100         "accessing fields in struct %0 is inefficient due to padding; only "101         "needs %1 bytes but is using %2 bytes")102        << Struct << (int)MinByteSize.getQuantity()103        << (int)CurrSize.getQuantity()104        << FixItHint::CreateInsertion(Struct->getEndLoc().getLocWithOffset(1),105                                      " __attribute__((packed))");106    diag(Struct->getLocation(),107         "use \"__attribute__((packed))\" to reduce the amount of padding "108         "applied to struct %0",109         DiagnosticIDs::Note)110        << Struct;111  }112 113  FixItHint FixIt;114  auto *Attribute = Struct->getAttr<AlignedAttr>();115  const std::string NewAlignQuantity =116      std::to_string((int)NewAlign.getQuantity());117  if (Attribute) {118    FixIt = FixItHint::CreateReplacement(119        Attribute->getRange(),120        (Twine("aligned(") + NewAlignQuantity + ")").str());121  } else {122    FixIt = FixItHint::CreateInsertion(123        Struct->getEndLoc().getLocWithOffset(1),124        (Twine(" __attribute__((aligned(") + NewAlignQuantity + ")))").str());125  }126 127  // And suggest the minimum power-of-two alignment for the struct as a whole128  // (with and without packing).129  if (NeedsAlignment) {130    diag(Struct->getLocation(),131         "accessing fields in struct %0 is inefficient due to poor alignment; "132         "currently aligned to %1 bytes, but recommended alignment is %2 bytes")133        << Struct << (int)CurrAlign.getQuantity() << NewAlignQuantity << FixIt;134 135    diag(Struct->getLocation(),136         "use \"__attribute__((aligned(%0)))\" to align struct %1 to %0 bytes",137         DiagnosticIDs::Note)138        << NewAlignQuantity << Struct;139  }140}141 142void StructPackAlignCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {143  Options.store(Opts, "MaxConfiguredAlignment", MaxConfiguredAlignment);144}145 146} // namespace clang::tidy::altera147