ffrankies created this revision. ffrankies added a reviewer: alexfh. ffrankies created this object with visibility "All Users". ffrankies added a project: clang-tools-extra. Herald added subscribers: arphaman, xazax.hun, Anastasia, mgorny, srhines. Herald added a reviewer: jdoerfert. Herald added a project: clang.
This lint check is a part of the FLOCL (**F**PGA **L**inters for **O**pen**CL**) project out of the Synergy Lab at Virginia Tech. FLOCL is a set of lint checks aimed at FPGA developers who write code in OpenCL. The FPGA struct pack align lint check finds structs that are inefficiently packed or aligned and recommends packing/aligning of the structs using the packed and aligned attributes as needed in a warning. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D66564 Files: clang-tidy/fpga/CMakeLists.txt clang-tidy/fpga/FPGATidyModule.cpp clang-tidy/fpga/StructPackAlignCheck.cpp clang-tidy/fpga/StructPackAlignCheck.h docs/clang-tidy/checks/fpga-struct-pack-align.rst docs/clang-tidy/checks/list.rst docs/clang-tidy/index.rst test/clang-tidy/fpga-struct-pack-align.cpp
Index: test/clang-tidy/fpga-struct-pack-align.cpp =================================================================== --- /dev/null +++ test/clang-tidy/fpga-struct-pack-align.cpp @@ -0,0 +1,74 @@ +// RUN: %check_clang_tidy %s fpga-struct-pack-align %t -- -header-filter=.* "--" --include opencl-c.h -cl-std=CL1.2 -c + +// Struct needs both alignment and packing +struct error { +char a; +double b; +char c; +}; +// CHECK-MESSAGES: :[[@LINE-5]]:8: warning: struct 'error' has inefficient access due to padding, only needs 10 bytes but is using 24 bytes, use "__attribute((packed))" [fpga-struct-pack-align] +// CHECK-MESSAGES: :[[@LINE-6]]:8: warning: struct 'error' has inefficient access due to poor alignment. Currently aligned to 8 bytes, but size 10 bytes is large enough to benefit from "__attribute((aligned(16)))" [fpga-struct-pack-align] + +// Struct is explicitly packed, but needs alignment +struct error_packed { +char a; +double b; +char c; +} __attribute((packed)); +// CHECK-MESSAGES: :[[@LINE-5]]:8: warning: struct 'error_packed' has inefficient access due to poor alignment. Currently aligned to 1 bytes, but size 10 bytes is large enough to benefit from "__attribute((aligned(16)))" [fpga-struct-pack-align] + +// Struct is properly packed, but needs alignment +struct align_only { +char a; +char b; +char c; +char d; +int e; +double f; +}; +// CHECK-MESSAGES: :[[@LINE-8]]:8: warning: struct 'align_only' has inefficient access due to poor alignment. Currently aligned to 8 bytes, but size 16 bytes is large enough to benefit from "__attribute((aligned(16)))" [fpga-struct-pack-align] + +// Struct is perfectly packed but wrongly aligned +struct bad_align { +char a; +double b; +char c; +} __attribute((packed)) __attribute((aligned(8))); +// CHECK-MESSAGES: :[[@LINE-5]]:8: warning: struct 'bad_align' has inefficient access due to poor alignment. Currently aligned to 8 bytes, but size 10 bytes is large enough to benefit from "__attribute((aligned(16)))" [fpga-struct-pack-align] + +struct bad_align2 { +char a; +double b; +char c; +} __attribute((packed)) __attribute((aligned(32))); +// CHECK-MESSAGES: :[[@LINE-5]]:8: warning: struct 'bad_align2' has inefficient access due to poor alignment. Currently aligned to 32 bytes, but size 10 bytes is large enough to benefit from "__attribute((aligned(16)))" [fpga-struct-pack-align] + +struct bad_align3 { +char a; +double b; +char c; +} __attribute((packed)) __attribute((aligned(4))); +// CHECK-MESSAGES: :[[@LINE-5]]:8: warning: struct 'bad_align3' has inefficient access due to poor alignment. Currently aligned to 4 bytes, but size 10 bytes is large enough to benefit from "__attribute((aligned(16)))" [fpga-struct-pack-align] + +// Struct is both perfectly packed and aligned +struct success { +char a; +double b; +char c; +} __attribute((packed)) __attribute((aligned(16))); +//Should take 10 bytes and be aligned to 16 bytes + +// Struct is properly packed, and explicitly aligned +struct success2 { +int a; +int b; +int c; +} __attribute((aligned(16))); + +// If struct is properly aligned, packing not needed +struct error_aligned { +char a; +double b; +char c; +} __attribute((aligned(16))); + Index: docs/clang-tidy/index.rst =================================================================== --- docs/clang-tidy/index.rst +++ docs/clang-tidy/index.rst @@ -64,6 +64,7 @@ ``cert-`` Checks related to CERT Secure Coding Guidelines. ``cppcoreguidelines-`` Checks related to C++ Core Guidelines. ``clang-analyzer-`` Clang Static Analyzer checks. +``fpga-`` Checks related to OpenCL programming for FPGAs.. ``fuchsia-`` Checks related to Fuchsia coding conventions. ``google-`` Checks related to Google coding conventions. ``hicpp-`` Checks related to High Integrity C++ Coding Standard. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -4,7 +4,6 @@ ================= .. toctree:: - abseil-duration-addition abseil-duration-comparison abseil-duration-conversion-cast @@ -19,8 +18,6 @@ abseil-redundant-strcat-calls abseil-str-cat-append abseil-string-find-startswith - abseil-time-comparison - abseil-time-subtraction abseil-upgrade-duration-conversions android-cloexec-accept android-cloexec-accept4 @@ -33,15 +30,12 @@ android-cloexec-inotify-init1 android-cloexec-memfd-create android-cloexec-open - android-cloexec-pipe - android-cloexec-pipe2 android-cloexec-socket android-comparison-in-temp-failure-retry boost-use-to-string bugprone-argument-comment bugprone-assert-side-effect bugprone-bool-pointer-implicit-conversion - bugprone-branch-clone bugprone-copy-constructor-init bugprone-dangling-handle bugprone-exception-escape @@ -59,7 +53,6 @@ bugprone-move-forwarding-reference bugprone-multiple-statement-macro bugprone-parent-virtual-call - bugprone-posix-return bugprone-sizeof-container bugprone-sizeof-expression bugprone-string-constructor @@ -76,7 +69,6 @@ bugprone-too-small-loop-variable bugprone-undefined-memory-manipulation bugprone-undelegated-constructor - bugprone-unhandled-self-assignment bugprone-unused-raii bugprone-unused-return-value bugprone-use-after-move @@ -102,96 +94,10 @@ cert-msc50-cpp cert-msc51-cpp cert-oop11-cpp (redirects to performance-move-constructor-init) <cert-oop11-cpp> - cert-oop54-cpp (redirects to bugprone-unhandled-self-assignment) <cert-oop54-cpp> - clang-analyzer-core.CallAndMessage - clang-analyzer-core.DivideZero - clang-analyzer-core.DynamicTypePropagation - clang-analyzer-core.NonNullParamChecker - clang-analyzer-core.NullDereference - clang-analyzer-core.StackAddressEscape - clang-analyzer-core.UndefinedBinaryOperatorResult - clang-analyzer-core.VLASize - clang-analyzer-core.uninitialized.ArraySubscript - clang-analyzer-core.uninitialized.Assign - clang-analyzer-core.uninitialized.Branch - clang-analyzer-core.uninitialized.CapturedBlockVariable - clang-analyzer-core.uninitialized.UndefReturn - clang-analyzer-cplusplus.InnerPointer - clang-analyzer-cplusplus.Move - clang-analyzer-cplusplus.NewDelete - clang-analyzer-cplusplus.NewDeleteLeaks - clang-analyzer-deadcode.DeadStores - clang-analyzer-nullability.NullPassedToNonnull - clang-analyzer-nullability.NullReturnedFromNonnull - clang-analyzer-nullability.NullableDereferenced - clang-analyzer-nullability.NullablePassedToNonnull - clang-analyzer-nullability.NullableReturnedFromNonnull - clang-analyzer-optin.cplusplus.UninitializedObject - clang-analyzer-optin.cplusplus.VirtualCall - clang-analyzer-optin.mpi.MPI-Checker - clang-analyzer-optin.osx.OSObjectCStyleCast - clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker - clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker - clang-analyzer-optin.performance.GCDAntipattern - clang-analyzer-optin.performance.Padding - clang-analyzer-optin.portability.UnixAPI - clang-analyzer-osx.API - clang-analyzer-osx.MIG - clang-analyzer-osx.NumberObjectConversion - clang-analyzer-osx.OSObjectRetainCount - clang-analyzer-osx.ObjCProperty - clang-analyzer-osx.SecKeychainAPI - clang-analyzer-osx.cocoa.AtSync - clang-analyzer-osx.cocoa.AutoreleaseWrite - clang-analyzer-osx.cocoa.ClassRelease - clang-analyzer-osx.cocoa.Dealloc - clang-analyzer-osx.cocoa.IncompatibleMethodTypes - clang-analyzer-osx.cocoa.Loops - clang-analyzer-osx.cocoa.MissingSuperCall - clang-analyzer-osx.cocoa.NSAutoreleasePool - clang-analyzer-osx.cocoa.NSError - clang-analyzer-osx.cocoa.NilArg - clang-analyzer-osx.cocoa.NonNilReturnValue - clang-analyzer-osx.cocoa.ObjCGenerics - clang-analyzer-osx.cocoa.RetainCount - clang-analyzer-osx.cocoa.RunLoopAutoreleaseLeak - clang-analyzer-osx.cocoa.SelfInit - clang-analyzer-osx.cocoa.SuperDealloc - clang-analyzer-osx.cocoa.UnusedIvars - clang-analyzer-osx.cocoa.VariadicMethodTypes - clang-analyzer-osx.coreFoundation.CFError - clang-analyzer-osx.coreFoundation.CFNumber - clang-analyzer-osx.coreFoundation.CFRetainRelease - clang-analyzer-osx.coreFoundation.containers.OutOfBounds - clang-analyzer-osx.coreFoundation.containers.PointerSizedValues - clang-analyzer-security.FloatLoopCounter - clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling - clang-analyzer-security.insecureAPI.UncheckedReturn - clang-analyzer-security.insecureAPI.bcmp - clang-analyzer-security.insecureAPI.bcopy - clang-analyzer-security.insecureAPI.bzero - clang-analyzer-security.insecureAPI.getpw - clang-analyzer-security.insecureAPI.gets - clang-analyzer-security.insecureAPI.mkstemp - clang-analyzer-security.insecureAPI.mktemp - clang-analyzer-security.insecureAPI.rand - clang-analyzer-security.insecureAPI.strcpy - clang-analyzer-security.insecureAPI.vfork - clang-analyzer-unix.API - clang-analyzer-unix.Malloc - clang-analyzer-unix.MallocSizeof - clang-analyzer-unix.MismatchedDeallocator - clang-analyzer-unix.Vfork - clang-analyzer-unix.cstring.BadSizeArg - clang-analyzer-unix.cstring.NullArg - clang-analyzer-valist.CopyToSelf - clang-analyzer-valist.Uninitialized - clang-analyzer-valist.Unterminated cppcoreguidelines-avoid-c-arrays (redirects to modernize-avoid-c-arrays) <cppcoreguidelines-avoid-c-arrays> cppcoreguidelines-avoid-goto cppcoreguidelines-avoid-magic-numbers (redirects to readability-magic-numbers) <cppcoreguidelines-avoid-magic-numbers> cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) <cppcoreguidelines-c-copy-assignment-signature> - cppcoreguidelines-explicit-virtual-functions (redirects to modernize-use-override) <cppcoreguidelines-explicit-virtual-functions> cppcoreguidelines-interfaces-global-init cppcoreguidelines-macro-usage cppcoreguidelines-narrowing-conversions @@ -210,8 +116,8 @@ cppcoreguidelines-pro-type-vararg cppcoreguidelines-slicing cppcoreguidelines-special-member-functions - fuchsia-default-arguments-calls - fuchsia-default-arguments-declarations + fpga-struct-pack-align + fuchsia-default-arguments fuchsia-header-anon-namespaces (redirects to google-build-namespaces) <fuchsia-header-anon-namespaces> fuchsia-multiple-inheritance fuchsia-overloaded-operator @@ -225,7 +131,6 @@ google-default-arguments google-explicit-constructor google-global-names-in-headers - google-objc-avoid-nsobject-new google-objc-avoid-throwing-exception google-objc-function-naming google-objc-global-variable-declaration @@ -238,7 +143,6 @@ google-runtime-int google-runtime-operator google-runtime-references - google-upgrade-googletest-case hicpp-avoid-c-arrays (redirects to modernize-avoid-c-arrays) <hicpp-avoid-c-arrays> hicpp-avoid-goto hicpp-braces-around-statements (redirects to readability-braces-around-statements) <hicpp-braces-around-statements> @@ -269,11 +173,9 @@ hicpp-use-nullptr (redirects to modernize-use-nullptr) <hicpp-use-nullptr> hicpp-use-override (redirects to modernize-use-override) <hicpp-use-override> hicpp-vararg (redirects to cppcoreguidelines-pro-type-vararg) <hicpp-vararg> - linuxkernel-must-use-errs llvm-header-guard llvm-include-order llvm-namespace-comment - llvm-prefer-isa-or-dyn-cast-in-conditionals llvm-twine-local misc-definitions-in-headers misc-misplaced-const @@ -314,7 +216,6 @@ modernize-use-noexcept modernize-use-nullptr modernize-use-override - modernize-use-trailing-return-type modernize-use-transparent-functors modernize-use-uncaught-exceptions modernize-use-using @@ -324,9 +225,6 @@ objc-avoid-spinlock objc-forbidden-subclassing objc-property-declaration - objc-super-self - openmp-exception-escape - openmp-use-default-none performance-faster-string-find performance-for-range-copy performance-implicit-conversion-in-loop @@ -344,7 +242,6 @@ readability-braces-around-statements readability-const-return-type readability-container-size-empty - readability-convert-member-functions-to-static readability-delete-null-pointer readability-deleted-default readability-else-after-return Index: docs/clang-tidy/checks/fpga-struct-pack-align.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/fpga-struct-pack-align.rst @@ -0,0 +1,49 @@ +.. title:: clang-tidy - fpga-struct-pack-align + +fpga-struct-pack-align +====================== + +Finds structs that are inefficiently packed or aligned, and recommends +packing and/or aligning of said structs as needed. + +Structs that are not packed take up more space than they should, and accessing +structs that are not well aligned is inefficient. + +Based on the "Altera SDK for OpenCL: Best Practices Guide". + +.. code-block:: c++ + + // The following struct is originally aligned to 4 bytes, and thus takes up + // 12 bytes of memory instead of 10. Packing the struct will make it use + // only 10 bytes of memory, and aligning it to 16 bytes will make it + // efficient to access. + struct example { + char a; // 1 byte + double b; // 8 bytes + char c; // 1 byte + }; + + // The following struct is arranged in such a way that packing is not needed. + // However, it is aligned to 4 bytes instead of 8, and thus needs to be + // explicitly aligned. + struct implicitly_packed_example { + char a; // 1 byte + char b; // 1 byte + char c; // 1 byte + char d; // 1 byte + int e; // 4 bytes + }; + + // The following struct is explicitly aligned and packed. + struct good_example { + char a; // 1 byte + double b; // 8 bytes + char c; // 1 byte + } __attribute((packed)) __attribute((aligned(16)); + + // Explicitly aligning a struct to a bad value will result in a warning + struct badly_aligned_example { + char a; // 1 byte + double b; // 8 bytes + char c; // 1 byte + } __attribute((packed)) __attribute((aligned(32))); Index: clang-tidy/fpga/StructPackAlignCheck.h =================================================================== --- /dev/null +++ clang-tidy/fpga/StructPackAlignCheck.h @@ -0,0 +1,36 @@ +//===--- StructPackAlignCheck.h - clang-tidy---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FPGA_STRUCT_PACK_ALIGN_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FPGA_STRUCT_PACK_ALIGN_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace FPGA { + +/// Finds structs that are inefficiently packed or aligned, and recommends +/// packing and/or aligning of said structs as needed. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/FPGA-struct-pack-align.html +class StructPackAlignCheck : public ClangTidyCheck { +public: + StructPackAlignCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace FPGA +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FPGA_STRUCT_PACK_ALIGN_H Index: clang-tidy/fpga/StructPackAlignCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/fpga/StructPackAlignCheck.cpp @@ -0,0 +1,113 @@ +//===--- StructPackAlignCheck.cpp - clang-tidy-----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef MAX_ALIGN_POWER_OF_TWO +#define MAX_ALIGN_POWER_OF_TWO 7 +#endif + +#include "StructPackAlignCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace FPGA { + +void StructPackAlignCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(recordDecl(isStruct()).bind("struct"), this); +} + +void StructPackAlignCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Struct = Result.Nodes.getNodeAs<RecordDecl>("struct"); + const auto *StructDef = Struct->getDefinition(); + + // If not a definition, do nothing + if (Struct != StructDef) return; + + // Get sizing info for the struct + std::vector<std::pair<unsigned int, unsigned int>> FieldSizes; + unsigned int TotalBitSize = 0; + for (auto 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.push_back(std::pair<unsigned int, unsigned int>( + StructFieldWidth, StructField->getFieldIndex())); + // TODO: Recommend a reorganization of the struct (sort by StructField size, + // largest to smallest) + TotalBitSize += StructFieldWidth; + } + // TODO: Express this as CharUnit rather than a hardcoded 8-bits (Rshift3)i + // After computing the minimum size in bits, check for an existing alignment + // flag + CharUnits CurrSize = Result.Context->getASTRecordLayout(Struct).getSize(); + CharUnits MinByteSize = CharUnits::fromQuantity((TotalBitSize +7) >> 3); + CharUnits CurrAlign = Result.Context->getASTRecordLayout( + Struct).getAlignment(); + CharUnits NewAlign = CharUnits::fromQuantity(1); + if (!MinByteSize.isPowerOfTwo()) { + int MSB = (int)MinByteSize.getQuantity(); + for (; MSB > 0; MSB >>= 1) { + NewAlign = NewAlign.alignTo(CharUnits::fromQuantity( + ((int)NewAlign.getQuantity()) << 1)); + //Abort if the computed alignment meets the maximum configured alignment + if (NewAlign.getQuantity() >= (1 << MAX_ALIGN_POWER_OF_TWO)) break; + } + } else { + NewAlign = MinByteSize; + } + + // Check if struct has a "packed" attribute + bool IsPacked = false; + if (Struct->hasAttrs()) { + for (auto StructAttribute : Struct->getAttrs()) { + if (std::string(StructAttribute->getSpelling()).compare("packed") == 0) { + IsPacked = true; + break; + } + } + } + + // 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 (MinByteSize < CurrSize && + ((Struct->getMaxAlignment()>>3) != NewAlign.getQuantity()) && + (CurrSize != NewAlign) && + !IsPacked) { + diag(Struct->getLocation(), + "struct %0 has inefficient access due to padding, only needs %1 bytes " + "but is using %2 bytes, use \"__attribute((packed))\"") + << Struct + << (int)MinByteSize.getQuantity() + << (int)CurrSize.getQuantity(); + } + + // And suggest the minimum power-of-two alignment for the struct as a whole + // (with and without packing) + if (CurrAlign.getQuantity() != NewAlign.getQuantity()) { + diag(Struct->getLocation(), + "struct %0 has inefficient access due to poor alignment. Currently " + "aligned to %1 bytes, but size %3 bytes is large enough to benefit " + "from \"__attribute((aligned(%2)))\"") + << Struct + << (int)CurrAlign.getQuantity() + << (int)NewAlign.getQuantity() + << (int)MinByteSize.getQuantity(); + } +} + +} // namespace FPGA +} // namespace tidy +} // namespace clang Index: clang-tidy/fpga/FPGATidyModule.cpp =================================================================== --- /dev/null +++ clang-tidy/fpga/FPGATidyModule.cpp @@ -0,0 +1,39 @@ +//===--- OpenCLTidyModule.cpp - clang-tidy --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "StructPackAlignCheck.h" + + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace FPGA { + +class FPGAModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<StructPackAlignCheck>( + "fpga-struct-pack-align"); + } +}; + +static ClangTidyModuleRegistry::Add<FPGAModule> X("fpga-module", "Adds Altera FPGA OpenCL lint checks."); + +} // namespace flocl + +// This anchor is used to force the linker to link in the generated object file +// and thus register the MyModule. +volatile int FPGAModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang Index: clang-tidy/fpga/CMakeLists.txt =================================================================== --- /dev/null +++ clang-tidy/fpga/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyFPGAModule + FPGATidyModule.cpp + StructPackAlignCheck.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyUtils + )
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits