From: John McCall Date: Sat, 14 Dec 2019 02:52:16 +0000 (-0500) Subject: Abstract serialization: TableGen "basic" reader/writer CRTP X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6404bd236240639d4986d1ee634ded4bc81d8bd8;p=platform%2Fupstream%2Fllvm.git Abstract serialization: TableGen "basic" reader/writer CRTP classes that serialize basic values --- diff --git a/clang/include/clang/AST/CMakeLists.txt b/clang/include/clang/AST/CMakeLists.txt index fc98398..8ba823f 100644 --- a/clang/include/clang/AST/CMakeLists.txt +++ b/clang/include/clang/AST/CMakeLists.txt @@ -35,6 +35,14 @@ clang_tablegen(TypeNodes.inc -gen-clang-type-nodes SOURCE ../Basic/TypeNodes.td TARGET ClangTypeNodes) +clang_tablegen(AbstractBasicReader.inc -gen-clang-basic-reader + SOURCE PropertiesBase.td + TARGET ClangAbstractBasicReader) + +clang_tablegen(AbstractBasicWriter.inc -gen-clang-basic-writer + SOURCE PropertiesBase.td + TARGET ClangAbstractBasicWriter) + clang_tablegen(CommentNodes.inc -gen-clang-comment-nodes SOURCE ../Basic/CommentNodes.td TARGET ClangCommentNodes) diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td new file mode 100644 index 0000000..c38bf7c --- /dev/null +++ b/clang/include/clang/AST/PropertiesBase.td @@ -0,0 +1,179 @@ +//==--- PropertiesBase.td - Baseline definitions for AST properties -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +class ASTNode; + +/// The type of the property. +class PropertyType { + /// The C++ type name for the type. + string CXXName = !if(!ne(typeName, ""), typeName, NAME); + + /// Whether the C++ type should generally be passed around by reference. + bit PassByReference = 0; + + /// Whether `const` should be prepended to the type when writing. + bit ConstWhenWriting = 0; + + /// Given a value of type Optional bound as 'value', yield a + /// CXXName that can be serialized into a DataStreamTypeWriter. + string PackOptional = ""; + + /// Given a value of type CXXName bound as 'value' that was deserialized + /// by a DataStreamTypeReader, yield an Optional. + string UnpackOptional = ""; + + /// A list of types for which buffeers must be passed to the read + /// operations. + list BufferElementTypes = []; +} + +/// Property types that correspond to specific C++ enums. +class EnumPropertyType : PropertyType {} + +/// Property types that correspond to a specific C++ class. +/// Supports optional values by using the null representation. +class RefPropertyType : PropertyType { + let PackOptional = + "value ? *value : nullptr"; + let UnpackOptional = + "value ? llvm::Optional<" # CXXName # ">(value) : llvm::None"; +} + +/// Property types that correspond to a specific subclass of another type. +class SubclassPropertyType + : RefPropertyType { + PropertyType Base = base; + string SubclassName = className; + let ConstWhenWriting = base.ConstWhenWriting; +} + +/// Property types that support optional values by using their +/// default value. +class DefaultValuePropertyType : PropertyType { + let PackOptional = + "value ? *value : " # CXXName # "()"; + let UnpackOptional = + "value.isNull() ? llvm::None : llvm::Optional<" # CXXName # ">(value)"; +} + +/// Property types that correspond to integer types and support optional +/// values by shifting the value over by 1. +class CountPropertyType : PropertyType { + let PackOptional = + "value ? *value + 1 : 0"; + let UnpackOptional = + "value ? llvm::Optional<" # CXXName # ">(value - 1) : llvm::None"; +} + +def APInt : PropertyType<"llvm::APInt"> { let PassByReference = 1; } +def APSInt : PropertyType<"llvm::APSInt"> { let PassByReference = 1; } +def ArraySizeModifier : EnumPropertyType<"ArrayType::ArraySizeModifier">; +def AttrKind : EnumPropertyType<"attr::Kind">; +def AutoTypeKeyword : EnumPropertyType; +def Bool : PropertyType<"bool">; +def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">; +def CallingConv : EnumPropertyType; +def DeclarationName : PropertyType; +def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">; +def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; } + def CXXRecordDeclRef : + SubclassPropertyType<"CXXRecordDecl", DeclRef>; + def FunctionDeclRef : + SubclassPropertyType<"FunctionDecl", DeclRef>; + def NamedDeclRef : + SubclassPropertyType<"NamedDecl", DeclRef>; + def NamespaceDeclRef : + SubclassPropertyType<"NamespaceDecl", DeclRef>; + def NamespaceAliasDeclRef : + SubclassPropertyType<"NamespaceAliasDecl", DeclRef>; + def ObjCProtocolDeclRef : + SubclassPropertyType<"ObjCProtocolDecl", DeclRef>; + def ObjCTypeParamDeclRef : + SubclassPropertyType<"ObjCTypeParamDecl", DeclRef>; + def TagDeclRef : + SubclassPropertyType<"TagDecl", DeclRef>; + def TemplateDeclRef : + SubclassPropertyType<"TemplateDecl", DeclRef>; + def TemplateTypeParmDeclRef : + SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>; + def TemplateTemplateParmDeclRef : + SubclassPropertyType<"TemplateTemplateParmDecl", DeclRef>; + def ValueDeclRef : + SubclassPropertyType<"ValueDecl", DeclRef>; +def ElaboratedTypeKeyword : EnumPropertyType; +def ExtParameterInfo : PropertyType<"FunctionProtoType::ExtParameterInfo">; +def Identifier : PropertyType<"IdentifierInfo*">; +def NestedNameSpecifier : PropertyType<"NestedNameSpecifier *">; +def NestedNameSpecifierKind : + EnumPropertyType<"NestedNameSpecifier::SpecifierKind">; +def OverloadedOperatorKind : EnumPropertyType; +def Qualifiers : PropertyType; +def QualType : DefaultValuePropertyType; +def RefQualifierKind : EnumPropertyType; +def Selector : PropertyType; +def SourceLocation : PropertyType; +def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; } + def ExprRef : SubclassPropertyType<"Expr", StmtRef>; +def TemplateArgument : PropertyType; +def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">; +def TemplateName : DefaultValuePropertyType; +def TemplateNameKind : EnumPropertyType<"TemplateName::NameKind">; +def UInt32 : CountPropertyType<"uint32_t">; +def UInt64 : CountPropertyType<"uint64_t">; +def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">; +def VectorKind : EnumPropertyType<"VectorType::VectorKind">; + +def ExceptionSpecInfo : PropertyType<"FunctionProtoType::ExceptionSpecInfo"> { + let BufferElementTypes = [ QualType ]; +} + +/// Arrays. The corresponding C++ type is ArrayRef of the corresponding +/// C++ type of the element. +class Array : PropertyType { + PropertyType Element = element; + let BufferElementTypes = [ element ]; +} + +/// llvm::Optional. The corresponding C++ type is generally just the +/// corresponding C++ type of the element. +/// +/// Optional may restrict the range of the operand for some +/// serialization clients. +class Optional : PropertyType { + PropertyType Element = element; + let PassByReference = element.PassByReference; +} + +/// A property of an AST node. +class Property { + ASTNode Class; + string Name = name; + PropertyType Type = type; + + /// A function for reading the property, expressed in terms of a variable + /// "node". + code Read; +} + +/// A rule for creating objects of this type. +class Creator { + ASTNode Class; + + /// A function for creating values of this kind, expressed in terms of a + /// variable `ctx` of type `ASTContext &`. Must also refer to all of the + /// properties by name. + code Create = create; +} + +/// A rule which overrides some of the normal rules. +class Override { + ASTNode Class; + + /// Properties from base classes that should be ignored. + list IgnoredProperties = []; +} diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 25d49fc..7b4c640 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -1,7 +1,8 @@ +class ASTNode; class AttrSubject; class DeclNode - : AttrSubject { + : ASTNode, AttrSubject { DeclNode Base = base; bit Abstract = abstract; string DiagSpelling = diagSpelling; diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 755cd0b..b24199b 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -1,6 +1,7 @@ +class ASTNode; class AttrSubject; -class StmtNode : AttrSubject { +class StmtNode : ASTNode, AttrSubject { StmtNode Base = base; bit Abstract = abstract; } diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td index 835db0b..2a08dcc 100644 --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -1,4 +1,6 @@ -class TypeNode { +class ASTNode; + +class TypeNode : ASTNode { TypeNode Base = base; bit Abstract = abstract; } diff --git a/clang/utils/TableGen/ASTTableGen.cpp b/clang/utils/TableGen/ASTTableGen.cpp index 11e0e42..c49bcd9 100644 --- a/clang/utils/TableGen/ASTTableGen.cpp +++ b/clang/utils/TableGen/ASTTableGen.cpp @@ -60,6 +60,26 @@ std::string clang::tblgen::StmtNode::getId() const { return (Twine(getName()) + "Class").str(); } +/// Emit a string spelling out the C++ value type. +void PropertyType::emitCXXValueTypeName(bool forRead, raw_ostream &out) const { + if (!isGenericSpecialization()) { + if (!forRead && isConstWhenWriting()) + out << "const "; + out << getCXXTypeName(); + } else if (auto elementType = getArrayElementType()) { + out << "llvm::ArrayRef<"; + elementType.emitCXXValueTypeName(forRead, out); + out << ">"; + } else if (auto valueType = getOptionalElementType()) { + out << "llvm::Optional<"; + valueType.emitCXXValueTypeName(forRead, out); + out << ">"; + } else { + //PrintFatalError(getLoc(), "unexpected generic property type"); + abort(); + } +} + // A map from a node to each of its child nodes. using ChildMap = std::multimap; diff --git a/clang/utils/TableGen/ASTTableGen.h b/clang/utils/TableGen/ASTTableGen.h index 185f809..87934f5 100644 --- a/clang/utils/TableGen/ASTTableGen.h +++ b/clang/utils/TableGen/ASTTableGen.h @@ -35,9 +35,37 @@ #define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent" #define LeafTypeClassName "LeafType" -// Property node hierarchy. +// Properties of AST nodes. #define PropertyClassName "Property" #define ClassFieldName "Class" +#define NameFieldName "Name" +#define TypeFieldName "Type" +#define ReadFieldName "Read" + +// Types of properties. +#define PropertyTypeClassName "PropertyType" +#define CXXTypeNameFieldName "CXXName" +#define PassByReferenceFieldName "PassByReference" +#define ConstWhenWritingFieldName "ConstWhenWriting" +#define PackOptionalCodeFieldName "PackOptional" +#define UnpackOptionalCodeFieldName "UnpackOptional" +#define BufferElementTypesFieldName "BufferElementTypes" +#define ArrayTypeClassName "Array" +#define ArrayElementTypeFieldName "Element" +#define OptionalTypeClassName "Optional" +#define OptionalElementTypeFieldName "Element" +#define SubclassPropertyTypeClassName "SubclassPropertyType" +#define SubclassBaseTypeFieldName "Base" +#define SubclassClassNameFieldName "SubclassName" +#define EnumPropertyTypeClassName "EnumPropertyType" + +// Creation rules. +#define CreationRuleClassName "Creator" +#define CreateFieldName "Create" + +// Override rules. +#define OverrideRuleClassName "Override" +#define IgnoredPropertiesFieldName "IgnoredProperties" namespace clang { namespace tblgen { @@ -169,6 +197,147 @@ public: } }; +/// The type of a property. +class PropertyType : public WrappedRecord { +public: + PropertyType(llvm::Record *record = nullptr) : WrappedRecord(record) {} + + /// Is this a generic specialization (i.e. `Array` or `Optional`)? + bool isGenericSpecialization() const { + return get()->isAnonymous(); + } + + /// The abstract type name of the property. Doesn't work for generic + /// specializations. + llvm::StringRef getAbstractTypeName() const { + return get()->getName(); + } + + /// The C++ type name of the property. Doesn't work for generic + /// specializations. + llvm::StringRef getCXXTypeName() const { + return get()->getValueAsString(CXXTypeNameFieldName); + } + void emitCXXValueTypeName(bool forRead, llvm::raw_ostream &out) const; + + /// Whether the C++ type should be passed around by reference. + bool shouldPassByReference() const { + return get()->getValueAsBit(PassByReferenceFieldName); + } + + /// Whether the C++ type should have 'const' prepended when working with + /// a value of the type being written. + bool isConstWhenWriting() const { + return get()->getValueAsBit(ConstWhenWritingFieldName); + } + + /// If this is `Array`, return `T`; otherwise return null. + PropertyType getArrayElementType() const { + if (isSubClassOf(ArrayTypeClassName)) + return get()->getValueAsDef(ArrayElementTypeFieldName); + return nullptr; + } + + /// If this is `Optional`, return `T`; otherwise return null. + PropertyType getOptionalElementType() const { + if (isSubClassOf(OptionalTypeClassName)) + return get()->getValueAsDef(OptionalElementTypeFieldName); + return nullptr; + } + + /// If this is a subclass type, return its superclass type. + PropertyType getSuperclassType() const { + if (isSubClassOf(SubclassPropertyTypeClassName)) + return get()->getValueAsDef(SubclassBaseTypeFieldName); + return nullptr; + } + + // Given that this is a subclass type, return the C++ name of its + // subclass type. This is just the bare class name, suitable for + // use in `cast<>`. + llvm::StringRef getSubclassClassName() const { + return get()->getValueAsString(SubclassClassNameFieldName); + } + + /// Does this represent an enum type? + bool isEnum() const { + return isSubClassOf(EnumPropertyTypeClassName); + } + + llvm::StringRef getPackOptionalCode() const { + return get()->getValueAsString(PackOptionalCodeFieldName); + } + + llvm::StringRef getUnpackOptionalCode() const { + return get()->getValueAsString(UnpackOptionalCodeFieldName); + } + + std::vector getBufferElementTypes() const { + return get()->getValueAsListOfDefs(BufferElementTypesFieldName); + } +}; + +/// A property of an AST node. +class Property : public WrappedRecord { +public: + Property(llvm::Record *record = nullptr) : WrappedRecord(record) {} + + /// Return the name of this property. + llvm::StringRef getName() const { + return get()->getValueAsString(NameFieldName); + } + + /// Return the type of this property. + PropertyType getType() const { + return get()->getValueAsDef(TypeFieldName); + } + + /// Return the class of which this is a property. + ASTNode getClass() const { + return get()->getValueAsDef(ClassFieldName); + } + + /// Return the code for reading this property. + llvm::StringRef getReadCode() const { + return get()->getValueAsString(ReadFieldName); + } +}; + +/// A rule for how to create an AST node from its properties. +class CreationRule : public WrappedRecord { +public: + CreationRule(llvm::Record *record = nullptr) : WrappedRecord(record) {} + + /// Return the class for which this is a creation rule. + /// Should never be abstract. + ASTNode getClass() const { + return get()->getValueAsDef(ClassFieldName); + } + + llvm::StringRef getCreationCode() const { + return get()->getValueAsString(CreateFieldName); + } +}; + +/// A rule which overrides the standard rules for serializing an AST node. +class OverrideRule : public WrappedRecord { +public: + OverrideRule(llvm::Record *record = nullptr) : WrappedRecord(record) {} + + /// Return the class for which this is an override rule. + /// Should never be abstract. + ASTNode getClass() const { + return get()->getValueAsDef(ClassFieldName); + } + + /// Return a set of properties that are unnecessary when serializing + /// this AST node. Generally this is used for inherited properties + /// that are derived for this subclass. + std::vector getIgnoredProperties() const { + return get()->getValueAsListOfStrings(IgnoredPropertiesFieldName); + } +}; + /// A visitor for an AST node hierarchy. Note that `base` can be null for /// the root class. template diff --git a/clang/utils/TableGen/CMakeLists.txt b/clang/utils/TableGen/CMakeLists.txt index 84d5488..7deca99 100644 --- a/clang/utils/TableGen/CMakeLists.txt +++ b/clang/utils/TableGen/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS Support) add_tablegen(clang-tblgen CLANG ASTTableGen.cpp ClangASTNodesEmitter.cpp + ClangASTPropertiesEmitter.cpp ClangAttrEmitter.cpp ClangCommentCommandInfoEmitter.cpp ClangCommentHTMLNamedCharacterReferenceEmitter.cpp diff --git a/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp b/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp new file mode 100644 index 0000000..c80b10e --- /dev/null +++ b/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp @@ -0,0 +1,658 @@ +//=== ClangASTPropsEmitter.cpp - Generate Clang AST properties --*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend emits code for working with Clang AST properties. +// +//===----------------------------------------------------------------------===// + +#include "ASTTableGen.h" +#include "TableGenBackends.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include +using namespace llvm; +using namespace clang; +using namespace clang::tblgen; + +static StringRef getReaderResultType(TypeNode _) { return "QualType"; } + +namespace { + +struct ReaderWriterInfo { + bool IsReader; + + /// The name of the node hierarchy. Not actually sensitive to IsReader, + /// but useful to cache here anyway. + StringRef HierarchyName; + + /// The suffix on classes: Reader/Writer + StringRef ClassSuffix; + + /// The base name of methods: read/write + StringRef MethodPrefix; + + /// The name of the property helper member: R/W + StringRef HelperVariable; + + /// The result type of methods on the class. + StringRef ResultType; + + template + static ReaderWriterInfo forReader() { + return ReaderWriterInfo{ + true, + NodeClass::getASTHierarchyName(), + "Reader", + "read", + "R", + getReaderResultType(NodeClass()) + }; + } + + template + static ReaderWriterInfo forWriter() { + return ReaderWriterInfo{ + false, + NodeClass::getASTHierarchyName(), + "Writer", + "write", + "W", + "void" + }; + } +}; + +struct NodeInfo { + std::vector Properties; + CreationRule Creator = nullptr; + OverrideRule Override = nullptr; +}; + +class ASTPropsEmitter { + raw_ostream &Out; + RecordKeeper &Records; + std::map NodeInfos; + +public: + ASTPropsEmitter(RecordKeeper &records, raw_ostream &out) + : Out(out), Records(records) { + + // Find all the properties. + for (Property property : + records.getAllDerivedDefinitions(PropertyClassName)) { + ASTNode node = property.getClass(); + NodeInfos[node].Properties.push_back(property); + } + + // Find all the creation rules. + for (CreationRule creationRule : + records.getAllDerivedDefinitions(CreationRuleClassName)) { + ASTNode node = creationRule.getClass(); + + auto &info = NodeInfos[node]; + if (info.Creator) { + PrintFatalError(creationRule.getLoc(), + "multiple creator rules for \"" + node.getName() + + "\""); + } + info.Creator = creationRule; + } + + // Find all the override rules. + for (OverrideRule overrideRule : + records.getAllDerivedDefinitions(OverrideRuleClassName)) { + ASTNode node = overrideRule.getClass(); + + auto &info = NodeInfos[node]; + if (info.Override) { + PrintFatalError(overrideRule.getLoc(), + "multiple override rules for \"" + node.getName() + + "\""); + } + info.Override = overrideRule; + } + + Validator(*this).validate(); + } + + void visitAllProperties(ASTNode derived, const NodeInfo &derivedInfo, + function_ref visit) { + std::set ignoredProperties; + + auto overrideRule = derivedInfo.Override; + if (overrideRule) { + auto list = overrideRule.getIgnoredProperties(); + ignoredProperties.insert(list.begin(), list.end()); + } + + for (ASTNode node = derived; node; node = node.getBase()) { + auto it = NodeInfos.find(node); + + // Ignore intermediate nodes that don't add interesting properties. + if (it == NodeInfos.end()) continue; + auto &info = it->second; + + for (Property prop : info.Properties) { + if (ignoredProperties.count(prop.getName())) + continue; + + visit(prop); + } + } + } + + template + void emitReaderClass() { + auto info = ReaderWriterInfo::forReader(); + emitReaderWriterClass(info); + } + + template + void emitWriterClass() { + auto info = ReaderWriterInfo::forWriter(); + emitReaderWriterClass(info); + } + + template + void emitReaderWriterClass(const ReaderWriterInfo &info); + + template + void emitNodeReaderWriterMethod(NodeClass node, + const ReaderWriterInfo &info); + + void emitReadOfProperty(Property property); + void emitWriteOfProperty(Property property); + +private: + class Validator { + const ASTPropsEmitter &Emitter; + std::set ValidatedNodes; + + public: + Validator(const ASTPropsEmitter &emitter) : Emitter(emitter) {} + void validate(); + + private: + void validateNode(ASTNode node, const NodeInfo &nodeInfo); + void validateType(PropertyType type, WrappedRecord context); + }; +}; + +} // end anonymous namespace + +void ASTPropsEmitter::Validator::validate() { + for (auto &entry : Emitter.NodeInfos) { + validateNode(entry.first, entry.second); + } + + if (ErrorsPrinted > 0) { + PrintFatalError("property validation failed"); + } +} + +void ASTPropsEmitter::Validator::validateNode(ASTNode node, + const NodeInfo &nodeInfo) { + if (!ValidatedNodes.insert(node).second) return; + + // A map from property name to property. + std::map allProperties; + + // Walk the hierarchy, ignoring nodes that don't declare anything + // interesting. + for (auto base = node; base; base = base.getBase()) { + auto it = Emitter.NodeInfos.find(base); + if (it == Emitter.NodeInfos.end()) continue; + + auto &baseInfo = it->second; + for (Property property : baseInfo.Properties) { + validateType(property.getType(), property); + + auto result = allProperties.insert( + std::make_pair(property.getName(), property)); + + // Diagnose non-unique properties. + if (!result.second) { + // The existing property is more likely to be associated with a + // derived node, so use it as the error. + Property existingProperty = result.first->second; + PrintError(existingProperty.getLoc(), + "multiple properties named \"" + property.getName() + + "\" in hierarchy of " + node.getName()); + PrintNote(property.getLoc(), "existing property"); + } + } + } +} + +void ASTPropsEmitter::Validator::validateType(PropertyType type, + WrappedRecord context) { + if (!type.isGenericSpecialization()) { + if (type.getCXXTypeName() == "") { + PrintError(type.getLoc(), + "type is not generic but has no C++ type name"); + if (context) PrintNote(context.getLoc(), "type used here"); + } + } else if (auto eltType = type.getArrayElementType()) { + validateType(eltType, context); + } else if (auto valueType = type.getOptionalElementType()) { + validateType(valueType, context); + + if (valueType.getPackOptionalCode().empty()) { + PrintError(valueType.getLoc(), + "type doesn't provide optional-packing code"); + if (context) PrintNote(context.getLoc(), "type used here"); + } else if (valueType.getUnpackOptionalCode().empty()) { + PrintError(valueType.getLoc(), + "type doesn't provide optional-unpacking code"); + if (context) PrintNote(context.getLoc(), "type used here"); + } + } else { + PrintError(type.getLoc(), "unknown generic property type"); + if (context) PrintNote(context.getLoc(), "type used here"); + } +} + +/****************************************************************************/ +/**************************** AST READER/WRITERS ****************************/ +/****************************************************************************/ + +template +void ASTPropsEmitter::emitReaderWriterClass(const ReaderWriterInfo &info) { + StringRef suffix = info.ClassSuffix; + StringRef var = info.HelperVariable; + + // Enter the class declaration. + Out << "template \n" + "class Abstract" << info.HierarchyName << suffix << " {\n" + "public:\n" + " Property" << suffix << " &" << var << ";\n\n"; + + // Emit the constructor. + Out << " Abstract" << info.HierarchyName << suffix + << "(Property" << suffix << " &" << var << ") : " + << var << "(" << var << ") {}\n\n"; + + // Emit a method that dispatches on a kind to the appropriate node-specific + // method. + Out << " " << info.ResultType << " " << info.MethodPrefix << "("; + if (info.IsReader) + Out << NodeClass::getASTIdTypeName() << " kind"; + else + Out << "const " << info.HierarchyName << " *node"; + Out << ") {\n" + " switch ("; + if (info.IsReader) + Out << "kind"; + else + Out << "node->" << NodeClass::getASTIdAccessorName() << "()"; + Out << ") {\n"; + visitASTNodeHierarchy(Records, [&](NodeClass node, NodeClass _) { + if (node.isAbstract()) return; + Out << " case " << info.HierarchyName << "::" << node.getId() << ":\n" + " return " << info.MethodPrefix << node.getClassName() << "("; + if (!info.IsReader) + Out << "static_cast(node)"; + Out << ");\n"; + }); + Out << " }\n" + " llvm_unreachable(\"bad kind\");\n" + " }\n\n"; + + // Emit node-specific methods for all the concrete nodes. + visitASTNodeHierarchy(Records, + [&](NodeClass node, NodeClass base) { + if (node.isAbstract()) return; + emitNodeReaderWriterMethod(node, info); + }); + + // Finish the class. + Out << "};\n\n"; +} + +/// Emit a reader method for the given concrete AST node class. +template +void ASTPropsEmitter::emitNodeReaderWriterMethod(NodeClass node, + const ReaderWriterInfo &info) { + // Declare and start the method. + Out << " " << info.ResultType << " " + << info.MethodPrefix << node.getClassName() << "("; + if (!info.IsReader) + Out << "const " << node.getClassName() << " *node"; + Out << ") {\n"; + if (info.IsReader) + Out << " auto &ctx = " << info.HelperVariable << ".getASTContext();\n"; + + // Find the information for this node. + auto it = NodeInfos.find(node); + if (it == NodeInfos.end()) + PrintFatalError(node.getLoc(), + "no information about how to deserialize \"" + + node.getName() + "\""); + auto &nodeInfo = it->second; + + StringRef creationCode; + if (info.IsReader) { + // We should have a creation rule. + if (!nodeInfo.Creator) + PrintFatalError(node.getLoc(), + "no " CreationRuleClassName " for \"" + + node.getName() + "\""); + + creationCode = nodeInfo.Creator.getCreationCode(); + } + + // Emit code to read all the properties. + visitAllProperties(node, nodeInfo, [&](Property prop) { + // Verify that the creation code refers to this property. + if (info.IsReader && creationCode.find(prop.getName()) == StringRef::npos) + PrintFatalError(nodeInfo.Creator.getLoc(), + "creation code for " + node.getName() + + " doesn't refer to property \"" + + prop.getName() + "\""); + + // Emit code to read or write this property. + if (info.IsReader) + emitReadOfProperty(prop); + else + emitWriteOfProperty(prop); + }); + + // Emit the final creation code. + if (info.IsReader) + Out << " " << creationCode << "\n"; + + // Finish the method declaration. + Out << " }\n\n"; +} + +static void emitBasicReaderWriterMethodSuffix(raw_ostream &out, + PropertyType type, + bool isForRead) { + if (!type.isGenericSpecialization()) { + out << type.getAbstractTypeName(); + } else if (auto eltType = type.getArrayElementType()) { + out << "Array"; + // We only include an explicit template argument for reads so that + // we don't cause spurious const mismatches. + if (isForRead) { + out << "<"; + eltType.emitCXXValueTypeName(isForRead, out); + out << ">"; + } + } else if (auto valueType = type.getOptionalElementType()) { + out << "Optional"; + // We only include an explicit template argument for reads so that + // we don't cause spurious const mismatches. + if (isForRead) { + out << "<"; + valueType.emitCXXValueTypeName(isForRead, out); + out << ">"; + } + } else { + PrintFatalError(type.getLoc(), "unexpected generic property type"); + } +} + +/// Emit code to read the given property in a node-reader method. +void ASTPropsEmitter::emitReadOfProperty(Property property) { + PropertyType type = property.getType(); + auto name = property.getName(); + + // Declare all the necessary buffers. + auto bufferTypes = type.getBufferElementTypes(); + for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) { + Out << " llvm::SmallVector<"; + PropertyType(bufferTypes[i]).emitCXXValueTypeName(/*for read*/ true, Out); + Out << ", 8> " << name << "_buffer_" << i << ";\n"; + } + + // T prop = R.find("prop").read##ValueType(buffers...); + // We intentionally ignore shouldPassByReference here: we're going to + // get a pr-value back from read(), and we should be able to forward + // that in the creation rule. + Out << " "; + type.emitCXXValueTypeName(true, Out); + Out << " " << name << " = R.find(\"" << name << "\")." + << (type.isGenericSpecialization() ? "template " : "") << "read"; + emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ true); + Out << "("; + for (size_t i = 0, e = bufferTypes.size(); i != e; ++i) { + Out << (i > 0 ? ", " : "") << name << "_buffer_" << i; + } + Out << ");\n"; +} + +/// Emit code to write the given property in a node-writer method. +void ASTPropsEmitter::emitWriteOfProperty(Property property) { + // Focus down to the property: + // W.find("prop").write##ValueType(value); + Out << " W.find(\"" << property.getName() << "\").write"; + emitBasicReaderWriterMethodSuffix(Out, property.getType(), + /*for read*/ false); + Out << "(" << property.getReadCode() << ");\n"; +} + +/// Emit an .inc file that defines the AbstractFooReader class +/// for the given AST class hierarchy. +template +static void emitASTReader(RecordKeeper &records, raw_ostream &out, + StringRef description) { + emitSourceFileHeader(description, out); + + ASTPropsEmitter(records, out).emitReaderClass(); +} + +void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) { + emitASTReader(records, out, "A CRTP reader for Clang Type nodes"); +} + +/// Emit an .inc file that defines the AbstractFooWriter class +/// for the given AST class hierarchy. +template +static void emitASTWriter(RecordKeeper &records, raw_ostream &out, + StringRef description) { + emitSourceFileHeader(description, out); + + ASTPropsEmitter(records, out).emitWriterClass(); +} + +void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) { + emitASTWriter(records, out, "A CRTP writer for Clang Type nodes"); +} + +/****************************************************************************/ +/*************************** BASIC READER/WRITERS ***************************/ +/****************************************************************************/ + +static void emitDispatcherTemplate(ArrayRef types, raw_ostream &out, + const ReaderWriterInfo &info) { + // Declare the {Read,Write}Dispatcher template. + StringRef dispatcherPrefix = (info.IsReader ? "Read" : "Write"); + out << "template \n" + "struct " << dispatcherPrefix << "Dispatcher;\n"; + + // Declare a specific specialization of the dispatcher template. + auto declareSpecialization = + [&](StringRef specializationParameters, + const Twine &cxxTypeName, + StringRef methodSuffix) { + StringRef var = info.HelperVariable; + out << "template " << specializationParameters << "\n" + "struct " << dispatcherPrefix << "Dispatcher<" + << cxxTypeName << "> {\n"; + out << " template \n" + " static " << (info.IsReader ? cxxTypeName : "void") << " " + << info.MethodPrefix + << "(Basic" << info.ClassSuffix << " &" << var + << ", Args &&... args) {\n" + " return " << var << "." + << info.MethodPrefix << methodSuffix + << "(std::forward(args)...);\n" + " }\n" + "};\n"; + }; + + // Declare explicit specializations for each of the concrete types. + for (PropertyType type : types) { + declareSpecialization("<>", + type.getCXXTypeName(), + type.getAbstractTypeName()); + // Also declare a specialization for the const type when appropriate. + if (!info.IsReader && type.isConstWhenWriting()) { + declareSpecialization("<>", + "const " + type.getCXXTypeName(), + type.getAbstractTypeName()); + } + } + // Declare partial specializations for ArrayRef and Optional. + declareSpecialization("", + "llvm::ArrayRef", + "Array"); + declareSpecialization("", + "llvm::Optional", + "Optional"); + out << "\n"; +} + +static void emitPackUnpackOptionalTemplate(ArrayRef types, + raw_ostream &out, + const ReaderWriterInfo &info) { + StringRef classPrefix = (info.IsReader ? "Unpack" : "Pack"); + StringRef methodName = (info.IsReader ? "unpack" : "pack"); + + // Declare the {Pack,Unpack}OptionalValue template. + out << "template \n" + "struct " << classPrefix << "OptionalValue;\n"; + + auto declareSpecialization = [&](const Twine &typeName, + StringRef code) { + out << "template <>\n" + "struct " << classPrefix << "OptionalValue<" << typeName << "> {\n" + " static " << (info.IsReader ? "Optional<" : "") << typeName + << (info.IsReader ? "> " : " ") << methodName << "(" + << (info.IsReader ? "" : "Optional<") << typeName + << (info.IsReader ? "" : ">") << " value) {\n" + " return " << code << ";\n" + " }\n" + "};\n"; + }; + + for (PropertyType type : types) { + StringRef code = (info.IsReader ? type.getUnpackOptionalCode() + : type.getPackOptionalCode()); + if (code.empty()) continue; + + StringRef typeName = type.getCXXTypeName(); + declareSpecialization(typeName, code); + if (type.isConstWhenWriting() && !info.IsReader) + declareSpecialization("const " + typeName, code); + } + out << "\n"; +} + +static void emitBasicReaderWriterTemplate(ArrayRef types, + raw_ostream &out, + const ReaderWriterInfo &info) { + // Emit the Basic{Reader,Writer}Base template. + out << "template \n" + "class Basic" << info.ClassSuffix << "Base {\n"; + if (info.IsReader) + out << " ASTContext &C;\n"; + out << "protected:\n" + " Basic" << info.ClassSuffix << "Base" + << (info.IsReader ? "(ASTContext &ctx) : C(ctx)" : "()") + << " {}\n" + "public:\n"; + if (info.IsReader) + out << " ASTContext &getASTContext() { return C; }\n"; + out << " Impl &asImpl() { return static_cast(*this); }\n"; + + auto enterReaderWriterMethod = [&](StringRef cxxTypeName, + StringRef abstractTypeName, + bool shouldPassByReference, + bool constWhenWriting) { + out << " " << (info.IsReader ? cxxTypeName : "void") + << " " << info.MethodPrefix << abstractTypeName << "("; + if (!info.IsReader) + out << (shouldPassByReference || constWhenWriting ? "const " : "") + << cxxTypeName + << (shouldPassByReference ? " &" : "") << " value"; + out << ") {\n"; + }; + + // Emit {read,write}ValueType methods for all the enum and subclass types + // that default to using the integer/base-class implementations. + for (PropertyType type : types) { + if (type.isEnum()) { + enterReaderWriterMethod(type.getCXXTypeName(), + type.getAbstractTypeName(), + /*pass by reference*/ false, + /*const when writing*/ false); + if (info.IsReader) + out << " return " << type.getCXXTypeName() + << "(asImpl().readUInt32());\n"; + else + out << " asImpl().writeUInt32(uint32_t(value));\n"; + out << " }\n"; + } else if (PropertyType superclass = type.getSuperclassType()) { + enterReaderWriterMethod(type.getCXXTypeName(), + type.getAbstractTypeName(), + /*pass by reference*/ false, + /*const when writing*/ type.isConstWhenWriting()); + if (info.IsReader) + out << " return cast_or_null<" << type.getSubclassClassName() + << ">(asImpl().read" + << superclass.getAbstractTypeName() + << "());\n"; + else + out << " asImpl().write" << superclass.getAbstractTypeName() + << "(value);\n"; + out << " }\n"; + } else { + // The other types can't be handled as trivially. + } + } + out << "};\n\n"; +} + +static void emitBasicReaderWriterFile(RecordKeeper &records, raw_ostream &out, + const ReaderWriterInfo &info) { + auto types = records.getAllDerivedDefinitions(PropertyTypeClassName); + + emitDispatcherTemplate(types, out, info); + emitPackUnpackOptionalTemplate(types, out, info); + emitBasicReaderWriterTemplate(types, out, info); +} + +/// Emit an .inc file that defines some helper classes for reading +/// basic values. +void clang::EmitClangBasicReader(RecordKeeper &records, raw_ostream &out) { + emitSourceFileHeader("Helper classes for BasicReaders", out); + + // Use any property, we won't be using those properties. + auto info = ReaderWriterInfo::forReader(); + emitBasicReaderWriterFile(records, out, info); +} + +/// Emit an .inc file that defines some helper classes for writing +/// basic values. +void clang::EmitClangBasicWriter(RecordKeeper &records, raw_ostream &out) { + emitSourceFileHeader("Helper classes for BasicWriters", out); + + // Use any property, we won't be using those properties. + auto info = ReaderWriterInfo::forWriter(); + emitBasicReaderWriterFile(records, out, info); +} diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp index c118a32..75e305f 100644 --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -42,6 +42,8 @@ enum ActionType { GenClangAttrParsedAttrKinds, GenClangAttrTextNodeDump, GenClangAttrNodeTraverse, + GenClangBasicReader, + GenClangBasicWriter, GenClangDiagsDefs, GenClangDiagGroups, GenClangDiagsIndexName, @@ -131,6 +133,10 @@ cl::opt Action( "Generate Clang diagnostic groups"), clEnumValN(GenClangDiagsIndexName, "gen-clang-diags-index-name", "Generate Clang diagnostic name index"), + clEnumValN(GenClangBasicReader, "gen-clang-basic-reader", + "Generate Clang BasicReader classes"), + clEnumValN(GenClangBasicWriter, "gen-clang-basic-writer", + "Generate Clang BasicWriter classes"), clEnumValN(GenClangCommentNodes, "gen-clang-comment-nodes", "Generate Clang AST comment nodes"), clEnumValN(GenClangDeclNodes, "gen-clang-decl-nodes", @@ -276,6 +282,12 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenClangTypeNodes: EmitClangTypeNodes(Records, OS); break; + case GenClangBasicReader: + EmitClangBasicReader(Records, OS); + break; + case GenClangBasicWriter: + EmitClangBasicWriter(Records, OS); + break; case GenClangOpcodes: EmitClangOpcodes(Records, OS); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h index c3d1de8..7ac2e0e 100644 --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -27,8 +27,11 @@ namespace clang { void EmitClangDeclContext(llvm::RecordKeeper &RK, llvm::raw_ostream &OS); void EmitClangASTNodes(llvm::RecordKeeper &RK, llvm::raw_ostream &OS, const std::string &N, const std::string &S); +void EmitClangBasicReader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangBasicWriter(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangTypeNodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); - +void EmitClangTypeReader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangTypeWriter(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrParserStringSwitches(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangAttrSubjectMatchRulesParserStringSwitches(