diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -74,7 +74,9 @@ MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), ConstantsAsWritten(false), SuppressImplicitBase(false), FullyQualifiedName(false), - PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true) {} + PrintCanonicalTypes(false), PrintInjectedClassNameWithArguments(true), + SuppressRecordDecls(false), SuppressTypedefDecls(false), + SuppressEnumDecls(false), SuppressDeclNames(false) {} /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -275,6 +277,11 @@ /// Callbacks to use to allow the behavior of printing to be customized. const PrintingCallbacks *Callbacks = nullptr; + + unsigned SuppressRecordDecls : 1; + unsigned SuppressTypedefDecls : 1; + unsigned SuppressEnumDecls : 1; + unsigned SuppressDeclNames : 1; }; } // end namespace clang diff --git a/clang/include/clang/Frontend/ASTConsumers.h b/clang/include/clang/Frontend/ASTConsumers.h --- a/clang/include/clang/Frontend/ASTConsumers.h +++ b/clang/include/clang/Frontend/ASTConsumers.h @@ -31,15 +31,17 @@ // original C code. The output is intended to be in a format such that // clang could re-parse the output back into the same AST, but the // implementation is still incomplete. -std::unique_ptr CreateASTPrinter(std::unique_ptr OS, - StringRef FilterString); +std::unique_ptr +CreateASTPrinter(std::unique_ptr OS, StringRef FilterString, + StringRef FilterNodeTypeString = ""); // AST dumper: dumps the raw AST in human-readable form to the given output // stream, or stdout if OS is nullptr. std::unique_ptr CreateASTDumper(std::unique_ptr OS, StringRef FilterString, bool DumpDecls, bool Deserialize, bool DumpLookups, - bool DumpDeclTypes, ASTDumpOutputFormat Format); + bool DumpDeclTypes, ASTDumpOutputFormat Format, + StringRef FilterNodeTypeString = ""); // AST Decl node lister: prints qualified names of all filterable AST Decl // nodes. diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -277,6 +277,8 @@ Pack = true; T = PET->getPattern(); } + if (Policy.SuppressDeclNames) + DeclName = ""; T.print(Out, Policy, (Pack ? "..." : "") + DeclName, Indentation); } @@ -386,6 +388,15 @@ if (D->isImplicit()) continue; + if (isa(*D) && Policy.SuppressEnumDecls) + continue; + + if (isa(*D) && Policy.SuppressTypedefDecls) + continue; + + if (isa(*D) && Policy.SuppressRecordDecls) + continue; + // Don't print implicit specializations, as they are printed when visiting // corresponding templates. if (auto FD = dyn_cast(*D)) diff --git a/clang/lib/Frontend/ASTConsumers.cpp b/clang/lib/Frontend/ASTConsumers.cpp --- a/clang/lib/Frontend/ASTConsumers.cpp +++ b/clang/lib/Frontend/ASTConsumers.cpp @@ -36,10 +36,12 @@ enum Kind { DumpFull, Dump, Print, None }; ASTPrinter(std::unique_ptr Out, Kind K, ASTDumpOutputFormat Format, StringRef FilterString, - bool DumpLookups = false, bool DumpDeclTypes = false) + StringRef FilterNodeTypeString, bool DumpLookups = false, + bool DumpDeclTypes = false) : Out(Out ? *Out : llvm::outs()), OwnedOut(std::move(Out)), OutputKind(K), OutputFormat(Format), FilterString(FilterString), - DumpLookups(DumpLookups), DumpDeclTypes(DumpDeclTypes) {} + FilterNodeTypeString(FilterNodeTypeString), DumpLookups(DumpLookups), + DumpDeclTypes(DumpDeclTypes) {} void HandleTranslationUnit(ASTContext &Context) override { TranslationUnitDecl *D = Context.getTranslationUnitDecl(); @@ -78,6 +80,20 @@ bool filterMatches(Decl *D) { return getName(D).find(FilterString) != std::string::npos; } + + void adjustPrintingPolicy(PrintingPolicy &Policy) { + if (FilterNodeTypeString.find("record") != std::string::npos) + Policy.SuppressRecordDecls = true; + if (FilterNodeTypeString.find("typedef") != std::string::npos) + Policy.SuppressTypedefDecls = true; + if (FilterNodeTypeString.find("enum") != std::string::npos) + Policy.SuppressEnumDecls = true; + if (FilterNodeTypeString.find("declnames") != std::string::npos) + Policy.SuppressDeclNames = true; + + Policy.PolishForDeclaration = true; + } + void print(Decl *D) { if (DumpLookups) { if (DeclContext *DC = dyn_cast(D)) { @@ -90,6 +106,7 @@ Out << "Not a DeclContext\n"; } else if (OutputKind == Print) { PrintingPolicy Policy(D->getASTContext().getLangOpts()); + adjustPrintingPolicy(Policy); D->print(Out, Policy, /*Indentation=*/0, /*PrintInstantiation=*/true); } else if (OutputKind != None) { D->dump(Out, OutputKind == DumpFull, OutputFormat); @@ -121,6 +138,9 @@ /// Which declarations or DeclContexts to display. std::string FilterString; + /// Ignore certain node types + std::string FilterNodeTypeString; + /// Whether the primary output is lookup results or declarations. Individual /// results will be output with a format determined by OutputKind. This is /// incompatible with OutputKind == Print. @@ -155,21 +175,24 @@ std::unique_ptr clang::CreateASTPrinter(std::unique_ptr Out, - StringRef FilterString) { + StringRef FilterString, + StringRef FilterNodeTypeString) { return std::make_unique(std::move(Out), ASTPrinter::Print, - ADOF_Default, FilterString); + ADOF_Default, FilterString, + FilterNodeTypeString); } std::unique_ptr clang::CreateASTDumper(std::unique_ptr Out, StringRef FilterString, bool DumpDecls, bool Deserialize, bool DumpLookups, - bool DumpDeclTypes, ASTDumpOutputFormat Format) { + bool DumpDeclTypes, ASTDumpOutputFormat Format, + StringRef FilterNodeTypeString) { assert((DumpDecls || Deserialize || DumpLookups) && "nothing to dump"); return std::make_unique( std::move(Out), Deserialize ? ASTPrinter::DumpFull : DumpDecls ? ASTPrinter::Dump : ASTPrinter::None, - Format, FilterString, DumpLookups, DumpDeclTypes); + Format, FilterString, FilterNodeTypeString, DumpLookups, DumpDeclTypes); } std::unique_ptr clang::CreateASTDeclNodeLister() { diff --git a/clang/test/SemaOpenCL/compare-header-and-tablegen.cl b/clang/test/SemaOpenCL/compare-header-and-tablegen.cl new file mode 100644 --- /dev/null +++ b/clang/test/SemaOpenCL/compare-header-and-tablegen.cl @@ -0,0 +1,5 @@ +// RUN: clang-tblgen -gen-clang-opencl-builtin-header %S/../../lib/Sema/OpenCLBuiltins.td > %t1 +// RUN: sort %t1 -o %t1.sorted +// RUN: clang-check -extra-arg=-cl-std=CL2.0 --ast-print --ast-dump-node-type-filter=record,typedef,enum,declnames %S/../../lib/Headers/opencl-c.h > %t2 +// RUN: sort %t2 -o %t2.sorted +// RUN: diff %t1.sorted %t2.sorted diff --git a/clang/tools/clang-check/ClangCheck.cpp b/clang/tools/clang-check/ClangCheck.cpp --- a/clang/tools/clang-check/ClangCheck.cpp +++ b/clang/tools/clang-check/ClangCheck.cpp @@ -72,6 +72,10 @@ "ast-dump-filter", cl::desc(Options.getOptionHelpText(options::OPT_ast_dump_filter)), cl::cat(ClangCheckCategory)); +static cl::opt + ASTDumpNodeTypeFilter("ast-dump-node-type-filter", + cl::desc("Ignore some of the node types"), + cl::cat(ClangCheckCategory)); static cl::opt Analyze("analyze", cl::desc(Options.getOptionHelpText(options::OPT_analyze)), @@ -178,7 +182,8 @@ /*DumpDeclTypes=*/false, clang::ADOF_Default); if (ASTPrint) - return clang::CreateASTPrinter(nullptr, ASTDumpFilter); + return clang::CreateASTPrinter(nullptr, ASTDumpFilter, + ASTDumpNodeTypeFilter); return std::make_unique(); } }; diff --git a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp --- a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp +++ b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp @@ -228,6 +228,63 @@ // same entry (). MapVector SignatureListMap; }; + +// OpenCL builtin header emitter. This class processes the same TableGen input +// as BuiltinNameEmitter, but generates a header-like output of all the +// declarations which are contained in TableGen file +class OpenCLBuiltinHeaderEmitter { +public: + OpenCLBuiltinHeaderEmitter(RecordKeeper &Records, raw_ostream &OS) + : Records(Records), OS(OS) {} + + // Entrypoint to generate the header + void Emit(); + +private: + struct TypeFlags { + TypeFlags() : IsConst(false), IsVolatile(false), IsPointer(false) {} + bool IsConst : 1; + bool IsVolatile : 1; + bool IsPointer : 1; + StringRef AddrSpace; + }; + + // Return a string representation of the given type, such that it can be + // used as a type in OpenCL C code. + std::string getTypeString(const Record *Type, TypeFlags Flags, + int VectorSize) const; + + // Return the type(s) and vector size(s) for the given type. For + // non-GenericTypes, the resulting vectors will contain 1 element. For + // GenericTypes, the resulting vectors typically contain multiple elements. + void getTypeLists(Record *Type, TypeFlags &Flags, + std::vector &TypeList, + std::vector &VectorList) const; + + // Expand the TableGen Records representing a builtin function signature into + // one or more function signatures. Return them as a vector of a vector of + // strings, with each string containing an OpenCL C type and optional + // qualifiers. + // + // The Records may contain GenericTypes, which expand into multiple + // signatures. Repeated occurrences of GenericType in a signature expand to + // the same types. For example [char, FGenType, FGenType] expands to: + // [char, float, float] + // [char, float2, float2] + // [char, float3, float3] + // ... + void + expandTypesInSignature(const std::vector &Signature, + SmallVectorImpl> &Types); + + // Contains OpenCL builtin functions and related information, stored as + // Record instances. They are coming from the associated TableGen file. + RecordKeeper &Records; + + // The output file. + raw_ostream &OS; +}; + } // namespace void BuiltinNameEmitter::Emit() { @@ -816,7 +873,160 @@ OS << "\n} // OCL2Qual\n"; } +std::string OpenCLBuiltinHeaderEmitter::getTypeString(const Record *Type, + TypeFlags Flags, + int VectorSize) const { + std::string S; + if (Type->getValueAsBit("IsConst") || Flags.IsConst) { + S += "const "; + } + if (Type->getValueAsBit("IsVolatile") || Flags.IsVolatile) { + S += "volatile "; + } + + auto PrintAddrSpace = [&S](StringRef AddrSpace) { + S += StringSwitch(AddrSpace) + .Case("clang::LangAS::opencl_private", "__private") + .Case("clang::LangAS::opencl_global", "__global") + .Case("clang::LangAS::opencl_constant", "__constant") + .Case("clang::LangAS::opencl_local", "__local") + .Case("clang::LangAS::opencl_generic", "__generic") + .Default("__private"); + S += " "; + }; + if (Flags.IsPointer) { + PrintAddrSpace(Flags.AddrSpace); + } else if (Type->getValueAsBit("IsPointer")) { + PrintAddrSpace(Type->getValueAsString("AddrSpace")); + } + + StringRef Acc = Type->getValueAsString("AccessQualifier"); + if (Acc != "") { + S += StringSwitch(Acc) + .Case("RO", "__read_only ") + .Case("WO", "__write_only ") + .Case("RW", "__read_write "); + } + + S += Type->getValueAsString("Name").str(); + if (VectorSize > 1) { + S += std::to_string(VectorSize); + } + + if (Type->getValueAsBit("IsPointer") || Flags.IsPointer) { + S += " *"; + } + + return S; +} + +void OpenCLBuiltinHeaderEmitter::getTypeLists( + Record *Type, TypeFlags &Flags, std::vector &TypeList, + std::vector &VectorList) const { + bool isGenType = Type->isSubClassOf("GenericType"); + if (isGenType) { + TypeList = Type->getValueAsDef("TypeList")->getValueAsListOfDefs("List"); + VectorList = + Type->getValueAsDef("VectorList")->getValueAsListOfInts("List"); + return; + } else if (Type->isSubClassOf("PointerType") || + Type->isSubClassOf("ConstType") || + Type->isSubClassOf("VolatileType")) { + StringRef SubTypeName = Type->getValueAsString("Name"); + Record *PossibleGenType = Records.getDef(SubTypeName); + if (PossibleGenType && PossibleGenType->isSubClassOf("GenericType")) { + // When PointerType, ConstType, or VolatileType is applied to a + // GenericType, the flags need to be taken from the subtype, not from the + // GenericType. + Flags.IsPointer = Type->getValueAsBit("IsPointer"); + Flags.IsConst = Type->getValueAsBit("IsConst"); + Flags.IsVolatile = Type->getValueAsBit("IsVolatile"); + Flags.AddrSpace = Type->getValueAsString("AddrSpace"); + getTypeLists(PossibleGenType, Flags, TypeList, VectorList); + return; + } + } + + // Not a GenericType, so just insert the single type. + TypeList.push_back(Type); + VectorList.push_back(Type->getValueAsInt("VecWidth")); +} + +void OpenCLBuiltinHeaderEmitter::expandTypesInSignature( + const std::vector &Signature, + SmallVectorImpl> &Types) { + // Find out if there are any GenTypes in this signature, and if so, calculate + // into how many signatures they will expand. + unsigned NumSignatures = 1; + SmallVector, 4> ExpandedGenTypes; + for (const auto &Arg : Signature) { + SmallVector ExpandedArg; + std::vector TypeList; + std::vector VectorList; + TypeFlags Flags; + + getTypeLists(Arg, Flags, TypeList, VectorList); + + // Insert the Cartesian product of the types and vector sizes. + for (const auto &Vector : VectorList) { + for (const auto &Type : TypeList) { + ExpandedArg.push_back(getTypeString(Type, Flags, Vector)); + } + } + NumSignatures = std::max(NumSignatures, ExpandedArg.size()); + ExpandedGenTypes.push_back(ExpandedArg); + } + + // Now the total number of signatures is known. Populate the return list with + // all signatures. + for (unsigned I = 0; I < NumSignatures; I++) { + SmallVector Args; + + // Process a single signature. + for (unsigned ArgNum = 0; ArgNum < Signature.size(); ArgNum++) { + // For differently-sized GenTypes in a parameter list, the smaller + // GenTypes just repeat. + Args.push_back( + ExpandedGenTypes[ArgNum][I % ExpandedGenTypes[ArgNum].size()]); + } + Types.push_back(Args); + } +} + +void OpenCLBuiltinHeaderEmitter::Emit() { + // Iterate over all builtins. + std::vector Builtins = Records.getAllDerivedDefinitions("Builtin"); + + for (const auto *B : Builtins) { + StringRef Name = B->getValueAsString("Name"); + + SmallVector, 4> FTypes; + expandTypesInSignature(B->getValueAsListOfDefs("Signature"), FTypes); + + for (const auto &Signature : FTypes) { + // Emit function declaration. + OS << Signature[0] << ' '; + OS << Name; + OS << '('; + if (Signature.size() > 1) { + for (unsigned I = 1; I < Signature.size(); I++) { + if (I != 1) + OS << ", "; + OS << Signature[I]; + } + } + OS << ");\n"; + } + } +} + void clang::EmitClangOpenCLBuiltins(RecordKeeper &Records, raw_ostream &OS) { BuiltinNameEmitter NameChecker(Records, OS); NameChecker.Emit(); } + +void clang::EmitClangOpenCLBuiltinHeader(RecordKeeper &Records, + raw_ostream &OS) { + OpenCLBuiltinHeaderEmitter HeaderGenerator(Records, OS); + HeaderGenerator.Emit(); +} diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -63,6 +63,7 @@ GenClangCommentCommandInfo, GenClangCommentCommandList, GenClangOpenCLBuiltins, + GenClangOpenCLBuiltinHeader, GenArmNeon, GenArmFP16, GenArmBF16, @@ -195,6 +196,9 @@ "documentation comments"), clEnumValN(GenClangOpenCLBuiltins, "gen-clang-opencl-builtins", "Generate OpenCL builtin declaration handlers"), + clEnumValN(GenClangOpenCLBuiltinHeader, + "gen-clang-opencl-builtin-header", + "Generate OpenCL builtin header from TableGen file"), clEnumValN(GenArmNeon, "gen-arm-neon", "Generate arm_neon.h for clang"), clEnumValN(GenArmFP16, "gen-arm-fp16", "Generate arm_fp16.h for clang"), clEnumValN(GenArmBF16, "gen-arm-bf16", "Generate arm_bf16.h for clang"), @@ -375,6 +379,9 @@ case GenClangOpenCLBuiltins: EmitClangOpenCLBuiltins(Records, OS); break; + case GenClangOpenCLBuiltinHeader: + EmitClangOpenCLBuiltinHeader(Records, OS); + break; case GenClangSyntaxNodeList: EmitClangSyntaxNodeList(Records, OS); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -123,6 +123,8 @@ void EmitClangOpenCLBuiltins(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangOpenCLBuiltinHeader(llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); void EmitClangDataCollectors(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);