llvm-project
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
15using namespace clang::ast_matchers;16
17namespace clang::tidy::altera {18
19void StructPackAlignCheck::registerMatchers(MatchFinder *Finder) {20Finder->addMatcher(recordDecl(isStruct(), isDefinition(),21unless(isExpansionInSystemHeader()))22.bind("struct"),23this);24}
25
26CharUnits
27StructPackAlignCheck::computeRecommendedAlignment(CharUnits MinByteSize) const {28CharUnits NewAlign = CharUnits::fromQuantity(1);29if (!MinByteSize.isPowerOfTwo()) {30CharUnits::QuantityType MSB = MinByteSize.getQuantity();31for (; MSB > 0; MSB /= 2) {32NewAlign =33NewAlign.alignTo(CharUnits::fromQuantity(NewAlign.getQuantity() * 2));34// Abort if the computed alignment meets the maximum configured alignment.35if (NewAlign.getQuantity() >= MaxConfiguredAlignment)36break;37}38} else {39NewAlign = MinByteSize;40}41return NewAlign;42}
43
44void StructPackAlignCheck::check(const MatchFinder::MatchResult &Result) {45const auto *Struct = Result.Nodes.getNodeAs<RecordDecl>("struct");46
47// Do not trigger on templated struct declarations because the packing and48// alignment requirements are unknown.49if (Struct->isTemplated())50return;51
52// Packing and alignment requirements for invalid decls are meaningless.53if (Struct->isInvalidDecl())54return;55
56// Get sizing info for the struct.57llvm::SmallVector<std::pair<unsigned int, unsigned int>, 10> FieldSizes;58unsigned int TotalBitSize = 0;59for (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.63QualType StructFieldTy = StructField->getType();64if (StructFieldTy->isIncompleteType())65return;66unsigned int StructFieldWidth =67(unsigned int)Result.Context->getTypeInfo(StructFieldTy.getTypePtr())68.Width;69FieldSizes.emplace_back(StructFieldWidth, StructField->getFieldIndex());70// FIXME: Recommend a reorganization of the struct (sort by StructField71// size, largest to smallest).72TotalBitSize += StructFieldWidth;73}74
75uint64_t CharSize = Result.Context->getCharWidth();76CharUnits CurrSize = Result.Context->getASTRecordLayout(Struct).getSize();77CharUnits MinByteSize =78CharUnits::fromQuantity(std::max<clang::CharUnits::QuantityType>(79ceil(static_cast<float>(TotalBitSize) / CharSize), 1));80CharUnits MaxAlign = CharUnits::fromQuantity(81ceil((float)Struct->getMaxAlignment() / CharSize));82CharUnits CurrAlign =83Result.Context->getASTRecordLayout(Struct).getAlignment();84CharUnits NewAlign = computeRecommendedAlignment(MinByteSize);85
86bool IsPacked = Struct->hasAttr<PackedAttr>();87bool NeedsPacking = (MinByteSize < CurrSize) && (MaxAlign != NewAlign) &&88(CurrSize != NewAlign);89bool NeedsAlignment = CurrAlign.getQuantity() != NewAlign.getQuantity();90
91if (!NeedsAlignment && !NeedsPacking)92return;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 the96// minimum byte size would suggest as the new alignment.)97if (NeedsPacking && !IsPacked) {98diag(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))");105diag(Struct->getLocation(),106"use \"__attribute__((packed))\" to reduce the amount of padding "107"applied to struct %0",108DiagnosticIDs::Note)109<< Struct;110}111
112FixItHint FixIt;113auto *Attribute = Struct->getAttr<AlignedAttr>();114std::string NewAlignQuantity = std::to_string((int)NewAlign.getQuantity());115if (Attribute) {116FixIt = FixItHint::CreateReplacement(117Attribute->getRange(),118(Twine("aligned(") + NewAlignQuantity + ")").str());119} else {120FixIt = FixItHint::CreateInsertion(121Struct->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 whole126// (with and without packing).127if (NeedsAlignment) {128diag(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
133diag(Struct->getLocation(),134"use \"__attribute__((aligned(%0)))\" to align struct %1 to %0 bytes",135DiagnosticIDs::Note)136<< NewAlignQuantity << Struct;137}138}
139
140void StructPackAlignCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {141Options.store(Opts, "MaxConfiguredAlignment", MaxConfiguredAlignment);142}
143
144} // namespace clang::tidy::altera145