|  | //===--- 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> | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | namespace clang::tidy::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; | 
|  |  | 
|  | // Packing and alignment requirements for invalid decls are meaningless. | 
|  | if (Struct->isInvalidDecl()) | 
|  | 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. | 
|  | QualType StructFieldTy = StructField->getType(); | 
|  | if (StructFieldTy->isIncompleteType()) | 
|  | return; | 
|  | unsigned int StructFieldWidth = | 
|  | (unsigned int)Result.Context->getTypeInfo(StructFieldTy.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(std::max<clang::CharUnits::QuantityType>( | 
|  | ceil(static_cast<float>(TotalBitSize) / CharSize), 1)); | 
|  | 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) { | 
|  | FixIt = FixItHint::CreateReplacement( | 
|  | Attribute->getRange(), | 
|  | (Twine("aligned(") + NewAlignQuantity + ")").str()); | 
|  | } else { | 
|  | FixIt = FixItHint::CreateInsertion( | 
|  | Struct->getEndLoc().getLocWithOffset(1), | 
|  | (Twine(" __attribute__((aligned(") + NewAlignQuantity + ")))").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 clang::tidy::altera |