summaryrefslogtreecommitdiff
path: root/clang/utils
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-01-17 20:45:01 +0000
commit706b4fc47bbc608932d3b491ae19a3b9cde9497b (patch)
tree4adf86a776049cbf7f69a1929c4babcbbef925eb /clang/utils
parent7cc9cf2bf09f069cb2dd947ead05d0b54301fb71 (diff)
Notes
Diffstat (limited to 'clang/utils')
-rw-r--r--clang/utils/TableGen/ASTTableGen.cpp142
-rw-r--r--clang/utils/TableGen/ASTTableGen.h502
-rw-r--r--clang/utils/TableGen/ClangASTNodesEmitter.cpp177
-rw-r--r--clang/utils/TableGen/ClangASTPropertiesEmitter.cpp867
-rw-r--r--clang/utils/TableGen/ClangAttrEmitter.cpp20
-rw-r--r--clang/utils/TableGen/ClangDiagnosticsEmitter.cpp6
-rw-r--r--clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp206
-rw-r--r--clang/utils/TableGen/ClangTypeNodesEmitter.cpp194
-rw-r--r--clang/utils/TableGen/MveEmitter.cpp1882
-rw-r--r--clang/utils/TableGen/NeonEmitter.cpp632
-rw-r--r--clang/utils/TableGen/TableGen.cpp61
-rw-r--r--clang/utils/TableGen/TableGenBackends.h11
-rw-r--r--clang/utils/convert_arm_neon.py172
13 files changed, 4246 insertions, 626 deletions
diff --git a/clang/utils/TableGen/ASTTableGen.cpp b/clang/utils/TableGen/ASTTableGen.cpp
new file mode 100644
index 000000000000..3f6da40964e0
--- /dev/null
+++ b/clang/utils/TableGen/ASTTableGen.cpp
@@ -0,0 +1,142 @@
+//=== ASTTableGen.cpp - Helper functions for working with AST records -----===//
+//
+// 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 file defines some helper functions for working with tblegen reocrds
+// for the Clang AST: that is, the contents of files such as DeclNodes.td,
+// StmtNodes.td, and TypeNodes.td.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTTableGen.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/Error.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::tblgen;
+
+llvm::StringRef clang::tblgen::HasProperties::getName() const {
+ if (auto node = getAs<ASTNode>()) {
+ return node.getName();
+ } else if (auto typeCase = getAs<TypeCase>()) {
+ return typeCase.getCaseName();
+ } else {
+ PrintFatalError(getLoc(), "unexpected node declaring properties");
+ }
+}
+
+static StringRef removeExpectedNodeNameSuffix(Record *node, StringRef suffix) {
+ StringRef nodeName = node->getName();
+ if (!nodeName.endswith(suffix)) {
+ PrintFatalError(node->getLoc(),
+ Twine("name of node doesn't end in ") + suffix);
+ }
+ return nodeName.drop_back(suffix.size());
+}
+
+// Decl node names don't end in Decl for historical reasons, and it would
+// be somewhat annoying to fix now. Conveniently, this means the ID matches
+// is exactly the node name, and the class name is simply that plus Decl.
+std::string clang::tblgen::DeclNode::getClassName() const {
+ return (Twine(getName()) + "Decl").str();
+}
+StringRef clang::tblgen::DeclNode::getId() const {
+ return getName();
+}
+
+// Type nodes are all named ending in Type, just like the corresponding
+// C++ class, and the ID just strips this suffix.
+StringRef clang::tblgen::TypeNode::getClassName() const {
+ return getName();
+}
+StringRef clang::tblgen::TypeNode::getId() const {
+ return removeExpectedNodeNameSuffix(getRecord(), "Type");
+}
+
+// Stmt nodes are named the same as the C++ class, which has no regular
+// naming convention (all the non-expression statements end in Stmt,
+// and *many* expressions end in Expr, but there are also several
+// core expression classes like IntegerLiteral and BinaryOperator with
+// no standard suffix). The ID adds "Class" for historical reasons.
+StringRef clang::tblgen::StmtNode::getClassName() const {
+ return getName();
+}
+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<ASTNode, ASTNode>;
+
+static void visitASTNodeRecursive(ASTNode node, ASTNode base,
+ const ChildMap &map,
+ ASTNodeHierarchyVisitor<ASTNode> visit) {
+ visit(node, base);
+
+ auto i = map.lower_bound(node), e = map.upper_bound(node);
+ for (; i != e; ++i) {
+ visitASTNodeRecursive(i->second, node, map, visit);
+ }
+}
+
+static void visitHierarchy(RecordKeeper &records,
+ StringRef nodeClassName,
+ ASTNodeHierarchyVisitor<ASTNode> visit) {
+ // Check for the node class, just as a sanity check.
+ if (!records.getClass(nodeClassName)) {
+ PrintFatalError(Twine("cannot find definition for node class ")
+ + nodeClassName);
+ }
+
+ // Find all the nodes in the hierarchy.
+ auto nodes = records.getAllDerivedDefinitions(nodeClassName);
+
+ // Derive the child map.
+ ChildMap hierarchy;
+ ASTNode root;
+ for (ASTNode node : nodes) {
+ if (auto base = node.getBase())
+ hierarchy.insert(std::make_pair(base, node));
+ else if (root)
+ PrintFatalError(node.getLoc(),
+ "multiple root nodes in " + nodeClassName + " hierarchy");
+ else
+ root = node;
+ }
+ if (!root)
+ PrintFatalError(Twine("no root node in ") + nodeClassName + " hierarchy");
+
+ // Now visit the map recursively, starting at the root node.
+ visitASTNodeRecursive(root, ASTNode(), hierarchy, visit);
+}
+
+void clang::tblgen::visitASTNodeHierarchyImpl(RecordKeeper &records,
+ StringRef nodeClassName,
+ ASTNodeHierarchyVisitor<ASTNode> visit) {
+ visitHierarchy(records, nodeClassName, visit);
+}
diff --git a/clang/utils/TableGen/ASTTableGen.h b/clang/utils/TableGen/ASTTableGen.h
new file mode 100644
index 000000000000..ab9429f3feee
--- /dev/null
+++ b/clang/utils/TableGen/ASTTableGen.h
@@ -0,0 +1,502 @@
+//=== ASTTableGen.h - Common definitions for AST node tablegen --*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_AST_TABLEGEN_H
+#define CLANG_AST_TABLEGEN_H
+
+#include "llvm/TableGen/Record.h"
+#include "llvm/ADT/STLExtras.h"
+
+// These are spellings in the tblgen files.
+
+#define HasPropertiesClassName "HasProperties"
+
+// ASTNodes and their common fields. `Base` is actually defined
+// in subclasses, but it's still common across the hierarchies.
+#define ASTNodeClassName "ASTNode"
+#define BaseFieldName "Base"
+#define AbstractFieldName "Abstract"
+
+// Comment node hierarchy.
+#define CommentNodeClassName "CommentNode"
+
+// Decl node hierarchy.
+#define DeclNodeClassName "DeclNode"
+#define DeclContextNodeClassName "DeclContext"
+
+// Stmt node hierarchy.
+#define StmtNodeClassName "StmtNode"
+
+// Type node hierarchy.
+#define TypeNodeClassName "TypeNode"
+#define AlwaysDependentClassName "AlwaysDependent"
+#define NeverCanonicalClassName "NeverCanonical"
+#define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent"
+#define LeafTypeClassName "LeafType"
+
+// Cases of various non-ASTNode structured types like DeclarationName.
+#define TypeKindClassName "PropertyTypeKind"
+#define KindTypeFieldName "KindType"
+#define KindPropertyNameFieldName "KindPropertyName"
+#define TypeCaseClassName "PropertyTypeCase"
+
+// 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 ConditionalCodeFieldName "Conditional"
+#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"
+
+// Write helper rules.
+#define ReadHelperRuleClassName "ReadHelper"
+#define HelperCodeFieldName "Code"
+
+// Creation rules.
+#define CreationRuleClassName "Creator"
+#define CreateFieldName "Create"
+
+// Override rules.
+#define OverrideRuleClassName "Override"
+#define IgnoredPropertiesFieldName "IgnoredProperties"
+
+namespace clang {
+namespace tblgen {
+
+class WrappedRecord {
+ llvm::Record *Record;
+
+protected:
+ WrappedRecord(llvm::Record *record = nullptr) : Record(record) {}
+
+ llvm::Record *get() const {
+ assert(Record && "accessing null record");
+ return Record;
+ }
+
+public:
+ llvm::Record *getRecord() const { return Record; }
+
+ explicit operator bool() const { return Record != nullptr; }
+
+ llvm::ArrayRef<llvm::SMLoc> getLoc() const {
+ return get()->getLoc();
+ }
+
+ /// Does the node inherit from the given TableGen class?
+ bool isSubClassOf(llvm::StringRef className) const {
+ return get()->isSubClassOf(className);
+ }
+
+ template <class NodeClass>
+ NodeClass getAs() const {
+ return (isSubClassOf(NodeClass::getTableGenNodeClassName())
+ ? NodeClass(get()) : NodeClass());
+ }
+
+ friend bool operator<(WrappedRecord lhs, WrappedRecord rhs) {
+ assert(lhs && rhs && "sorting null nodes");
+ return lhs.get()->getName() < rhs.get()->getName();
+ }
+ friend bool operator>(WrappedRecord lhs, WrappedRecord rhs) {
+ return rhs < lhs;
+ }
+ friend bool operator<=(WrappedRecord lhs, WrappedRecord rhs) {
+ return !(rhs < lhs);
+ }
+ friend bool operator>=(WrappedRecord lhs, WrappedRecord rhs) {
+ return !(lhs < rhs);
+ }
+ friend bool operator==(WrappedRecord lhs, WrappedRecord rhs) {
+ // This should handle null nodes.
+ return lhs.getRecord() == rhs.getRecord();
+ }
+ friend bool operator!=(WrappedRecord lhs, WrappedRecord rhs) {
+ return !(lhs == rhs);
+ }
+};
+
+/// Anything in the AST that has properties.
+class HasProperties : public WrappedRecord {
+public:
+ static constexpr llvm::StringRef ClassName = HasPropertiesClassName;
+
+ HasProperties(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+ llvm::StringRef getName() const;
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return HasPropertiesClassName;
+ }
+};
+
+/// An (optional) reference to a TableGen node representing a class
+/// in one of Clang's AST hierarchies.
+class ASTNode : public HasProperties {
+public:
+ ASTNode(llvm::Record *record = nullptr) : HasProperties(record) {}
+
+ llvm::StringRef getName() const {
+ return get()->getName();
+ }
+
+ /// Return the node for the base, if there is one.
+ ASTNode getBase() const {
+ return get()->getValueAsOptionalDef(BaseFieldName);
+ }
+
+ /// Is the corresponding class abstract?
+ bool isAbstract() const {
+ return get()->getValueAsBit(AbstractFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return ASTNodeClassName;
+ }
+};
+
+class DeclNode : public ASTNode {
+public:
+ DeclNode(llvm::Record *record = nullptr) : ASTNode(record) {}
+
+ llvm::StringRef getId() const;
+ std::string getClassName() const;
+ DeclNode getBase() const { return DeclNode(ASTNode::getBase().getRecord()); }
+
+ static llvm::StringRef getASTHierarchyName() {
+ return "Decl";
+ }
+ static llvm::StringRef getASTIdTypeName() {
+ return "Decl::Kind";
+ }
+ static llvm::StringRef getASTIdAccessorName() {
+ return "getKind";
+ }
+ static llvm::StringRef getTableGenNodeClassName() {
+ return DeclNodeClassName;
+ }
+};
+
+class TypeNode : public ASTNode {
+public:
+ TypeNode(llvm::Record *record = nullptr) : ASTNode(record) {}
+
+ llvm::StringRef getId() const;
+ llvm::StringRef getClassName() const;
+ TypeNode getBase() const { return TypeNode(ASTNode::getBase().getRecord()); }
+
+ static llvm::StringRef getASTHierarchyName() {
+ return "Type";
+ }
+ static llvm::StringRef getASTIdTypeName() {
+ return "Type::TypeClass";
+ }
+ static llvm::StringRef getASTIdAccessorName() {
+ return "getTypeClass";
+ }
+ static llvm::StringRef getTableGenNodeClassName() {
+ return TypeNodeClassName;
+ }
+};
+
+class StmtNode : public ASTNode {
+public:
+ StmtNode(llvm::Record *record = nullptr) : ASTNode(record) {}
+
+ std::string getId() const;
+ llvm::StringRef getClassName() const;
+ StmtNode getBase() const { return StmtNode(ASTNode::getBase().getRecord()); }
+
+ static llvm::StringRef getASTHierarchyName() {
+ return "Stmt";
+ }
+ static llvm::StringRef getASTIdTypeName() {
+ return "Stmt::StmtClass";
+ }
+ static llvm::StringRef getASTIdAccessorName() {
+ return "getStmtClass";
+ }
+ static llvm::StringRef getTableGenNodeClassName() {
+ return StmtNodeClassName;
+ }
+};
+
+/// 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<T>` or `Optional<T>`)?
+ 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<T>`, return `T`; otherwise return null.
+ PropertyType getArrayElementType() const {
+ if (isSubClassOf(ArrayTypeClassName))
+ return get()->getValueAsDef(ArrayElementTypeFieldName);
+ return nullptr;
+ }
+
+ /// If this is `Optional<T>`, 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<llvm::Record*> getBufferElementTypes() const {
+ return get()->getValueAsListOfDefs(BufferElementTypesFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return PropertyTypeClassName;
+ }
+};
+
+/// A rule for returning the kind of a type.
+class TypeKindRule : public WrappedRecord {
+public:
+ TypeKindRule(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+ /// Return the type to which this applies.
+ PropertyType getParentType() const {
+ return get()->getValueAsDef(TypeFieldName);
+ }
+
+ /// Return the type of the kind.
+ PropertyType getKindType() const {
+ return get()->getValueAsDef(KindTypeFieldName);
+ }
+
+ /// Return the name to use for the kind property.
+ llvm::StringRef getKindPropertyName() const {
+ return get()->getValueAsString(KindPropertyNameFieldName);
+ }
+
+ /// Return the code for reading the kind value.
+ llvm::StringRef getReadCode() const {
+ return get()->getValueAsString(ReadFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return TypeKindClassName;
+ }
+};
+
+/// An implementation case of a property type.
+class TypeCase : public HasProperties {
+public:
+ TypeCase(llvm::Record *record = nullptr) : HasProperties(record) {}
+
+ /// Return the name of this case.
+ llvm::StringRef getCaseName() const {
+ return get()->getValueAsString(NameFieldName);
+ }
+
+ /// Return the type of which this is a case.
+ PropertyType getParentType() const {
+ return get()->getValueAsDef(TypeFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return TypeCaseClassName;
+ }
+};
+
+/// 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.
+ HasProperties getClass() const {
+ return get()->getValueAsDef(ClassFieldName);
+ }
+
+ /// Return the code for reading this property.
+ llvm::StringRef getReadCode() const {
+ return get()->getValueAsString(ReadFieldName);
+ }
+
+ /// Return the code for determining whether to add this property.
+ llvm::StringRef getCondition() const {
+ return get()->getValueAsString(ConditionalCodeFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return PropertyClassName;
+ }
+};
+
+/// A rule for running some helper code for reading properties from
+/// a value (which is actually done when writing the value out).
+class ReadHelperRule : public WrappedRecord {
+public:
+ ReadHelperRule(llvm::Record *record = nullptr) : WrappedRecord(record) {}
+
+ /// Return the class for which this is a creation rule.
+ /// Should never be abstract.
+ HasProperties getClass() const {
+ return get()->getValueAsDef(ClassFieldName);
+ }
+
+ llvm::StringRef getHelperCode() const {
+ return get()->getValueAsString(HelperCodeFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return ReadHelperRuleClassName;
+ }
+};
+
+/// 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.
+ HasProperties getClass() const {
+ return get()->getValueAsDef(ClassFieldName);
+ }
+
+ llvm::StringRef getCreationCode() const {
+ return get()->getValueAsString(CreateFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return CreationRuleClassName;
+ }
+};
+
+/// 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.
+ HasProperties 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<llvm::StringRef> getIgnoredProperties() const {
+ return get()->getValueAsListOfStrings(IgnoredPropertiesFieldName);
+ }
+
+ static llvm::StringRef getTableGenNodeClassName() {
+ return OverrideRuleClassName;
+ }
+};
+
+/// A visitor for an AST node hierarchy. Note that `base` can be null for
+/// the root class.
+template <class NodeClass>
+using ASTNodeHierarchyVisitor =
+ llvm::function_ref<void(NodeClass node, NodeClass base)>;
+
+void visitASTNodeHierarchyImpl(llvm::RecordKeeper &records,
+ llvm::StringRef nodeClassName,
+ ASTNodeHierarchyVisitor<ASTNode> visit);
+
+template <class NodeClass>
+void visitASTNodeHierarchy(llvm::RecordKeeper &records,
+ ASTNodeHierarchyVisitor<NodeClass> visit) {
+ visitASTNodeHierarchyImpl(records, NodeClass::getTableGenNodeClassName(),
+ [visit](ASTNode node, ASTNode base) {
+ visit(NodeClass(node.getRecord()),
+ NodeClass(base.getRecord()));
+ });
+}
+
+} // end namespace clang::tblgen
+} // end namespace clang
+
+#endif
diff --git a/clang/utils/TableGen/ClangASTNodesEmitter.cpp b/clang/utils/TableGen/ClangASTNodesEmitter.cpp
index 3ece9be6a5c7..1cc46cb06570 100644
--- a/clang/utils/TableGen/ClangASTNodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangASTNodesEmitter.cpp
@@ -10,8 +10,10 @@
//
//===----------------------------------------------------------------------===//
+#include "ASTTableGen.h"
#include "TableGenBackends.h"
+#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <cctype>
@@ -19,6 +21,8 @@
#include <set>
#include <string>
using namespace llvm;
+using namespace clang;
+using namespace clang::tblgen;
/// ClangASTNodesEmitter - The top-level class emits .inc files containing
/// declarations of Clang statements.
@@ -26,12 +30,15 @@ using namespace llvm;
namespace {
class ClangASTNodesEmitter {
// A map from a node to each of its derived nodes.
- typedef std::multimap<Record*, Record*> ChildMap;
+ typedef std::multimap<ASTNode, ASTNode> ChildMap;
typedef ChildMap::const_iterator ChildIterator;
RecordKeeper &Records;
- Record Root;
+ ASTNode Root;
+ const std::string &NodeClassName;
const std::string &BaseSuffix;
+ std::string MacroHierarchyName;
+ ChildMap Tree;
// Create a macro-ized version of a name
static std::string macroName(std::string S) {
@@ -41,23 +48,30 @@ class ClangASTNodesEmitter {
return S;
}
+ const std::string &macroHierarchyName() {
+ assert(Root && "root node not yet derived!");
+ if (MacroHierarchyName.empty())
+ MacroHierarchyName = macroName(Root.getName());
+ return MacroHierarchyName;
+ }
+
// Return the name to be printed in the base field. Normally this is
// the record's name plus the base suffix, but if it is the root node and
// the suffix is non-empty, it's just the suffix.
- std::string baseName(Record &R) {
- if (&R == &Root && !BaseSuffix.empty())
+ std::string baseName(ASTNode node) {
+ if (node == Root && !BaseSuffix.empty())
return BaseSuffix;
- return R.getName().str() + BaseSuffix;
+ return node.getName().str() + BaseSuffix;
}
- std::pair<Record *, Record *> EmitNode (const ChildMap &Tree, raw_ostream& OS,
- Record *Base);
+ void deriveChildTree();
+
+ std::pair<ASTNode, ASTNode> EmitNode(raw_ostream& OS, ASTNode Base);
public:
explicit ClangASTNodesEmitter(RecordKeeper &R, const std::string &N,
const std::string &S)
- : Records(R), Root(N, SMLoc(), R), BaseSuffix(S)
- {}
+ : Records(R), NodeClassName(N), BaseSuffix(S) {}
// run - Output the .inc file contents
void run(raw_ostream &OS);
@@ -70,109 +84,115 @@ public:
// Returns the first and last non-abstract subrecords
// Called recursively to ensure that nodes remain contiguous
-std::pair<Record *, Record *> ClangASTNodesEmitter::EmitNode(
- const ChildMap &Tree,
- raw_ostream &OS,
- Record *Base) {
- std::string BaseName = macroName(Base->getName());
+std::pair<ASTNode, ASTNode> ClangASTNodesEmitter::EmitNode(raw_ostream &OS,
+ ASTNode Base) {
+ std::string BaseName = macroName(Base.getName());
ChildIterator i = Tree.lower_bound(Base), e = Tree.upper_bound(Base);
+ bool HasChildren = (i != e);
- Record *First = nullptr, *Last = nullptr;
- // This might be the pseudo-node for Stmt; don't assume it has an Abstract
- // bit
- if (Base->getValue("Abstract") && !Base->getValueAsBit("Abstract"))
+ ASTNode First, Last;
+ if (!Base.isAbstract())
First = Last = Base;
for (; i != e; ++i) {
- Record *R = i->second;
- bool Abstract = R->getValueAsBit("Abstract");
- std::string NodeName = macroName(R->getName());
+ ASTNode Child = i->second;
+ bool Abstract = Child.isAbstract();
+ std::string NodeName = macroName(Child.getName());
OS << "#ifndef " << NodeName << "\n";
OS << "# define " << NodeName << "(Type, Base) "
<< BaseName << "(Type, Base)\n";
OS << "#endif\n";
- if (Abstract)
- OS << "ABSTRACT_" << macroName(Root.getName()) << "(" << NodeName << "("
- << R->getName() << ", " << baseName(*Base) << "))\n";
- else
- OS << NodeName << "(" << R->getName() << ", "
- << baseName(*Base) << ")\n";
+ if (Abstract) OS << "ABSTRACT_" << macroHierarchyName() << "(";
+ OS << NodeName << "(" << Child.getName() << ", " << baseName(Base) << ")";
+ if (Abstract) OS << ")";
+ OS << "\n";
- if (Tree.find(R) != Tree.end()) {
- const std::pair<Record *, Record *> &Result
- = EmitNode(Tree, OS, R);
- if (!First && Result.first)
- First = Result.first;
- if (Result.second)
- Last = Result.second;
- } else {
- if (!Abstract) {
- Last = R;
+ auto Result = EmitNode(OS, Child);
+ assert(Result.first && Result.second && "node didn't have children?");
- if (!First)
- First = R;
- }
- }
+ // Update the range of Base.
+ if (!First) First = Result.first;
+ Last = Result.second;
OS << "#undef " << NodeName << "\n\n";
}
- if (First) {
- assert (Last && "Got a first node but not a last node for a range!");
- if (Base == &Root)
- OS << "LAST_" << macroName(Root.getName()) << "_RANGE(";
+ // If there aren't first/last nodes, it must be because there were no
+ // children and this node was abstract, which is not a sensible combination.
+ if (!First) {
+ PrintFatalError(Base.getLoc(), "abstract node has no children");
+ }
+ assert(Last && "set First without Last");
+
+ if (HasChildren) {
+ // Use FOO_RANGE unless this is the last of the ranges, in which case
+ // use LAST_FOO_RANGE.
+ if (Base == Root)
+ OS << "LAST_" << macroHierarchyName() << "_RANGE(";
else
- OS << macroName(Root.getName()) << "_RANGE(";
- OS << Base->getName() << ", " << First->getName() << ", "
- << Last->getName() << ")\n\n";
+ OS << macroHierarchyName() << "_RANGE(";
+ OS << Base.getName() << ", " << First.getName() << ", "
+ << Last.getName() << ")\n\n";
}
return std::make_pair(First, Last);
}
+void ClangASTNodesEmitter::deriveChildTree() {
+ assert(!Root && "already computed tree");
+
+ // Emit statements
+ const std::vector<Record*> Stmts
+ = Records.getAllDerivedDefinitions(NodeClassName);
+
+ for (unsigned i = 0, e = Stmts.size(); i != e; ++i) {
+ Record *R = Stmts[i];
+
+ if (auto B = R->getValueAsOptionalDef(BaseFieldName))
+ Tree.insert(std::make_pair(B, R));
+ else if (Root)
+ PrintFatalError(R->getLoc(),
+ Twine("multiple root nodes in \"") + NodeClassName
+ + "\" hierarchy");
+ else
+ Root = R;
+ }
+
+ if (!Root)
+ PrintFatalError(Twine("didn't find root node in \"") + NodeClassName
+ + "\" hierarchy");
+}
+
void ClangASTNodesEmitter::run(raw_ostream &OS) {
+ deriveChildTree();
+
emitSourceFileHeader("List of AST nodes of a particular kind", OS);
// Write the preamble
- OS << "#ifndef ABSTRACT_" << macroName(Root.getName()) << "\n";
- OS << "# define ABSTRACT_" << macroName(Root.getName()) << "(Type) Type\n";
+ OS << "#ifndef ABSTRACT_" << macroHierarchyName() << "\n";
+ OS << "# define ABSTRACT_" << macroHierarchyName() << "(Type) Type\n";
OS << "#endif\n";
- OS << "#ifndef " << macroName(Root.getName()) << "_RANGE\n";
+ OS << "#ifndef " << macroHierarchyName() << "_RANGE\n";
OS << "# define "
- << macroName(Root.getName()) << "_RANGE(Base, First, Last)\n";
+ << macroHierarchyName() << "_RANGE(Base, First, Last)\n";
OS << "#endif\n\n";
- OS << "#ifndef LAST_" << macroName(Root.getName()) << "_RANGE\n";
+ OS << "#ifndef LAST_" << macroHierarchyName() << "_RANGE\n";
OS << "# define LAST_"
- << macroName(Root.getName()) << "_RANGE(Base, First, Last) "
- << macroName(Root.getName()) << "_RANGE(Base, First, Last)\n";
+ << macroHierarchyName() << "_RANGE(Base, First, Last) "
+ << macroHierarchyName() << "_RANGE(Base, First, Last)\n";
OS << "#endif\n\n";
-
- // Emit statements
- const std::vector<Record*> Stmts
- = Records.getAllDerivedDefinitions(Root.getName());
-
- ChildMap Tree;
-
- for (unsigned i = 0, e = Stmts.size(); i != e; ++i) {
- Record *R = Stmts[i];
-
- if (R->getValue("Base"))
- Tree.insert(std::make_pair(R->getValueAsDef("Base"), R));
- else
- Tree.insert(std::make_pair(&Root, R));
- }
- EmitNode(Tree, OS, &Root);
+ EmitNode(OS, Root);
- OS << "#undef " << macroName(Root.getName()) << "\n";
- OS << "#undef " << macroName(Root.getName()) << "_RANGE\n";
- OS << "#undef LAST_" << macroName(Root.getName()) << "_RANGE\n";
- OS << "#undef ABSTRACT_" << macroName(Root.getName()) << "\n";
+ OS << "#undef " << macroHierarchyName() << "\n";
+ OS << "#undef " << macroHierarchyName() << "_RANGE\n";
+ OS << "#undef LAST_" << macroHierarchyName() << "_RANGE\n";
+ OS << "#undef ABSTRACT_" << macroHierarchyName() << "\n";
}
void clang::EmitClangASTNodes(RecordKeeper &RK, raw_ostream &OS,
@@ -199,15 +219,14 @@ void clang::EmitClangDeclContext(RecordKeeper &Records, raw_ostream &OS) {
typedef std::vector<Record*> RecordVector;
RecordVector DeclContextsVector
- = Records.getAllDerivedDefinitions("DeclContext");
- RecordVector Decls = Records.getAllDerivedDefinitions("Decl");
+ = Records.getAllDerivedDefinitions(DeclContextNodeClassName);
+ RecordVector Decls = Records.getAllDerivedDefinitions(DeclNodeClassName);
RecordSet DeclContexts (DeclContextsVector.begin(), DeclContextsVector.end());
for (RecordVector::iterator i = Decls.begin(), e = Decls.end(); i != e; ++i) {
Record *R = *i;
- if (R->getValue("Base")) {
- Record *B = R->getValueAsDef("Base");
+ if (Record *B = R->getValueAsOptionalDef(BaseFieldName)) {
if (DeclContexts.find(B) != DeclContexts.end()) {
OS << "DECL_CONTEXT_BASE(" << B->getName() << ")\n";
DeclContexts.erase(B);
diff --git a/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp b/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp
new file mode 100644
index 000000000000..256ca42e9aab
--- /dev/null
+++ b/clang/utils/TableGen/ClangASTPropertiesEmitter.cpp
@@ -0,0 +1,867 @@
+//=== 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 <cctype>
+#include <map>
+#include <set>
+#include <string>
+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 <class NodeClass>
+ static ReaderWriterInfo forReader() {
+ return ReaderWriterInfo{
+ true,
+ NodeClass::getASTHierarchyName(),
+ "Reader",
+ "read",
+ "R",
+ getReaderResultType(NodeClass())
+ };
+ }
+
+ template <class NodeClass>
+ static ReaderWriterInfo forWriter() {
+ return ReaderWriterInfo{
+ false,
+ NodeClass::getASTHierarchyName(),
+ "Writer",
+ "write",
+ "W",
+ "void"
+ };
+ }
+};
+
+struct NodeInfo {
+ std::vector<Property> Properties;
+ CreationRule Creator = nullptr;
+ OverrideRule Override = nullptr;
+ ReadHelperRule ReadHelper = nullptr;
+};
+
+struct CasedTypeInfo {
+ TypeKindRule KindRule;
+ std::vector<TypeCase> Cases;
+};
+
+class ASTPropsEmitter {
+ raw_ostream &Out;
+ RecordKeeper &Records;
+ std::map<HasProperties, NodeInfo> NodeInfos;
+ std::vector<PropertyType> AllPropertyTypes;
+ std::map<PropertyType, CasedTypeInfo> CasedTypeInfos;
+
+public:
+ ASTPropsEmitter(RecordKeeper &records, raw_ostream &out)
+ : Out(out), Records(records) {
+
+ // Find all the properties.
+ for (Property property :
+ records.getAllDerivedDefinitions(PropertyClassName)) {
+ HasProperties node = property.getClass();
+ NodeInfos[node].Properties.push_back(property);
+ }
+
+ // Find all the creation rules.
+ for (CreationRule creationRule :
+ records.getAllDerivedDefinitions(CreationRuleClassName)) {
+ HasProperties 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)) {
+ HasProperties node = overrideRule.getClass();
+
+ auto &info = NodeInfos[node];
+ if (info.Override) {
+ PrintFatalError(overrideRule.getLoc(),
+ "multiple override rules for \"" + node.getName()
+ + "\"");
+ }
+ info.Override = overrideRule;
+ }
+
+ // Find all the write helper rules.
+ for (ReadHelperRule helperRule :
+ records.getAllDerivedDefinitions(ReadHelperRuleClassName)) {
+ HasProperties node = helperRule.getClass();
+
+ auto &info = NodeInfos[node];
+ if (info.ReadHelper) {
+ PrintFatalError(helperRule.getLoc(),
+ "multiple write helper rules for \"" + node.getName()
+ + "\"");
+ }
+ info.ReadHelper = helperRule;
+ }
+
+ // Find all the concrete property types.
+ for (PropertyType type :
+ records.getAllDerivedDefinitions(PropertyTypeClassName)) {
+ // Ignore generic specializations; they're generally not useful when
+ // emitting basic emitters etc.
+ if (type.isGenericSpecialization()) continue;
+
+ AllPropertyTypes.push_back(type);
+ }
+
+ // Find all the type kind rules.
+ for (TypeKindRule kindRule :
+ records.getAllDerivedDefinitions(TypeKindClassName)) {
+ PropertyType type = kindRule.getParentType();
+ auto &info = CasedTypeInfos[type];
+ if (info.KindRule) {
+ PrintFatalError(kindRule.getLoc(),
+ "multiple kind rules for \""
+ + type.getCXXTypeName() + "\"");
+ }
+ info.KindRule = kindRule;
+ }
+
+ // Find all the type cases.
+ for (TypeCase typeCase :
+ records.getAllDerivedDefinitions(TypeCaseClassName)) {
+ CasedTypeInfos[typeCase.getParentType()].Cases.push_back(typeCase);
+ }
+
+ Validator(*this).validate();
+ }
+
+ void visitAllProperties(HasProperties derived, const NodeInfo &derivedInfo,
+ function_ref<void (Property)> visit) {
+ std::set<StringRef> ignoredProperties;
+
+ auto overrideRule = derivedInfo.Override;
+ if (overrideRule) {
+ auto list = overrideRule.getIgnoredProperties();
+ ignoredProperties.insert(list.begin(), list.end());
+ }
+
+ // TODO: we should sort the properties in various ways
+ // - put arrays at the end to enable abbreviations
+ // - put conditional properties after properties used in the condition
+
+ visitAllNodesWithInfo(derived, derivedInfo,
+ [&](HasProperties node, const NodeInfo &info) {
+ for (Property prop : info.Properties) {
+ if (ignoredProperties.count(prop.getName()))
+ continue;
+
+ visit(prop);
+ }
+ });
+ }
+
+ void visitAllNodesWithInfo(HasProperties derivedNode,
+ const NodeInfo &derivedNodeInfo,
+ llvm::function_ref<void (HasProperties node,
+ const NodeInfo &info)>
+ visit) {
+ visit(derivedNode, derivedNodeInfo);
+
+ // Also walk the bases if appropriate.
+ if (ASTNode base = derivedNode.getAs<ASTNode>()) {
+ for (base = base.getBase(); base; base = base.getBase()) {
+ auto it = NodeInfos.find(base);
+
+ // Ignore intermediate nodes that don't add interesting properties.
+ if (it == NodeInfos.end()) continue;
+ auto &baseInfo = it->second;
+
+ visit(base, baseInfo);
+ }
+ }
+ }
+
+ template <class NodeClass>
+ void emitNodeReaderClass() {
+ auto info = ReaderWriterInfo::forReader<NodeClass>();
+ emitNodeReaderWriterClass<NodeClass>(info);
+ }
+
+ template <class NodeClass>
+ void emitNodeWriterClass() {
+ auto info = ReaderWriterInfo::forWriter<NodeClass>();
+ emitNodeReaderWriterClass<NodeClass>(info);
+ }
+
+ template <class NodeClass>
+ void emitNodeReaderWriterClass(const ReaderWriterInfo &info);
+
+ template <class NodeClass>
+ void emitNodeReaderWriterMethod(NodeClass node,
+ const ReaderWriterInfo &info);
+
+ void emitPropertiedReaderWriterBody(HasProperties node,
+ const ReaderWriterInfo &info);
+
+ void emitReadOfProperty(StringRef readerName, Property property);
+ void emitReadOfProperty(StringRef readerName, StringRef name,
+ PropertyType type, StringRef condition = "");
+
+ void emitWriteOfProperty(StringRef writerName, Property property);
+ void emitWriteOfProperty(StringRef writerName, StringRef name,
+ PropertyType type, StringRef readCode,
+ StringRef condition = "");
+
+ void emitBasicReaderWriterFile(const ReaderWriterInfo &info);
+ void emitDispatcherTemplate(const ReaderWriterInfo &info);
+ void emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info);
+ void emitBasicReaderWriterTemplate(const ReaderWriterInfo &info);
+
+ void emitCasedReaderWriterMethodBody(PropertyType type,
+ const CasedTypeInfo &typeCases,
+ const ReaderWriterInfo &info);
+
+private:
+ class Validator {
+ ASTPropsEmitter &Emitter;
+ std::set<HasProperties> ValidatedNodes;
+
+ public:
+ Validator(ASTPropsEmitter &emitter) : Emitter(emitter) {}
+ void validate();
+
+ private:
+ void validateNode(HasProperties 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(HasProperties derivedNode,
+ const NodeInfo &derivedNodeInfo) {
+ if (!ValidatedNodes.insert(derivedNode).second) return;
+
+ // A map from property name to property.
+ std::map<StringRef, Property> allProperties;
+
+ Emitter.visitAllNodesWithInfo(derivedNode, derivedNodeInfo,
+ [&](HasProperties node,
+ const NodeInfo &nodeInfo) {
+ for (Property property : nodeInfo.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 " + derivedNode.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 <class NodeClass>
+void ASTPropsEmitter::emitNodeReaderWriterClass(const ReaderWriterInfo &info) {
+ StringRef suffix = info.ClassSuffix;
+ StringRef var = info.HelperVariable;
+
+ // Enter the class declaration.
+ Out << "template <class Property" << suffix << ">\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<NodeClass>(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<const " << node.getClassName()
+ << " *>(node)";
+ Out << ");\n";
+ });
+ Out << " }\n"
+ " llvm_unreachable(\"bad kind\");\n"
+ " }\n\n";
+
+ // Emit node-specific methods for all the concrete nodes.
+ visitASTNodeHierarchy<NodeClass>(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 <class NodeClass>
+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";
+
+ emitPropertiedReaderWriterBody(node, info);
+
+ // Finish the method declaration.
+ Out << " }\n\n";
+}
+
+void ASTPropsEmitter::emitPropertiedReaderWriterBody(HasProperties node,
+ const ReaderWriterInfo &info) {
+ // 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 the ReadHelper code, if present.
+ if (!info.IsReader && nodeInfo.ReadHelper) {
+ Out << " " << nodeInfo.ReadHelper.getHelperCode() << "\n";
+ }
+
+ // 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(info.HelperVariable, prop);
+ else
+ emitWriteOfProperty(info.HelperVariable, prop);
+ });
+
+ // Emit the final creation code.
+ if (info.IsReader)
+ Out << " " << creationCode << "\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(StringRef readerName,
+ Property property) {
+ emitReadOfProperty(readerName, property.getName(), property.getType(),
+ property.getCondition());
+}
+
+void ASTPropsEmitter::emitReadOfProperty(StringRef readerName,
+ StringRef name,
+ PropertyType type,
+ StringRef condition) {
+ // 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 << " ";
+ if (!condition.empty()) Out << "llvm::Optional<";
+ type.emitCXXValueTypeName(true, Out);
+ if (!condition.empty()) Out << ">";
+ Out << " " << name;
+
+ if (condition.empty()) {
+ Out << " = ";
+ } else {
+ Out << ";\n"
+ " if (" << condition << ") {\n"
+ " " << name << ".emplace(";
+ }
+
+ Out << readerName << ".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 << ")";
+
+ if (condition.empty()) {
+ Out << ";\n";
+ } else {
+ Out << ");\n"
+ " }\n";
+ }
+}
+
+/// Emit code to write the given property in a node-writer method.
+void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
+ Property property) {
+ emitWriteOfProperty(writerName, property.getName(), property.getType(),
+ property.getReadCode(), property.getCondition());
+}
+
+void ASTPropsEmitter::emitWriteOfProperty(StringRef writerName,
+ StringRef name,
+ PropertyType type,
+ StringRef readCode,
+ StringRef condition) {
+ if (!condition.empty()) {
+ Out << " if (" << condition << ") {\n";
+ }
+
+ // Focus down to the property:
+ // T prop = <READ>;
+ // W.find("prop").write##ValueType(prop);
+ Out << " ";
+ type.emitCXXValueTypeName(false, Out);
+ Out << " " << name << " = (" << readCode << ");\n"
+ " " << writerName << ".find(\"" << name << "\").write";
+ emitBasicReaderWriterMethodSuffix(Out, type, /*for read*/ false);
+ Out << "(" << name << ");\n";
+
+ if (!condition.empty()) {
+ Out << " }\n";
+ }
+}
+
+/// Emit an .inc file that defines the AbstractFooReader class
+/// for the given AST class hierarchy.
+template <class NodeClass>
+static void emitASTReader(RecordKeeper &records, raw_ostream &out,
+ StringRef description) {
+ emitSourceFileHeader(description, out);
+
+ ASTPropsEmitter(records, out).emitNodeReaderClass<NodeClass>();
+}
+
+void clang::EmitClangTypeReader(RecordKeeper &records, raw_ostream &out) {
+ emitASTReader<TypeNode>(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 <class NodeClass>
+static void emitASTWriter(RecordKeeper &records, raw_ostream &out,
+ StringRef description) {
+ emitSourceFileHeader(description, out);
+
+ ASTPropsEmitter(records, out).emitNodeWriterClass<NodeClass>();
+}
+
+void clang::EmitClangTypeWriter(RecordKeeper &records, raw_ostream &out) {
+ emitASTWriter<TypeNode>(records, out, "A CRTP writer for Clang Type nodes");
+}
+
+/****************************************************************************/
+/*************************** BASIC READER/WRITERS ***************************/
+/****************************************************************************/
+
+void
+ASTPropsEmitter::emitDispatcherTemplate(const ReaderWriterInfo &info) {
+ // Declare the {Read,Write}Dispatcher template.
+ StringRef dispatcherPrefix = (info.IsReader ? "Read" : "Write");
+ Out << "template <class ValueType>\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 <class Basic" << info.ClassSuffix << ", class... Args>\n"
+ " static " << (info.IsReader ? cxxTypeName : "void") << " "
+ << info.MethodPrefix
+ << "(Basic" << info.ClassSuffix << " &" << var
+ << ", Args &&... args) {\n"
+ " return " << var << "."
+ << info.MethodPrefix << methodSuffix
+ << "(std::forward<Args>(args)...);\n"
+ " }\n"
+ "};\n";
+ };
+
+ // Declare explicit specializations for each of the concrete types.
+ for (PropertyType type : AllPropertyTypes) {
+ 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("<class T>",
+ "llvm::ArrayRef<T>",
+ "Array");
+ declareSpecialization("<class T>",
+ "llvm::Optional<T>",
+ "Optional");
+ Out << "\n";
+}
+
+void
+ASTPropsEmitter::emitPackUnpackOptionalTemplate(const ReaderWriterInfo &info) {
+ StringRef classPrefix = (info.IsReader ? "Unpack" : "Pack");
+ StringRef methodName = (info.IsReader ? "unpack" : "pack");
+
+ // Declare the {Pack,Unpack}OptionalValue template.
+ Out << "template <class ValueType>\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 : AllPropertyTypes) {
+ 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";
+}
+
+void
+ASTPropsEmitter::emitBasicReaderWriterTemplate(const ReaderWriterInfo &info) {
+ // Emit the Basic{Reader,Writer}Base template.
+ Out << "template <class Impl>\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<Impl&>(*this); }\n";
+
+ auto enterReaderWriterMethod = [&](StringRef cxxTypeName,
+ StringRef abstractTypeName,
+ bool shouldPassByReference,
+ bool constWhenWriting,
+ StringRef paramName) {
+ Out << " " << (info.IsReader ? cxxTypeName : "void")
+ << " " << info.MethodPrefix << abstractTypeName << "(";
+ if (!info.IsReader)
+ Out << (shouldPassByReference || constWhenWriting ? "const " : "")
+ << cxxTypeName
+ << (shouldPassByReference ? " &" : "") << " " << paramName;
+ 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 : AllPropertyTypes) {
+ auto enterMethod = [&](StringRef paramName) {
+ enterReaderWriterMethod(type.getCXXTypeName(),
+ type.getAbstractTypeName(),
+ type.shouldPassByReference(),
+ type.isConstWhenWriting(),
+ paramName);
+ };
+ auto exitMethod = [&] {
+ Out << " }\n";
+ };
+
+ // Handled cased types.
+ auto casedIter = CasedTypeInfos.find(type);
+ if (casedIter != CasedTypeInfos.end()) {
+ enterMethod("node");
+ emitCasedReaderWriterMethodBody(type, casedIter->second, info);
+ exitMethod();
+
+ } else if (type.isEnum()) {
+ enterMethod("value");
+ if (info.IsReader)
+ Out << " return asImpl().template readEnum<"
+ << type.getCXXTypeName() << ">();\n";
+ else
+ Out << " asImpl().writeEnum(value);\n";
+ exitMethod();
+
+ } else if (PropertyType superclass = type.getSuperclassType()) {
+ enterMethod("value");
+ if (info.IsReader)
+ Out << " return cast_or_null<" << type.getSubclassClassName()
+ << ">(asImpl().read"
+ << superclass.getAbstractTypeName()
+ << "());\n";
+ else
+ Out << " asImpl().write" << superclass.getAbstractTypeName()
+ << "(value);\n";
+ exitMethod();
+
+ } else {
+ // The other types can't be handled as trivially.
+ }
+ }
+ Out << "};\n\n";
+}
+
+void ASTPropsEmitter::emitCasedReaderWriterMethodBody(PropertyType type,
+ const CasedTypeInfo &typeCases,
+ const ReaderWriterInfo &info) {
+ if (typeCases.Cases.empty()) {
+ assert(typeCases.KindRule);
+ PrintFatalError(typeCases.KindRule.getLoc(),
+ "no cases found for \"" + type.getCXXTypeName() + "\"");
+ }
+ if (!typeCases.KindRule) {
+ assert(!typeCases.Cases.empty());
+ PrintFatalError(typeCases.Cases.front().getLoc(),
+ "no kind rule for \"" + type.getCXXTypeName() + "\"");
+ }
+
+ auto var = info.HelperVariable;
+ std::string subvar = ("sub" + var).str();
+
+ // Bind `ctx` for readers.
+ if (info.IsReader)
+ Out << " auto &ctx = asImpl().getASTContext();\n";
+
+ // Start an object.
+ Out << " auto &&" << subvar << " = asImpl()."
+ << info.MethodPrefix << "Object();\n";
+
+ // Read/write the kind property;
+ TypeKindRule kindRule = typeCases.KindRule;
+ StringRef kindProperty = kindRule.getKindPropertyName();
+ PropertyType kindType = kindRule.getKindType();
+ if (info.IsReader) {
+ emitReadOfProperty(subvar, kindProperty, kindType);
+ } else {
+ // Write the property. Note that this will implicitly read the
+ // kind into a local variable with the right name.
+ emitWriteOfProperty(subvar, kindProperty, kindType,
+ kindRule.getReadCode());
+ }
+
+ // Prepare a ReaderWriterInfo with a helper variable that will use
+ // the sub-reader/writer.
+ ReaderWriterInfo subInfo = info;
+ subInfo.HelperVariable = subvar;
+
+ // Switch on the kind.
+ Out << " switch (" << kindProperty << ") {\n";
+ for (TypeCase typeCase : typeCases.Cases) {
+ Out << " case " << type.getCXXTypeName() << "::"
+ << typeCase.getCaseName() << ": {\n";
+ emitPropertiedReaderWriterBody(typeCase, subInfo);
+ if (!info.IsReader)
+ Out << " return;\n";
+ Out << " }\n\n";
+ }
+ Out << " }\n"
+ " llvm_unreachable(\"bad " << kindType.getCXXTypeName()
+ << "\");\n";
+}
+
+void ASTPropsEmitter::emitBasicReaderWriterFile(const ReaderWriterInfo &info) {
+ emitDispatcherTemplate(info);
+ emitPackUnpackOptionalTemplate(info);
+ emitBasicReaderWriterTemplate(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<TypeNode>();
+ ASTPropsEmitter(records, out).emitBasicReaderWriterFile(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<TypeNode>();
+ ASTPropsEmitter(records, out).emitBasicReaderWriterFile(info);
+}
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index 0d92a321c747..4c3742c8e339 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "TableGenBackends.h"
+#include "ASTTableGen.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
@@ -101,9 +102,9 @@ static std::string ReadPCHRecord(StringRef type) {
return StringSwitch<std::string>(type)
.EndsWith("Decl *", "Record.GetLocalDeclAs<"
+ std::string(type, 0, type.size()-1) + ">(Record.readInt())")
- .Case("TypeSourceInfo *", "Record.getTypeSourceInfo()")
+ .Case("TypeSourceInfo *", "Record.readTypeSourceInfo()")
.Case("Expr *", "Record.readExpr()")
- .Case("IdentifierInfo *", "Record.getIdentifierInfo()")
+ .Case("IdentifierInfo *", "Record.readIdentifier()")
.Case("StringRef", "Record.readString()")
.Case("ParamIdx", "ParamIdx::deserialize(Record.readInt())")
.Default("Record.readInt()");
@@ -584,7 +585,7 @@ namespace {
OS << " " << getLowerName() << "Ptr = Record.readExpr();\n";
OS << " else\n";
OS << " " << getLowerName()
- << "Ptr = Record.getTypeSourceInfo();\n";
+ << "Ptr = Record.readTypeSourceInfo();\n";
}
void writePCHWrite(raw_ostream &OS) const override {
@@ -1808,7 +1809,7 @@ struct PragmaClangAttributeSupport {
} // end anonymous namespace
static bool doesDeclDeriveFrom(const Record *D, const Record *Base) {
- const Record *CurrentBase = D->getValueAsDef("Base");
+ const Record *CurrentBase = D->getValueAsOptionalDef(BaseFieldName);
if (!CurrentBase)
return false;
if (CurrentBase == Base)
@@ -1849,7 +1850,8 @@ PragmaClangAttributeSupport::PragmaClangAttributeSupport(
std::vector<Record *> Aggregates =
Records.getAllDerivedDefinitions("AttrSubjectMatcherAggregateRule");
- std::vector<Record *> DeclNodes = Records.getAllDerivedDefinitions("DDecl");
+ std::vector<Record *> DeclNodes =
+ Records.getAllDerivedDefinitions(DeclNodeClassName);
for (const auto *Aggregate : Aggregates) {
Record *SubjectDecl = Aggregate->getValueAsDef("Subject");
@@ -3303,9 +3305,8 @@ static std::string GetDiagnosticSpelling(const Record &R) {
// If we couldn't find the DiagSpelling in this object, we can check to see
// if the object is one that has a base, and if it is, loop up to the Base
// member recursively.
- std::string Super = R.getSuperClasses().back().first->getName();
- if (Super == "DDecl" || Super == "DStmt")
- return GetDiagnosticSpelling(*R.getValueAsDef("Base"));
+ if (auto Base = R.getValueAsOptionalDef(BaseFieldName))
+ return GetDiagnosticSpelling(*Base);
return "";
}
@@ -3385,7 +3386,8 @@ static std::string GenerateCustomAppertainsTo(const Record &Subject,
if (I != CustomSubjectSet.end())
return *I;
- Record *Base = Subject.getValueAsDef("Base");
+ // This only works with non-root Decls.
+ Record *Base = Subject.getValueAsDef(BaseFieldName);
// Not currently support custom subjects within custom subjects.
if (Base->isSubClassOf("SubsetSubject")) {
diff --git a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
index 778375010041..f694c3e4380a 100644
--- a/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
+++ b/clang/utils/TableGen/ClangDiagnosticsEmitter.cpp
@@ -134,7 +134,7 @@ namespace {
const Record *ExplicitDef;
- GroupInfo() : ExplicitDef(nullptr) {}
+ GroupInfo() : IDNo(0), ExplicitDef(nullptr) {}
};
} // end anonymous namespace.
@@ -554,7 +554,7 @@ public:
ModifierType ModKind;
std::vector<Piece *> Options;
- int Index;
+ int Index = 0;
static bool classof(const Piece *P) {
return P->getPieceClass() == SelectPieceClass ||
@@ -566,7 +566,7 @@ struct PluralPiece : SelectPiece {
PluralPiece() : SelectPiece(PluralPieceClass, MT_Plural) {}
std::vector<Piece *> OptionPrefixes;
- int Index;
+ int Index = 0;
static bool classof(const Piece *P) {
return P->getPieceClass() == PluralPieceClass;
diff --git a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
index c8975d7bf615..41d33b550680 100644
--- a/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
+++ b/clang/utils/TableGen/ClangOpenCLBuiltinEmitter.cpp
@@ -26,6 +26,11 @@
//
// * Structs and enums to represent types and function signatures.
//
+// * const char *FunctionExtensionTable[]
+// List of space-separated OpenCL extensions. A builtin references an
+// entry in this table when the builtin requires a particular (set of)
+// extension(s) to be enabled.
+//
// * OpenCLTypeStruct TypeTable[]
// Type information for return types and arguments.
//
@@ -69,6 +74,13 @@
using namespace llvm;
namespace {
+
+// A list of signatures that are shared by one or more builtin functions.
+struct BuiltinTableEntries {
+ SmallVector<StringRef, 4> Names;
+ std::vector<std::pair<const Record *, unsigned>> Signatures;
+};
+
class BuiltinNameEmitter {
public:
BuiltinNameEmitter(RecordKeeper &Records, raw_ostream &OS)
@@ -79,6 +91,9 @@ public:
void Emit();
private:
+ // A list of indices into the builtin function table.
+ using BuiltinIndexListTy = SmallVector<unsigned, 11>;
+
// Contains OpenCL builtin functions and related information, stored as
// Record instances. They are coming from the associated TableGen file.
RecordKeeper &Records;
@@ -106,6 +121,26 @@ private:
// FctOverloadMap and TypeMap.
void GetOverloads();
+ // Compare two lists of signatures and check that e.g. the OpenCL version,
+ // function attributes, and extension are equal for each signature.
+ // \param Candidate (in) Entry in the SignatureListMap to check.
+ // \param SignatureList (in) List of signatures of the considered function.
+ // \returns true if the two lists of signatures are identical.
+ bool CanReuseSignature(
+ BuiltinIndexListTy *Candidate,
+ std::vector<std::pair<const Record *, unsigned>> &SignatureList);
+
+ // Group functions with the same list of signatures by populating the
+ // SignatureListMap.
+ // Some builtin functions have the same list of signatures, for example the
+ // "sin" and "cos" functions. To save space in the BuiltinTable, the
+ // "isOpenCLBuiltin" function will have the same output for these two
+ // function names.
+ void GroupBySignature();
+
+ // Emit the FunctionExtensionTable that lists all function extensions.
+ void EmitExtensionTable();
+
// Emit the TypeTable containing all types used by OpenCL builtins.
void EmitTypeTable();
@@ -123,12 +158,13 @@ private:
// each function, and is a struct OpenCLBuiltinDecl.
// E.g.:
// // 891 convert_float2_rtn
- // { 58, 2, 100, 0 },
+ // { 58, 2, 3, 100, 0 },
// This means that the signature of this convert_float2_rtn overload has
// 1 argument (+1 for the return type), stored at index 58 in
- // the SignatureTable. The last two values represent the minimum (1.0) and
- // maximum (0, meaning no max version) OpenCL version in which this overload
- // is supported.
+ // the SignatureTable. This prototype requires extension "3" in the
+ // FunctionExtensionTable. The last two values represent the minimum (1.0)
+ // and maximum (0, meaning no max version) OpenCL version in which this
+ // overload is supported.
void EmitBuiltinTable();
// Emit a StringMatcher function to check whether a function name is an
@@ -164,12 +200,34 @@ private:
// Contains the map of OpenCL types to their index in the TypeTable.
MapVector<const Record *, unsigned> TypeMap;
+ // List of OpenCL function extensions mapping extension strings to
+ // an index into the FunctionExtensionTable.
+ StringMap<unsigned> FunctionExtensionIndex;
+
// List of OpenCL type names in the same order as in enum OpenCLTypeID.
// This list does not contain generic types.
std::vector<const Record *> TypeList;
// Same as TypeList, but for generic types only.
std::vector<const Record *> GenTypeList;
+
+ // Map an ordered vector of signatures to their original Record instances,
+ // and to a list of function names that share these signatures.
+ //
+ // For example, suppose the "cos" and "sin" functions have only three
+ // signatures, and these signatures are at index Ix in the SignatureTable:
+ // cos | sin | Signature | Index
+ // float cos(float) | float sin(float) | Signature1 | I1
+ // double cos(double) | double sin(double) | Signature2 | I2
+ // half cos(half) | half sin(half) | Signature3 | I3
+ //
+ // Then we will create a mapping of the vector of signatures:
+ // SignatureListMap[<I1, I2, I3>] = <
+ // <"cos", "sin">,
+ // <Signature1, Signature2, Signature3>>
+ // The function "tan", having the same signatures, would be mapped to the
+ // same entry (<I1, I2, I3>).
+ MapVector<BuiltinIndexListTy *, BuiltinTableEntries> SignatureListMap;
};
} // namespace
@@ -182,15 +240,18 @@ void BuiltinNameEmitter::Emit() {
// Emit enums and structs.
EmitDeclarations();
+ // Parse the Records to populate the internal lists.
GetOverloads();
+ GroupBySignature();
// Emit tables.
+ EmitExtensionTable();
EmitTypeTable();
EmitSignatureTable();
EmitBuiltinTable();
+ // Emit functions.
EmitStringMatcher();
-
EmitQualTypeFinder();
}
@@ -271,6 +332,14 @@ struct OpenCLBuiltinStruct {
// the SignatureTable represent the complete signature. The first type at
// index SigTableIndex is the return type.
const unsigned NumTypes;
+ // Function attribute __attribute__((pure))
+ const bool IsPure;
+ // Function attribute __attribute__((const))
+ const bool IsConst;
+ // Function attribute __attribute__((convergent))
+ const bool IsConv;
+ // OpenCL extension(s) required for this overload.
+ const unsigned short Extension;
// First OpenCL version in which this overload was introduced (e.g. CL20).
const unsigned short MinVersion;
// First OpenCL version in which this overload was removed (e.g. CL20).
@@ -361,6 +430,23 @@ void BuiltinNameEmitter::GetOverloads() {
}
}
+void BuiltinNameEmitter::EmitExtensionTable() {
+ OS << "static const char *FunctionExtensionTable[] = {\n";
+ unsigned Index = 0;
+ std::vector<Record *> FuncExtensions =
+ Records.getAllDerivedDefinitions("FunctionExtension");
+
+ for (const auto &FE : FuncExtensions) {
+ // Emit OpenCL extension table entry.
+ OS << " // " << Index << ": " << FE->getName() << "\n"
+ << " \"" << FE->getValueAsString("ExtName") << "\",\n";
+
+ // Record index of this extension.
+ FunctionExtensionIndex[FE->getName()] = Index++;
+ }
+ OS << "};\n\n";
+}
+
void BuiltinNameEmitter::EmitTypeTable() {
OS << "static const OpenCLTypeStruct TypeTable[] = {\n";
for (const auto &T : TypeMap) {
@@ -402,13 +488,22 @@ void BuiltinNameEmitter::EmitBuiltinTable() {
unsigned Index = 0;
OS << "static const OpenCLBuiltinStruct BuiltinTable[] = {\n";
- for (const auto &FOM : FctOverloadMap) {
+ for (const auto &SLM : SignatureListMap) {
- OS << " // " << (Index + 1) << ": " << FOM.first << "\n";
+ OS << " // " << (Index + 1) << ": ";
+ for (const auto &Name : SLM.second.Names) {
+ OS << Name << ", ";
+ }
+ OS << "\n";
- for (const auto &Overload : FOM.second) {
+ for (const auto &Overload : SLM.second.Signatures) {
+ StringRef ExtName = Overload.first->getValueAsDef("Extension")->getName();
OS << " { " << Overload.second << ", "
<< Overload.first->getValueAsListOfDefs("Signature").size() << ", "
+ << (Overload.first->getValueAsBit("IsPure")) << ", "
+ << (Overload.first->getValueAsBit("IsConst")) << ", "
+ << (Overload.first->getValueAsBit("IsConv")) << ", "
+ << FunctionExtensionIndex[ExtName] << ", "
<< Overload.first->getValueAsDef("MinVersion")->getValueAsInt("ID")
<< ", "
<< Overload.first->getValueAsDef("MaxVersion")->getValueAsInt("ID")
@@ -419,19 +514,92 @@ void BuiltinNameEmitter::EmitBuiltinTable() {
OS << "};\n\n";
}
+bool BuiltinNameEmitter::CanReuseSignature(
+ BuiltinIndexListTy *Candidate,
+ std::vector<std::pair<const Record *, unsigned>> &SignatureList) {
+ assert(Candidate->size() == SignatureList.size() &&
+ "signature lists should have the same size");
+
+ auto &CandidateSigs =
+ SignatureListMap.find(Candidate)->second.Signatures;
+ for (unsigned Index = 0; Index < Candidate->size(); Index++) {
+ const Record *Rec = SignatureList[Index].first;
+ const Record *Rec2 = CandidateSigs[Index].first;
+ if (Rec->getValueAsBit("IsPure") == Rec2->getValueAsBit("IsPure") &&
+ Rec->getValueAsBit("IsConst") == Rec2->getValueAsBit("IsConst") &&
+ Rec->getValueAsBit("IsConv") == Rec2->getValueAsBit("IsConv") &&
+ Rec->getValueAsDef("MinVersion")->getValueAsInt("ID") ==
+ Rec2->getValueAsDef("MinVersion")->getValueAsInt("ID") &&
+ Rec->getValueAsDef("MaxVersion")->getValueAsInt("ID") ==
+ Rec2->getValueAsDef("MaxVersion")->getValueAsInt("ID") &&
+ Rec->getValueAsDef("Extension")->getName() ==
+ Rec2->getValueAsDef("Extension")->getName()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void BuiltinNameEmitter::GroupBySignature() {
+ // List of signatures known to be emitted.
+ std::vector<BuiltinIndexListTy *> KnownSignatures;
+
+ for (auto &Fct : FctOverloadMap) {
+ bool FoundReusableSig = false;
+
+ // Gather all signatures for the current function.
+ auto *CurSignatureList = new BuiltinIndexListTy();
+ for (const auto &Signature : Fct.second) {
+ CurSignatureList->push_back(Signature.second);
+ }
+ // Sort the list to facilitate future comparisons.
+ std::sort(CurSignatureList->begin(), CurSignatureList->end());
+
+ // Check if we have already seen another function with the same list of
+ // signatures. If so, just add the name of the function.
+ for (auto *Candidate : KnownSignatures) {
+ if (Candidate->size() == CurSignatureList->size() &&
+ *Candidate == *CurSignatureList) {
+ if (CanReuseSignature(Candidate, Fct.second)) {
+ SignatureListMap.find(Candidate)->second.Names.push_back(Fct.first);
+ FoundReusableSig = true;
+ }
+ }
+ }
+
+ if (FoundReusableSig) {
+ delete CurSignatureList;
+ } else {
+ // Add a new entry.
+ SignatureListMap[CurSignatureList] = {
+ SmallVector<StringRef, 4>(1, Fct.first), Fct.second};
+ KnownSignatures.push_back(CurSignatureList);
+ }
+ }
+
+ for (auto *I : KnownSignatures) {
+ delete I;
+ }
+}
+
void BuiltinNameEmitter::EmitStringMatcher() {
std::vector<StringMatcher::StringPair> ValidBuiltins;
unsigned CumulativeIndex = 1;
- for (auto &i : FctOverloadMap) {
- auto &Ov = i.second;
- std::string RetStmt;
- raw_string_ostream SS(RetStmt);
- SS << "return std::make_pair(" << CumulativeIndex << ", " << Ov.size()
- << ");";
- SS.flush();
- CumulativeIndex += Ov.size();
- ValidBuiltins.push_back(StringMatcher::StringPair(i.first, RetStmt));
+ for (const auto &SLM : SignatureListMap) {
+ const auto &Ovl = SLM.second.Signatures;
+
+ // A single signature list may be used by different builtins. Return the
+ // same <index, length> pair for each of those builtins.
+ for (const auto &FctName : SLM.second.Names) {
+ std::string RetStmt;
+ raw_string_ostream SS(RetStmt);
+ SS << "return std::make_pair(" << CumulativeIndex << ", " << Ovl.size()
+ << ");";
+ SS.flush();
+ ValidBuiltins.push_back(StringMatcher::StringPair(FctName, RetStmt));
+ }
+ CumulativeIndex += Ovl.size();
}
OS << R"(
@@ -583,9 +751,7 @@ static void OCL2Qual(ASTContext &Context, const OpenCLTypeStruct &Ty,
}
// End of switch statement.
- OS << " default:\n"
- << " llvm_unreachable(\"OpenCL builtin type not handled yet\");\n"
- << " } // end of switch (Ty.ID)\n\n";
+ OS << " } // end of switch (Ty.ID)\n\n";
// Step 2.
// Add ExtVector types if this was a generic type, as the switch statement
diff --git a/clang/utils/TableGen/ClangTypeNodesEmitter.cpp b/clang/utils/TableGen/ClangTypeNodesEmitter.cpp
index c9986c8fa496..690042f3200e 100644
--- a/clang/utils/TableGen/ClangTypeNodesEmitter.cpp
+++ b/clang/utils/TableGen/ClangTypeNodesEmitter.cpp
@@ -45,6 +45,9 @@
//
//===----------------------------------------------------------------------===//
+#include "ASTTableGen.h"
+#include "TableGenBackends.h"
+
#include "llvm/ADT/StringRef.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
@@ -52,9 +55,10 @@
#include <set>
#include <string>
#include <vector>
-#include "TableGenBackends.h"
using namespace llvm;
+using namespace clang;
+using namespace clang::tblgen;
// These are spellings in the generated output.
#define TypeMacroName "TYPE"
@@ -66,155 +70,139 @@ using namespace llvm;
#define LastTypeMacroName "LAST_TYPE"
#define LeafTypeMacroName "LEAF_TYPE"
-// These are spellings in the tblgen file.
-// (Type is also used for the spelling of the AST class.)
#define TypeClassName "Type"
-#define DerivedTypeClassName "DerivedType"
-#define AlwaysDependentClassName "AlwaysDependent"
-#define NeverCanonicalClassName "NeverCanonical"
-#define NeverCanonicalUnlessDependentClassName "NeverCanonicalUnlessDependent"
-#define LeafTypeClassName "LeafType"
-#define AbstractFieldName "Abstract"
-#define BaseFieldName "Base"
-
-static StringRef getIdForType(Record *type) {
- // The record name is expected to be the full C++ class name,
- // including "Type". Check for that and strip it off.
- auto fullName = type->getName();
- if (!fullName.endswith("Type"))
- PrintFatalError(type->getLoc(), "name of Type node doesn't end in Type");
- return fullName.drop_back(4);
-}
namespace {
class TypeNodeEmitter {
- RecordKeeper &Records;
- raw_ostream &Out;
- const std::vector<Record*> Types;
- std::vector<StringRef> MacrosToUndef;
+ RecordKeeper &Records;
+ raw_ostream &Out;
+ const std::vector<Record*> Types;
+ std::vector<StringRef> MacrosToUndef;
public:
- TypeNodeEmitter(RecordKeeper &records, raw_ostream &out)
- : Records(records), Out(out),
- Types(Records.getAllDerivedDefinitions("Type")) {
- }
+ TypeNodeEmitter(RecordKeeper &records, raw_ostream &out)
+ : Records(records), Out(out),
+ Types(Records.getAllDerivedDefinitions(TypeNodeClassName)) {
+ }
- void emit();
+ void emit();
private:
- void emitFallbackDefine(StringRef macroName, StringRef fallbackMacroName,
- StringRef args);
+ void emitFallbackDefine(StringRef macroName, StringRef fallbackMacroName,
+ StringRef args);
- void emitNodeInvocations();
- void emitLastNodeInvocation();
- void emitLeafNodeInvocations();
+ void emitNodeInvocations();
+ void emitLastNodeInvocation(TypeNode lastType);
+ void emitLeafNodeInvocations();
- void addMacroToUndef(StringRef macroName);
- void emitUndefs();
+ void addMacroToUndef(StringRef macroName);
+ void emitUndefs();
};
}
void TypeNodeEmitter::emit() {
- if (Types.empty())
- PrintFatalError("no Type records in input!");
+ if (Types.empty())
+ PrintFatalError("no Type records in input!");
- emitSourceFileHeader("An x-macro database of Clang type nodes", Out);
+ emitSourceFileHeader("An x-macro database of Clang type nodes", Out);
- // Preamble
- addMacroToUndef(TypeMacroName);
- addMacroToUndef(AbstractTypeMacroName);
- emitFallbackDefine(AbstractTypeMacroName, TypeMacroName, TypeMacroArgs);
- emitFallbackDefine(NonCanonicalTypeMacroName, TypeMacroName, TypeMacroArgs);
- emitFallbackDefine(DependentTypeMacroName, TypeMacroName, TypeMacroArgs);
- emitFallbackDefine(NonCanonicalUnlessDependentTypeMacroName, TypeMacroName,
- TypeMacroArgs);
+ // Preamble
+ addMacroToUndef(TypeMacroName);
+ addMacroToUndef(AbstractTypeMacroName);
+ emitFallbackDefine(AbstractTypeMacroName, TypeMacroName, TypeMacroArgs);
+ emitFallbackDefine(NonCanonicalTypeMacroName, TypeMacroName, TypeMacroArgs);
+ emitFallbackDefine(DependentTypeMacroName, TypeMacroName, TypeMacroArgs);
+ emitFallbackDefine(NonCanonicalUnlessDependentTypeMacroName, TypeMacroName,
+ TypeMacroArgs);
- // Invocations.
- emitNodeInvocations();
- emitLastNodeInvocation();
- emitLeafNodeInvocations();
+ // Invocations.
+ emitNodeInvocations();
+ emitLeafNodeInvocations();
- // Postmatter
- emitUndefs();
+ // Postmatter
+ emitUndefs();
}
void TypeNodeEmitter::emitFallbackDefine(StringRef macroName,
- StringRef fallbackMacroName,
- StringRef args) {
+ StringRef fallbackMacroName,
+ StringRef args) {
Out << "#ifndef " << macroName << "\n";
Out << "# define " << macroName << args
- << " " << fallbackMacroName << args << "\n";
+ << " " << fallbackMacroName << args << "\n";
Out << "#endif\n";
addMacroToUndef(macroName);
}
void TypeNodeEmitter::emitNodeInvocations() {
- for (auto type : Types) {
- // The name with the Type suffix.
- StringRef id = getIdForType(type);
+ TypeNode lastType;
+
+ visitASTNodeHierarchy<TypeNode>(Records, [&](TypeNode type, TypeNode base) {
+ // If this is the Type node itself, skip it; it can't be handled
+ // uniformly by metaprograms because it doesn't have a base.
+ if (!base) return;
+
+ // Figure out which macro to use.
+ StringRef macroName;
+ auto setMacroName = [&](StringRef newName) {
+ if (!macroName.empty())
+ PrintFatalError(type.getLoc(),
+ Twine("conflict when computing macro name for "
+ "Type node: trying to use both \"")
+ + macroName + "\" and \"" + newName + "\"");
+ macroName = newName;
+ };
+ if (type.isSubClassOf(AlwaysDependentClassName))
+ setMacroName(DependentTypeMacroName);
+ if (type.isSubClassOf(NeverCanonicalClassName))
+ setMacroName(NonCanonicalTypeMacroName);
+ if (type.isSubClassOf(NeverCanonicalUnlessDependentClassName))
+ setMacroName(NonCanonicalUnlessDependentTypeMacroName);
+ if (type.isAbstract())
+ setMacroName(AbstractTypeMacroName);
+ if (macroName.empty())
+ macroName = TypeMacroName;
- // Figure out which macro to use.
- StringRef macroName;
- auto setMacroName = [&](StringRef newName) {
- if (!macroName.empty())
- PrintFatalError(type->getLoc(),
- Twine("conflict when computing macro name for "
- "Type node: trying to use both \"")
- + macroName + "\" and \"" + newName + "\"");
- macroName = newName;
- };
- if (type->isSubClassOf(AlwaysDependentClassName))
- setMacroName(DependentTypeMacroName);
- if (type->isSubClassOf(NeverCanonicalClassName))
- setMacroName(NonCanonicalTypeMacroName);
- if (type->isSubClassOf(NeverCanonicalUnlessDependentClassName))
- setMacroName(NonCanonicalUnlessDependentTypeMacroName);
- if (type->getValueAsBit(AbstractFieldName))
- setMacroName(AbstractTypeMacroName);
- if (macroName.empty())
- macroName = TypeMacroName;
+ // Generate the invocation line.
+ Out << macroName << "(" << type.getId() << ", "
+ << base.getClassName() << ")\n";
- // Compute the base class.
- StringRef baseName = TypeClassName;
- if (type->isSubClassOf(DerivedTypeClassName))
- baseName = type->getValueAsDef(BaseFieldName)->getName();
+ lastType = type;
+ });
- // Generate the invocation line.
- Out << macroName << "(" << id << ", " << baseName << ")\n";
- }
+ emitLastNodeInvocation(lastType);
}
-void TypeNodeEmitter::emitLastNodeInvocation() {
- // We check that this is non-empty earlier.
- Out << "#ifdef " LastTypeMacroName "\n"
- LastTypeMacroName "(" << getIdForType(Types.back()) << ")\n"
- "#undef " LastTypeMacroName "\n"
- "#endif\n";
+void TypeNodeEmitter::emitLastNodeInvocation(TypeNode type) {
+ // We check that this is non-empty earlier.
+ Out << "#ifdef " LastTypeMacroName "\n"
+ LastTypeMacroName "(" << type.getId() << ")\n"
+ "#undef " LastTypeMacroName "\n"
+ "#endif\n";
}
void TypeNodeEmitter::emitLeafNodeInvocations() {
- Out << "#ifdef " LeafTypeMacroName "\n";
+ Out << "#ifdef " LeafTypeMacroName "\n";
- for (auto type : Types) {
- if (!type->isSubClassOf(LeafTypeClassName)) continue;
- Out << LeafTypeMacroName "(" << getIdForType(type) << ")\n";
- }
+ for (TypeNode type : Types) {
+ if (!type.isSubClassOf(LeafTypeClassName)) continue;
+ Out << LeafTypeMacroName "(" << type.getId() << ")\n";
+ }
- Out << "#undef " LeafTypeMacroName "\n"
- "#endif\n";
+ Out << "#undef " LeafTypeMacroName "\n"
+ "#endif\n";
}
void TypeNodeEmitter::addMacroToUndef(StringRef macroName) {
- MacrosToUndef.push_back(macroName);
+ MacrosToUndef.push_back(macroName);
}
void TypeNodeEmitter::emitUndefs() {
- for (auto &macroName : MacrosToUndef) {
- Out << "#undef " << macroName << "\n";
- }
+ for (auto &macroName : MacrosToUndef) {
+ Out << "#undef " << macroName << "\n";
+ }
}
void clang::EmitClangTypeNodes(RecordKeeper &records, raw_ostream &out) {
- TypeNodeEmitter(records, out).emit();
+ TypeNodeEmitter(records, out).emit();
}
diff --git a/clang/utils/TableGen/MveEmitter.cpp b/clang/utils/TableGen/MveEmitter.cpp
new file mode 100644
index 000000000000..431e5c477c2b
--- /dev/null
+++ b/clang/utils/TableGen/MveEmitter.cpp
@@ -0,0 +1,1882 @@
+//===- MveEmitter.cpp - Generate arm_mve.h for use with clang -*- 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 set of linked tablegen backends is responsible for emitting the bits
+// and pieces that implement <arm_mve.h>, which is defined by the ACLE standard
+// and provides a set of types and functions for (more or less) direct access
+// to the MVE instruction set, including the scalar shifts as well as the
+// vector instructions.
+//
+// MVE's standard intrinsic functions are unusual in that they have a system of
+// polymorphism. For example, the function vaddq() can behave like vaddq_u16(),
+// vaddq_f32(), vaddq_s8(), etc., depending on the types of the vector
+// arguments you give it.
+//
+// This constrains the implementation strategies. The usual approach to making
+// the user-facing functions polymorphic would be to either use
+// __attribute__((overloadable)) to make a set of vaddq() functions that are
+// all inline wrappers on the underlying clang builtins, or to define a single
+// vaddq() macro which expands to an instance of _Generic.
+//
+// The inline-wrappers approach would work fine for most intrinsics, except for
+// the ones that take an argument required to be a compile-time constant,
+// because if you wrap an inline function around a call to a builtin, the
+// constant nature of the argument is not passed through.
+//
+// The _Generic approach can be made to work with enough effort, but it takes a
+// lot of machinery, because of the design feature of _Generic that even the
+// untaken branches are required to pass all front-end validity checks such as
+// type-correctness. You can work around that by nesting further _Generics all
+// over the place to coerce things to the right type in untaken branches, but
+// what you get out is complicated, hard to guarantee its correctness, and
+// worst of all, gives _completely unreadable_ error messages if the user gets
+// the types wrong for an intrinsic call.
+//
+// Therefore, my strategy is to introduce a new __attribute__ that allows a
+// function to be mapped to a clang builtin even though it doesn't have the
+// same name, and then declare all the user-facing MVE function names with that
+// attribute, mapping each one directly to the clang builtin. And the
+// polymorphic ones have __attribute__((overloadable)) as well. So once the
+// compiler has resolved the overload, it knows the internal builtin ID of the
+// selected function, and can check the immediate arguments against that; and
+// if the user gets the types wrong in a call to a polymorphic intrinsic, they
+// get a completely clear error message showing all the declarations of that
+// function in the header file and explaining why each one doesn't fit their
+// call.
+//
+// The downside of this is that if every clang builtin has to correspond
+// exactly to a user-facing ACLE intrinsic, then you can't save work in the
+// frontend by doing it in the header file: CGBuiltin.cpp has to do the entire
+// job of converting an ACLE intrinsic call into LLVM IR. So the Tablegen
+// description for an MVE intrinsic has to contain a full description of the
+// sequence of IRBuilder calls that clang will need to make.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace {
+
+class MveEmitter;
+class Result;
+
+// -----------------------------------------------------------------------------
+// A system of classes to represent all the types we'll need to deal with in
+// the prototypes of intrinsics.
+//
+// Query methods include finding out the C name of a type; the "LLVM name" in
+// the sense of a C++ code snippet that can be used in the codegen function;
+// the suffix that represents the type in the ACLE intrinsic naming scheme
+// (e.g. 's32' represents int32_t in intrinsics such as vaddq_s32); whether the
+// type is floating-point related (hence should be under #ifdef in the MVE
+// header so that it isn't included in integer-only MVE mode); and the type's
+// size in bits. Not all subtypes support all these queries.
+
+class Type {
+public:
+ enum class TypeKind {
+ // Void appears as a return type (for store intrinsics, which are pure
+ // side-effect). It's also used as the parameter type in the Tablegen
+ // when an intrinsic doesn't need to come in various suffixed forms like
+ // vfooq_s8,vfooq_u16,vfooq_f32.
+ Void,
+
+ // Scalar is used for ordinary int and float types of all sizes.
+ Scalar,
+
+ // Vector is used for anything that occupies exactly one MVE vector
+ // register, i.e. {uint,int,float}NxM_t.
+ Vector,
+
+ // MultiVector is used for the {uint,int,float}NxMxK_t types used by the
+ // interleaving load/store intrinsics v{ld,st}{2,4}q.
+ MultiVector,
+
+ // Predicate is used by all the predicated intrinsics. Its C
+ // representation is mve_pred16_t (which is just an alias for uint16_t).
+ // But we give more detail here, by indicating that a given predicate
+ // instruction is logically regarded as a vector of i1 containing the
+ // same number of lanes as the input vector type. So our Predicate type
+ // comes with a lane count, which we use to decide which kind of <n x i1>
+ // we'll invoke the pred_i2v IR intrinsic to translate it into.
+ Predicate,
+
+ // Pointer is used for pointer types (obviously), and comes with a flag
+ // indicating whether it's a pointer to a const or mutable instance of
+ // the pointee type.
+ Pointer,
+ };
+
+private:
+ const TypeKind TKind;
+
+protected:
+ Type(TypeKind K) : TKind(K) {}
+
+public:
+ TypeKind typeKind() const { return TKind; }
+ virtual ~Type() = default;
+ virtual bool requiresFloat() const = 0;
+ virtual unsigned sizeInBits() const = 0;
+ virtual std::string cName() const = 0;
+ virtual std::string llvmName() const {
+ PrintFatalError("no LLVM type name available for type " + cName());
+ }
+ virtual std::string acleSuffix(std::string) const {
+ PrintFatalError("no ACLE suffix available for this type");
+ }
+};
+
+enum class ScalarTypeKind { SignedInt, UnsignedInt, Float };
+inline std::string toLetter(ScalarTypeKind kind) {
+ switch (kind) {
+ case ScalarTypeKind::SignedInt:
+ return "s";
+ case ScalarTypeKind::UnsignedInt:
+ return "u";
+ case ScalarTypeKind::Float:
+ return "f";
+ }
+ llvm_unreachable("Unhandled ScalarTypeKind enum");
+}
+inline std::string toCPrefix(ScalarTypeKind kind) {
+ switch (kind) {
+ case ScalarTypeKind::SignedInt:
+ return "int";
+ case ScalarTypeKind::UnsignedInt:
+ return "uint";
+ case ScalarTypeKind::Float:
+ return "float";
+ }
+ llvm_unreachable("Unhandled ScalarTypeKind enum");
+}
+
+class VoidType : public Type {
+public:
+ VoidType() : Type(TypeKind::Void) {}
+ unsigned sizeInBits() const override { return 0; }
+ bool requiresFloat() const override { return false; }
+ std::string cName() const override { return "void"; }
+
+ static bool classof(const Type *T) { return T->typeKind() == TypeKind::Void; }
+ std::string acleSuffix(std::string) const override { return ""; }
+};
+
+class PointerType : public Type {
+ const Type *Pointee;
+ bool Const;
+
+public:
+ PointerType(const Type *Pointee, bool Const)
+ : Type(TypeKind::Pointer), Pointee(Pointee), Const(Const) {}
+ unsigned sizeInBits() const override { return 32; }
+ bool requiresFloat() const override { return Pointee->requiresFloat(); }
+ std::string cName() const override {
+ std::string Name = Pointee->cName();
+
+ // The syntax for a pointer in C is different when the pointee is
+ // itself a pointer. The MVE intrinsics don't contain any double
+ // pointers, so we don't need to worry about that wrinkle.
+ assert(!isa<PointerType>(Pointee) && "Pointer to pointer not supported");
+
+ if (Const)
+ Name = "const " + Name;
+ return Name + " *";
+ }
+ std::string llvmName() const override {
+ return "llvm::PointerType::getUnqual(" + Pointee->llvmName() + ")";
+ }
+
+ static bool classof(const Type *T) {
+ return T->typeKind() == TypeKind::Pointer;
+ }
+};
+
+// Base class for all the types that have a name of the form
+// [prefix][numbers]_t, like int32_t, uint16x8_t, float32x4x2_t.
+//
+// For this sub-hierarchy we invent a cNameBase() method which returns the
+// whole name except for the trailing "_t", so that Vector and MultiVector can
+// append an extra "x2" or whatever to their element type's cNameBase(). Then
+// the main cName() query method puts "_t" on the end for the final type name.
+
+class CRegularNamedType : public Type {
+ using Type::Type;
+ virtual std::string cNameBase() const = 0;
+
+public:
+ std::string cName() const override { return cNameBase() + "_t"; }
+};
+
+class ScalarType : public CRegularNamedType {
+ ScalarTypeKind Kind;
+ unsigned Bits;
+ std::string NameOverride;
+
+public:
+ ScalarType(const Record *Record) : CRegularNamedType(TypeKind::Scalar) {
+ Kind = StringSwitch<ScalarTypeKind>(Record->getValueAsString("kind"))
+ .Case("s", ScalarTypeKind::SignedInt)
+ .Case("u", ScalarTypeKind::UnsignedInt)
+ .Case("f", ScalarTypeKind::Float);
+ Bits = Record->getValueAsInt("size");
+ NameOverride = Record->getValueAsString("nameOverride");
+ }
+ unsigned sizeInBits() const override { return Bits; }
+ ScalarTypeKind kind() const { return Kind; }
+ std::string suffix() const { return toLetter(Kind) + utostr(Bits); }
+ std::string cNameBase() const override {
+ return toCPrefix(Kind) + utostr(Bits);
+ }
+ std::string cName() const override {
+ if (NameOverride.empty())
+ return CRegularNamedType::cName();
+ return NameOverride;
+ }
+ std::string llvmName() const override {
+ if (Kind == ScalarTypeKind::Float) {
+ if (Bits == 16)
+ return "HalfTy";
+ if (Bits == 32)
+ return "FloatTy";
+ if (Bits == 64)
+ return "DoubleTy";
+ PrintFatalError("bad size for floating type");
+ }
+ return "Int" + utostr(Bits) + "Ty";
+ }
+ std::string acleSuffix(std::string overrideLetter) const override {
+ return "_" + (overrideLetter.size() ? overrideLetter : toLetter(Kind))
+ + utostr(Bits);
+ }
+ bool isInteger() const { return Kind != ScalarTypeKind::Float; }
+ bool requiresFloat() const override { return !isInteger(); }
+ bool hasNonstandardName() const { return !NameOverride.empty(); }
+
+ static bool classof(const Type *T) {
+ return T->typeKind() == TypeKind::Scalar;
+ }
+};
+
+class VectorType : public CRegularNamedType {
+ const ScalarType *Element;
+ unsigned Lanes;
+
+public:
+ VectorType(const ScalarType *Element, unsigned Lanes)
+ : CRegularNamedType(TypeKind::Vector), Element(Element), Lanes(Lanes) {}
+ unsigned sizeInBits() const override { return Lanes * Element->sizeInBits(); }
+ unsigned lanes() const { return Lanes; }
+ bool requiresFloat() const override { return Element->requiresFloat(); }
+ std::string cNameBase() const override {
+ return Element->cNameBase() + "x" + utostr(Lanes);
+ }
+ std::string llvmName() const override {
+ return "llvm::VectorType::get(" + Element->llvmName() + ", " +
+ utostr(Lanes) + ")";
+ }
+
+ static bool classof(const Type *T) {
+ return T->typeKind() == TypeKind::Vector;
+ }
+};
+
+class MultiVectorType : public CRegularNamedType {
+ const VectorType *Element;
+ unsigned Registers;
+
+public:
+ MultiVectorType(unsigned Registers, const VectorType *Element)
+ : CRegularNamedType(TypeKind::MultiVector), Element(Element),
+ Registers(Registers) {}
+ unsigned sizeInBits() const override {
+ return Registers * Element->sizeInBits();
+ }
+ unsigned registers() const { return Registers; }
+ bool requiresFloat() const override { return Element->requiresFloat(); }
+ std::string cNameBase() const override {
+ return Element->cNameBase() + "x" + utostr(Registers);
+ }
+
+ // MultiVectorType doesn't override llvmName, because we don't expect to do
+ // automatic code generation for the MVE intrinsics that use it: the {vld2,
+ // vld4, vst2, vst4} family are the only ones that use these types, so it was
+ // easier to hand-write the codegen for dealing with these structs than to
+ // build in lots of extra automatic machinery that would only be used once.
+
+ static bool classof(const Type *T) {
+ return T->typeKind() == TypeKind::MultiVector;
+ }
+};
+
+class PredicateType : public CRegularNamedType {
+ unsigned Lanes;
+
+public:
+ PredicateType(unsigned Lanes)
+ : CRegularNamedType(TypeKind::Predicate), Lanes(Lanes) {}
+ unsigned sizeInBits() const override { return 16; }
+ std::string cNameBase() const override { return "mve_pred16"; }
+ bool requiresFloat() const override { return false; };
+ std::string llvmName() const override {
+ // Use <4 x i1> instead of <2 x i1> for two-lane vector types. See
+ // the comment in llvm/lib/Target/ARM/ARMInstrMVE.td for further
+ // explanation.
+ unsigned ModifiedLanes = (Lanes == 2 ? 4 : Lanes);
+
+ return "llvm::VectorType::get(Builder.getInt1Ty(), " +
+ utostr(ModifiedLanes) + ")";
+ }
+
+ static bool classof(const Type *T) {
+ return T->typeKind() == TypeKind::Predicate;
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Class to facilitate merging together the code generation for many intrinsics
+// by means of varying a few constant or type parameters.
+//
+// Most obviously, the intrinsics in a single parametrised family will have
+// code generation sequences that only differ in a type or two, e.g. vaddq_s8
+// and vaddq_u16 will look the same apart from putting a different vector type
+// in the call to CGM.getIntrinsic(). But also, completely different intrinsics
+// will often code-generate in the same way, with only a different choice of
+// _which_ IR intrinsic they lower to (e.g. vaddq_m_s8 and vmulq_m_s8), but
+// marshalling the arguments and return values of the IR intrinsic in exactly
+// the same way. And others might differ only in some other kind of constant,
+// such as a lane index.
+//
+// So, when we generate the IR-building code for all these intrinsics, we keep
+// track of every value that could possibly be pulled out of the code and
+// stored ahead of time in a local variable. Then we group together intrinsics
+// by textual equivalence of the code that would result if _all_ those
+// parameters were stored in local variables. That gives us maximal sets that
+// can be implemented by a single piece of IR-building code by changing
+// parameter values ahead of time.
+//
+// After we've done that, we do a second pass in which we only allocate _some_
+// of the parameters into local variables, by tracking which ones have the same
+// values as each other (so that a single variable can be reused) and which
+// ones are the same across the whole set (so that no variable is needed at
+// all).
+//
+// Hence the class below. Its allocParam method is invoked during code
+// generation by every method of a Result subclass (see below) that wants to
+// give it the opportunity to pull something out into a switchable parameter.
+// It returns a variable name for the parameter, or (if it's being used in the
+// second pass once we've decided that some parameters don't need to be stored
+// in variables after all) it might just return the input expression unchanged.
+
+struct CodeGenParamAllocator {
+ // Accumulated during code generation
+ std::vector<std::string> *ParamTypes = nullptr;
+ std::vector<std::string> *ParamValues = nullptr;
+
+ // Provided ahead of time in pass 2, to indicate which parameters are being
+ // assigned to what. This vector contains an entry for each call to
+ // allocParam expected during code gen (which we counted up in pass 1), and
+ // indicates the number of the parameter variable that should be returned, or
+ // -1 if this call shouldn't allocate a parameter variable at all.
+ //
+ // We rely on the recursive code generation working identically in passes 1
+ // and 2, so that the same list of calls to allocParam happen in the same
+ // order. That guarantees that the parameter numbers recorded in pass 1 will
+ // match the entries in this vector that store what MveEmitter::EmitBuiltinCG
+ // decided to do about each one in pass 2.
+ std::vector<int> *ParamNumberMap = nullptr;
+
+ // Internally track how many things we've allocated
+ unsigned nparams = 0;
+
+ std::string allocParam(StringRef Type, StringRef Value) {
+ unsigned ParamNumber;
+
+ if (!ParamNumberMap) {
+ // In pass 1, unconditionally assign a new parameter variable to every
+ // value we're asked to process.
+ ParamNumber = nparams++;
+ } else {
+ // In pass 2, consult the map provided by the caller to find out which
+ // variable we should be keeping things in.
+ int MapValue = (*ParamNumberMap)[nparams++];
+ if (MapValue < 0)
+ return Value;
+ ParamNumber = MapValue;
+ }
+
+ // If we've allocated a new parameter variable for the first time, store
+ // its type and value to be retrieved after codegen.
+ if (ParamTypes && ParamTypes->size() == ParamNumber)
+ ParamTypes->push_back(Type);
+ if (ParamValues && ParamValues->size() == ParamNumber)
+ ParamValues->push_back(Value);
+
+ // Unimaginative naming scheme for parameter variables.
+ return "Param" + utostr(ParamNumber);
+ }
+};
+
+// -----------------------------------------------------------------------------
+// System of classes that represent all the intermediate values used during
+// code-generation for an intrinsic.
+//
+// The base class 'Result' can represent a value of the LLVM type 'Value', or
+// sometimes 'Address' (for loads/stores, including an alignment requirement).
+//
+// In the case where the Tablegen provides a value in the codegen dag as a
+// plain integer literal, the Result object we construct here will be one that
+// returns true from hasIntegerConstantValue(). This allows the generated C++
+// code to use the constant directly in contexts which can take a literal
+// integer, such as Builder.CreateExtractValue(thing, 1), without going to the
+// effort of calling llvm::ConstantInt::get() and then pulling the constant
+// back out of the resulting llvm:Value later.
+
+class Result {
+public:
+ // Convenient shorthand for the pointer type we'll be using everywhere.
+ using Ptr = std::shared_ptr<Result>;
+
+private:
+ Ptr Predecessor;
+ std::string VarName;
+ bool VarNameUsed = false;
+ unsigned Visited = 0;
+
+public:
+ virtual ~Result() = default;
+ using Scope = std::map<std::string, Ptr>;
+ virtual void genCode(raw_ostream &OS, CodeGenParamAllocator &) const = 0;
+ virtual bool hasIntegerConstantValue() const { return false; }
+ virtual uint32_t integerConstantValue() const { return 0; }
+ virtual bool hasIntegerValue() const { return false; }
+ virtual std::string getIntegerValue(const std::string &) {
+ llvm_unreachable("non-working Result::getIntegerValue called");
+ }
+ virtual std::string typeName() const { return "Value *"; }
+
+ // Mostly, when a code-generation operation has a dependency on prior
+ // operations, it's because it uses the output values of those operations as
+ // inputs. But there's one exception, which is the use of 'seq' in Tablegen
+ // to indicate that operations have to be performed in sequence regardless of
+ // whether they use each others' output values.
+ //
+ // So, the actual generation of code is done by depth-first search, using the
+ // prerequisites() method to get a list of all the other Results that have to
+ // be computed before this one. That method divides into the 'predecessor',
+ // set by setPredecessor() while processing a 'seq' dag node, and the list
+ // returned by 'morePrerequisites', which each subclass implements to return
+ // a list of the Results it uses as input to whatever its own computation is
+ // doing.
+
+ virtual void morePrerequisites(std::vector<Ptr> &output) const {}
+ std::vector<Ptr> prerequisites() const {
+ std::vector<Ptr> ToRet;
+ if (Predecessor)
+ ToRet.push_back(Predecessor);
+ morePrerequisites(ToRet);
+ return ToRet;
+ }
+
+ void setPredecessor(Ptr p) {
+ assert(!Predecessor);
+ Predecessor = p;
+ }
+
+ // Each Result will be assigned a variable name in the output code, but not
+ // all those variable names will actually be used (e.g. the return value of
+ // Builder.CreateStore has void type, so nobody will want to refer to it). To
+ // prevent annoying compiler warnings, we track whether each Result's
+ // variable name was ever actually mentioned in subsequent statements, so
+ // that it can be left out of the final generated code.
+ std::string varname() {
+ VarNameUsed = true;
+ return VarName;
+ }
+ void setVarname(const StringRef s) { VarName = s; }
+ bool varnameUsed() const { return VarNameUsed; }
+
+ // Emit code to generate this result as a Value *.
+ virtual std::string asValue() {
+ return varname();
+ }
+
+ // Code generation happens in multiple passes. This method tracks whether a
+ // Result has yet been visited in a given pass, without the need for a
+ // tedious loop in between passes that goes through and resets a 'visited'
+ // flag back to false: you just set Pass=1 the first time round, and Pass=2
+ // the second time.
+ bool needsVisiting(unsigned Pass) {
+ bool ToRet = Visited < Pass;
+ Visited = Pass;
+ return ToRet;
+ }
+};
+
+// Result subclass that retrieves one of the arguments to the clang builtin
+// function. In cases where the argument has pointer type, we call
+// EmitPointerWithAlignment and store the result in a variable of type Address,
+// so that load and store IR nodes can know the right alignment. Otherwise, we
+// call EmitScalarExpr.
+//
+// There are aggregate parameters in the MVE intrinsics API, but we don't deal
+// with them in this Tablegen back end: they only arise in the vld2q/vld4q and
+// vst2q/vst4q family, which is few enough that we just write the code by hand
+// for those in CGBuiltin.cpp.
+class BuiltinArgResult : public Result {
+public:
+ unsigned ArgNum;
+ bool AddressType;
+ bool Immediate;
+ BuiltinArgResult(unsigned ArgNum, bool AddressType, bool Immediate)
+ : ArgNum(ArgNum), AddressType(AddressType), Immediate(Immediate) {}
+ void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {
+ OS << (AddressType ? "EmitPointerWithAlignment" : "EmitScalarExpr")
+ << "(E->getArg(" << ArgNum << "))";
+ }
+ std::string typeName() const override {
+ return AddressType ? "Address" : Result::typeName();
+ }
+ // Emit code to generate this result as a Value *.
+ std::string asValue() override {
+ if (AddressType)
+ return "(" + varname() + ".getPointer())";
+ return Result::asValue();
+ }
+ bool hasIntegerValue() const override { return Immediate; }
+ std::string getIntegerValue(const std::string &IntType) override {
+ return "GetIntegerConstantValue<" + IntType + ">(E->getArg(" +
+ utostr(ArgNum) + "), getContext())";
+ }
+};
+
+// Result subclass for an integer literal appearing in Tablegen. This may need
+// to be turned into an llvm::Result by means of llvm::ConstantInt::get(), or
+// it may be used directly as an integer, depending on which IRBuilder method
+// it's being passed to.
+class IntLiteralResult : public Result {
+public:
+ const ScalarType *IntegerType;
+ uint32_t IntegerValue;
+ IntLiteralResult(const ScalarType *IntegerType, uint32_t IntegerValue)
+ : IntegerType(IntegerType), IntegerValue(IntegerValue) {}
+ void genCode(raw_ostream &OS,
+ CodeGenParamAllocator &ParamAlloc) const override {
+ OS << "llvm::ConstantInt::get("
+ << ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName())
+ << ", ";
+ OS << ParamAlloc.allocParam(IntegerType->cName(), utostr(IntegerValue))
+ << ")";
+ }
+ bool hasIntegerConstantValue() const override { return true; }
+ uint32_t integerConstantValue() const override { return IntegerValue; }
+};
+
+// Result subclass representing a cast between different integer types. We use
+// our own ScalarType abstraction as the representation of the target type,
+// which gives both size and signedness.
+class IntCastResult : public Result {
+public:
+ const ScalarType *IntegerType;
+ Ptr V;
+ IntCastResult(const ScalarType *IntegerType, Ptr V)
+ : IntegerType(IntegerType), V(V) {}
+ void genCode(raw_ostream &OS,
+ CodeGenParamAllocator &ParamAlloc) const override {
+ OS << "Builder.CreateIntCast(" << V->varname() << ", "
+ << ParamAlloc.allocParam("llvm::Type *", IntegerType->llvmName()) << ", "
+ << ParamAlloc.allocParam("bool",
+ IntegerType->kind() == ScalarTypeKind::SignedInt
+ ? "true"
+ : "false")
+ << ")";
+ }
+ void morePrerequisites(std::vector<Ptr> &output) const override {
+ output.push_back(V);
+ }
+};
+
+// Result subclass representing a cast between different pointer types.
+class PointerCastResult : public Result {
+public:
+ const PointerType *PtrType;
+ Ptr V;
+ PointerCastResult(const PointerType *PtrType, Ptr V)
+ : PtrType(PtrType), V(V) {}
+ void genCode(raw_ostream &OS,
+ CodeGenParamAllocator &ParamAlloc) const override {
+ OS << "Builder.CreatePointerCast(" << V->asValue() << ", "
+ << ParamAlloc.allocParam("llvm::Type *", PtrType->llvmName()) << ")";
+ }
+ void morePrerequisites(std::vector<Ptr> &output) const override {
+ output.push_back(V);
+ }
+};
+
+// Result subclass representing a call to an IRBuilder method. Each IRBuilder
+// method we want to use will have a Tablegen record giving the method name and
+// describing any important details of how to call it, such as whether a
+// particular argument should be an integer constant instead of an llvm::Value.
+class IRBuilderResult : public Result {
+public:
+ StringRef CallPrefix;
+ std::vector<Ptr> Args;
+ std::set<unsigned> AddressArgs;
+ std::map<unsigned, std::string> IntegerArgs;
+ IRBuilderResult(StringRef CallPrefix, std::vector<Ptr> Args,
+ std::set<unsigned> AddressArgs,
+ std::map<unsigned, std::string> IntegerArgs)
+ : CallPrefix(CallPrefix), Args(Args), AddressArgs(AddressArgs),
+ IntegerArgs(IntegerArgs) {}
+ void genCode(raw_ostream &OS,
+ CodeGenParamAllocator &ParamAlloc) const override {
+ OS << CallPrefix;
+ const char *Sep = "";
+ for (unsigned i = 0, e = Args.size(); i < e; ++i) {
+ Ptr Arg = Args[i];
+ auto it = IntegerArgs.find(i);
+
+ OS << Sep;
+ Sep = ", ";
+
+ if (it != IntegerArgs.end()) {
+ if (Arg->hasIntegerConstantValue())
+ OS << "static_cast<" << it->second << ">("
+ << ParamAlloc.allocParam(it->second,
+ utostr(Arg->integerConstantValue()))
+ << ")";
+ else if (Arg->hasIntegerValue())
+ OS << ParamAlloc.allocParam(it->second,
+ Arg->getIntegerValue(it->second));
+ } else {
+ OS << Arg->varname();
+ }
+ }
+ OS << ")";
+ }
+ void morePrerequisites(std::vector<Ptr> &output) const override {
+ for (unsigned i = 0, e = Args.size(); i < e; ++i) {
+ Ptr Arg = Args[i];
+ if (IntegerArgs.find(i) != IntegerArgs.end())
+ continue;
+ output.push_back(Arg);
+ }
+ }
+};
+
+// Result subclass representing making an Address out of a Value.
+class AddressResult : public Result {
+public:
+ Ptr Arg;
+ unsigned Align;
+ AddressResult(Ptr Arg, unsigned Align) : Arg(Arg), Align(Align) {}
+ void genCode(raw_ostream &OS,
+ CodeGenParamAllocator &ParamAlloc) const override {
+ OS << "Address(" << Arg->varname() << ", CharUnits::fromQuantity("
+ << Align << "))";
+ }
+ std::string typeName() const override {
+ return "Address";
+ }
+ void morePrerequisites(std::vector<Ptr> &output) const override {
+ output.push_back(Arg);
+ }
+};
+
+// Result subclass representing a call to an IR intrinsic, which we first have
+// to look up using an Intrinsic::ID constant and an array of types.
+class IRIntrinsicResult : public Result {
+public:
+ std::string IntrinsicID;
+ std::vector<const Type *> ParamTypes;
+ std::vector<Ptr> Args;
+ IRIntrinsicResult(StringRef IntrinsicID, std::vector<const Type *> ParamTypes,
+ std::vector<Ptr> Args)
+ : IntrinsicID(IntrinsicID), ParamTypes(ParamTypes), Args(Args) {}
+ void genCode(raw_ostream &OS,
+ CodeGenParamAllocator &ParamAlloc) const override {
+ std::string IntNo = ParamAlloc.allocParam(
+ "Intrinsic::ID", "Intrinsic::" + IntrinsicID);
+ OS << "Builder.CreateCall(CGM.getIntrinsic(" << IntNo;
+ if (!ParamTypes.empty()) {
+ OS << ", llvm::SmallVector<llvm::Type *, " << ParamTypes.size() << "> {";
+ const char *Sep = "";
+ for (auto T : ParamTypes) {
+ OS << Sep << ParamAlloc.allocParam("llvm::Type *", T->llvmName());
+ Sep = ", ";
+ }
+ OS << "}";
+ }
+ OS << "), llvm::SmallVector<Value *, " << Args.size() << "> {";
+ const char *Sep = "";
+ for (auto Arg : Args) {
+ OS << Sep << Arg->asValue();
+ Sep = ", ";
+ }
+ OS << "})";
+ }
+ void morePrerequisites(std::vector<Ptr> &output) const override {
+ output.insert(output.end(), Args.begin(), Args.end());
+ }
+};
+
+// Result subclass that specifies a type, for use in IRBuilder operations such
+// as CreateBitCast that take a type argument.
+class TypeResult : public Result {
+public:
+ const Type *T;
+ TypeResult(const Type *T) : T(T) {}
+ void genCode(raw_ostream &OS, CodeGenParamAllocator &) const override {
+ OS << T->llvmName();
+ }
+ std::string typeName() const override {
+ return "llvm::Type *";
+ }
+};
+
+// -----------------------------------------------------------------------------
+// Class that describes a single ACLE intrinsic.
+//
+// A Tablegen record will typically describe more than one ACLE intrinsic, by
+// means of setting the 'list<Type> Params' field to a list of multiple
+// parameter types, so as to define vaddq_{s8,u8,...,f16,f32} all in one go.
+// We'll end up with one instance of ACLEIntrinsic for *each* parameter type,
+// rather than a single one for all of them. Hence, the constructor takes both
+// a Tablegen record and the current value of the parameter type.
+
+class ACLEIntrinsic {
+ // Structure documenting that one of the intrinsic's arguments is required to
+ // be a compile-time constant integer, and what constraints there are on its
+ // value. Used when generating Sema checking code.
+ struct ImmediateArg {
+ enum class BoundsType { ExplicitRange, UInt };
+ BoundsType boundsType;
+ int64_t i1, i2;
+ StringRef ExtraCheckType, ExtraCheckArgs;
+ const Type *ArgType;
+ };
+
+ // For polymorphic intrinsics, FullName is the explicit name that uniquely
+ // identifies this variant of the intrinsic, and ShortName is the name it
+ // shares with at least one other intrinsic.
+ std::string ShortName, FullName;
+
+ // A very small number of intrinsics _only_ have a polymorphic
+ // variant (vuninitializedq taking an unevaluated argument).
+ bool PolymorphicOnly;
+
+ // Another rarely-used flag indicating that the builtin doesn't
+ // evaluate its argument(s) at all.
+ bool NonEvaluating;
+
+ const Type *ReturnType;
+ std::vector<const Type *> ArgTypes;
+ std::map<unsigned, ImmediateArg> ImmediateArgs;
+ Result::Ptr Code;
+
+ std::map<std::string, std::string> CustomCodeGenArgs;
+
+ // Recursive function that does the internals of code generation.
+ void genCodeDfs(Result::Ptr V, std::list<Result::Ptr> &Used,
+ unsigned Pass) const {
+ if (!V->needsVisiting(Pass))
+ return;
+
+ for (Result::Ptr W : V->prerequisites())
+ genCodeDfs(W, Used, Pass);
+
+ Used.push_back(V);
+ }
+
+public:
+ const std::string &shortName() const { return ShortName; }
+ const std::string &fullName() const { return FullName; }
+ const Type *returnType() const { return ReturnType; }
+ const std::vector<const Type *> &argTypes() const { return ArgTypes; }
+ bool requiresFloat() const {
+ if (ReturnType->requiresFloat())
+ return true;
+ for (const Type *T : ArgTypes)
+ if (T->requiresFloat())
+ return true;
+ return false;
+ }
+ bool polymorphic() const { return ShortName != FullName; }
+ bool polymorphicOnly() const { return PolymorphicOnly; }
+ bool nonEvaluating() const { return NonEvaluating; }
+
+ // External entry point for code generation, called from MveEmitter.
+ void genCode(raw_ostream &OS, CodeGenParamAllocator &ParamAlloc,
+ unsigned Pass) const {
+ if (!hasCode()) {
+ for (auto kv : CustomCodeGenArgs)
+ OS << " " << kv.first << " = " << kv.second << ";\n";
+ OS << " break; // custom code gen\n";
+ return;
+ }
+ std::list<Result::Ptr> Used;
+ genCodeDfs(Code, Used, Pass);
+
+ unsigned varindex = 0;
+ for (Result::Ptr V : Used)
+ if (V->varnameUsed())
+ V->setVarname("Val" + utostr(varindex++));
+
+ for (Result::Ptr V : Used) {
+ OS << " ";
+ if (V == Used.back()) {
+ assert(!V->varnameUsed());
+ OS << "return "; // FIXME: what if the top-level thing is void?
+ } else if (V->varnameUsed()) {
+ std::string Type = V->typeName();
+ OS << V->typeName();
+ if (!StringRef(Type).endswith("*"))
+ OS << " ";
+ OS << V->varname() << " = ";
+ }
+ V->genCode(OS, ParamAlloc);
+ OS << ";\n";
+ }
+ }
+ bool hasCode() const { return Code != nullptr; }
+
+ static std::string signedHexLiteral(const llvm::APInt &iOrig) {
+ llvm::APInt i = iOrig.trunc(64);
+ SmallString<40> s;
+ i.toString(s, 16, true, true);
+ return s.str();
+ }
+
+ std::string genSema() const {
+ std::vector<std::string> SemaChecks;
+
+ for (const auto &kv : ImmediateArgs) {
+ const ImmediateArg &IA = kv.second;
+
+ llvm::APInt lo(128, 0), hi(128, 0);
+ switch (IA.boundsType) {
+ case ImmediateArg::BoundsType::ExplicitRange:
+ lo = IA.i1;
+ hi = IA.i2;
+ break;
+ case ImmediateArg::BoundsType::UInt:
+ lo = 0;
+ hi = IA.i1;
+ break;
+ }
+
+ llvm::APInt typelo, typehi;
+ unsigned Bits = IA.ArgType->sizeInBits();
+ if (cast<ScalarType>(IA.ArgType)->kind() == ScalarTypeKind::SignedInt) {
+ typelo = llvm::APInt::getSignedMinValue(Bits).sext(128);
+ typehi = llvm::APInt::getSignedMaxValue(Bits).sext(128);
+ } else {
+ typelo = llvm::APInt::getMinValue(Bits).zext(128);
+ typehi = llvm::APInt::getMaxValue(Bits).zext(128);
+ }
+
+ std::string Index = utostr(kv.first);
+
+ if (lo.sle(typelo) && hi.sge(typehi))
+ SemaChecks.push_back("SemaBuiltinConstantArg(TheCall, " + Index + ")");
+ else
+ SemaChecks.push_back("SemaBuiltinConstantArgRange(TheCall, " + Index +
+ ", " + signedHexLiteral(lo) + ", " +
+ signedHexLiteral(hi) + ")");
+
+ if (!IA.ExtraCheckType.empty()) {
+ std::string Suffix;
+ if (!IA.ExtraCheckArgs.empty())
+ Suffix = (Twine(", ") + IA.ExtraCheckArgs).str();
+ SemaChecks.push_back((Twine("SemaBuiltinConstantArg") +
+ IA.ExtraCheckType + "(TheCall, " + Index +
+ Suffix + ")")
+ .str());
+ }
+ }
+ if (SemaChecks.empty())
+ return "";
+ return (Twine(" return ") +
+ join(std::begin(SemaChecks), std::end(SemaChecks),
+ " ||\n ") +
+ ";\n")
+ .str();
+ }
+
+ ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param);
+};
+
+// -----------------------------------------------------------------------------
+// The top-level class that holds all the state from analyzing the entire
+// Tablegen input.
+
+class MveEmitter {
+ // MveEmitter holds a collection of all the types we've instantiated.
+ VoidType Void;
+ std::map<std::string, std::unique_ptr<ScalarType>> ScalarTypes;
+ std::map<std::tuple<ScalarTypeKind, unsigned, unsigned>,
+ std::unique_ptr<VectorType>>
+ VectorTypes;
+ std::map<std::pair<std::string, unsigned>, std::unique_ptr<MultiVectorType>>
+ MultiVectorTypes;
+ std::map<unsigned, std::unique_ptr<PredicateType>> PredicateTypes;
+ std::map<std::string, std::unique_ptr<PointerType>> PointerTypes;
+
+ // And all the ACLEIntrinsic instances we've created.
+ std::map<std::string, std::unique_ptr<ACLEIntrinsic>> ACLEIntrinsics;
+
+public:
+ // Methods to create a Type object, or return the right existing one from the
+ // maps stored in this object.
+ const VoidType *getVoidType() { return &Void; }
+ const ScalarType *getScalarType(StringRef Name) {
+ return ScalarTypes[Name].get();
+ }
+ const ScalarType *getScalarType(Record *R) {
+ return getScalarType(R->getName());
+ }
+ const VectorType *getVectorType(const ScalarType *ST, unsigned Lanes) {
+ std::tuple<ScalarTypeKind, unsigned, unsigned> key(ST->kind(),
+ ST->sizeInBits(), Lanes);
+ if (VectorTypes.find(key) == VectorTypes.end())
+ VectorTypes[key] = std::make_unique<VectorType>(ST, Lanes);
+ return VectorTypes[key].get();
+ }
+ const VectorType *getVectorType(const ScalarType *ST) {
+ return getVectorType(ST, 128 / ST->sizeInBits());
+ }
+ const MultiVectorType *getMultiVectorType(unsigned Registers,
+ const VectorType *VT) {
+ std::pair<std::string, unsigned> key(VT->cNameBase(), Registers);
+ if (MultiVectorTypes.find(key) == MultiVectorTypes.end())
+ MultiVectorTypes[key] = std::make_unique<MultiVectorType>(Registers, VT);
+ return MultiVectorTypes[key].get();
+ }
+ const PredicateType *getPredicateType(unsigned Lanes) {
+ unsigned key = Lanes;
+ if (PredicateTypes.find(key) == PredicateTypes.end())
+ PredicateTypes[key] = std::make_unique<PredicateType>(Lanes);
+ return PredicateTypes[key].get();
+ }
+ const PointerType *getPointerType(const Type *T, bool Const) {
+ PointerType PT(T, Const);
+ std::string key = PT.cName();
+ if (PointerTypes.find(key) == PointerTypes.end())
+ PointerTypes[key] = std::make_unique<PointerType>(PT);
+ return PointerTypes[key].get();
+ }
+
+ // Methods to construct a type from various pieces of Tablegen. These are
+ // always called in the context of setting up a particular ACLEIntrinsic, so
+ // there's always an ambient parameter type (because we're iterating through
+ // the Params list in the Tablegen record for the intrinsic), which is used
+ // to expand Tablegen classes like 'Vector' which mean something different in
+ // each member of a parametric family.
+ const Type *getType(Record *R, const Type *Param);
+ const Type *getType(DagInit *D, const Type *Param);
+ const Type *getType(Init *I, const Type *Param);
+
+ // Functions that translate the Tablegen representation of an intrinsic's
+ // code generation into a collection of Value objects (which will then be
+ // reprocessed to read out the actual C++ code included by CGBuiltin.cpp).
+ Result::Ptr getCodeForDag(DagInit *D, const Result::Scope &Scope,
+ const Type *Param);
+ Result::Ptr getCodeForDagArg(DagInit *D, unsigned ArgNum,
+ const Result::Scope &Scope, const Type *Param);
+ Result::Ptr getCodeForArg(unsigned ArgNum, const Type *ArgType, bool Promote,
+ bool Immediate);
+
+ // Constructor and top-level functions.
+
+ MveEmitter(RecordKeeper &Records);
+
+ void EmitHeader(raw_ostream &OS);
+ void EmitBuiltinDef(raw_ostream &OS);
+ void EmitBuiltinSema(raw_ostream &OS);
+ void EmitBuiltinCG(raw_ostream &OS);
+ void EmitBuiltinAliases(raw_ostream &OS);
+};
+
+const Type *MveEmitter::getType(Init *I, const Type *Param) {
+ if (auto Dag = dyn_cast<DagInit>(I))
+ return getType(Dag, Param);
+ if (auto Def = dyn_cast<DefInit>(I))
+ return getType(Def->getDef(), Param);
+
+ PrintFatalError("Could not convert this value into a type");
+}
+
+const Type *MveEmitter::getType(Record *R, const Type *Param) {
+ // Pass to a subfield of any wrapper records. We don't expect more than one
+ // of these: immediate operands are used as plain numbers rather than as
+ // llvm::Value, so it's meaningless to promote their type anyway.
+ if (R->isSubClassOf("Immediate"))
+ R = R->getValueAsDef("type");
+ else if (R->isSubClassOf("unpromoted"))
+ R = R->getValueAsDef("underlying_type");
+
+ if (R->getName() == "Void")
+ return getVoidType();
+ if (R->isSubClassOf("PrimitiveType"))
+ return getScalarType(R);
+ if (R->isSubClassOf("ComplexType"))
+ return getType(R->getValueAsDag("spec"), Param);
+
+ PrintFatalError(R->getLoc(), "Could not convert this record into a type");
+}
+
+const Type *MveEmitter::getType(DagInit *D, const Type *Param) {
+ // The meat of the getType system: types in the Tablegen are represented by a
+ // dag whose operators select sub-cases of this function.
+
+ Record *Op = cast<DefInit>(D->getOperator())->getDef();
+ if (!Op->isSubClassOf("ComplexTypeOp"))
+ PrintFatalError(
+ "Expected ComplexTypeOp as dag operator in type expression");
+
+ if (Op->getName() == "CTO_Parameter") {
+ if (isa<VoidType>(Param))
+ PrintFatalError("Parametric type in unparametrised context");
+ return Param;
+ }
+
+ if (Op->getName() == "CTO_Vec") {
+ const Type *Element = getType(D->getArg(0), Param);
+ if (D->getNumArgs() == 1) {
+ return getVectorType(cast<ScalarType>(Element));
+ } else {
+ const Type *ExistingVector = getType(D->getArg(1), Param);
+ return getVectorType(cast<ScalarType>(Element),
+ cast<VectorType>(ExistingVector)->lanes());
+ }
+ }
+
+ if (Op->getName() == "CTO_Pred") {
+ const Type *Element = getType(D->getArg(0), Param);
+ return getPredicateType(128 / Element->sizeInBits());
+ }
+
+ if (Op->isSubClassOf("CTO_Tuple")) {
+ unsigned Registers = Op->getValueAsInt("n");
+ const Type *Element = getType(D->getArg(0), Param);
+ return getMultiVectorType(Registers, cast<VectorType>(Element));
+ }
+
+ if (Op->isSubClassOf("CTO_Pointer")) {
+ const Type *Pointee = getType(D->getArg(0), Param);
+ return getPointerType(Pointee, Op->getValueAsBit("const"));
+ }
+
+ if (Op->getName() == "CTO_CopyKind") {
+ const ScalarType *STSize = cast<ScalarType>(getType(D->getArg(0), Param));
+ const ScalarType *STKind = cast<ScalarType>(getType(D->getArg(1), Param));
+ for (const auto &kv : ScalarTypes) {
+ const ScalarType *RT = kv.second.get();
+ if (RT->kind() == STKind->kind() && RT->sizeInBits() == STSize->sizeInBits())
+ return RT;
+ }
+ PrintFatalError("Cannot find a type to satisfy CopyKind");
+ }
+
+ if (Op->isSubClassOf("CTO_ScaleSize")) {
+ const ScalarType *STKind = cast<ScalarType>(getType(D->getArg(0), Param));
+ int Num = Op->getValueAsInt("num"), Denom = Op->getValueAsInt("denom");
+ unsigned DesiredSize = STKind->sizeInBits() * Num / Denom;
+ for (const auto &kv : ScalarTypes) {
+ const ScalarType *RT = kv.second.get();
+ if (RT->kind() == STKind->kind() && RT->sizeInBits() == DesiredSize)
+ return RT;
+ }
+ PrintFatalError("Cannot find a type to satisfy ScaleSize");
+ }
+
+ PrintFatalError("Bad operator in type dag expression");
+}
+
+Result::Ptr MveEmitter::getCodeForDag(DagInit *D, const Result::Scope &Scope,
+ const Type *Param) {
+ Record *Op = cast<DefInit>(D->getOperator())->getDef();
+
+ if (Op->getName() == "seq") {
+ Result::Scope SubScope = Scope;
+ Result::Ptr PrevV = nullptr;
+ for (unsigned i = 0, e = D->getNumArgs(); i < e; ++i) {
+ // We don't use getCodeForDagArg here, because the argument name
+ // has different semantics in a seq
+ Result::Ptr V =
+ getCodeForDag(cast<DagInit>(D->getArg(i)), SubScope, Param);
+ StringRef ArgName = D->getArgNameStr(i);
+ if (!ArgName.empty())
+ SubScope[ArgName] = V;
+ if (PrevV)
+ V->setPredecessor(PrevV);
+ PrevV = V;
+ }
+ return PrevV;
+ } else if (Op->isSubClassOf("Type")) {
+ if (D->getNumArgs() != 1)
+ PrintFatalError("Type casts should have exactly one argument");
+ const Type *CastType = getType(Op, Param);
+ Result::Ptr Arg = getCodeForDagArg(D, 0, Scope, Param);
+ if (const auto *ST = dyn_cast<ScalarType>(CastType)) {
+ if (!ST->requiresFloat()) {
+ if (Arg->hasIntegerConstantValue())
+ return std::make_shared<IntLiteralResult>(
+ ST, Arg->integerConstantValue());
+ else
+ return std::make_shared<IntCastResult>(ST, Arg);
+ }
+ } else if (const auto *PT = dyn_cast<PointerType>(CastType)) {
+ return std::make_shared<PointerCastResult>(PT, Arg);
+ }
+ PrintFatalError("Unsupported type cast");
+ } else if (Op->getName() == "address") {
+ if (D->getNumArgs() != 2)
+ PrintFatalError("'address' should have two arguments");
+ Result::Ptr Arg = getCodeForDagArg(D, 0, Scope, Param);
+ unsigned Alignment;
+ if (auto *II = dyn_cast<IntInit>(D->getArg(1))) {
+ Alignment = II->getValue();
+ } else {
+ PrintFatalError("'address' alignment argument should be an integer");
+ }
+ return std::make_shared<AddressResult>(Arg, Alignment);
+ } else if (Op->getName() == "unsignedflag") {
+ if (D->getNumArgs() != 1)
+ PrintFatalError("unsignedflag should have exactly one argument");
+ Record *TypeRec = cast<DefInit>(D->getArg(0))->getDef();
+ if (!TypeRec->isSubClassOf("Type"))
+ PrintFatalError("unsignedflag's argument should be a type");
+ if (const auto *ST = dyn_cast<ScalarType>(getType(TypeRec, Param))) {
+ return std::make_shared<IntLiteralResult>(
+ getScalarType("u32"), ST->kind() == ScalarTypeKind::UnsignedInt);
+ } else {
+ PrintFatalError("unsignedflag's argument should be a scalar type");
+ }
+ } else {
+ std::vector<Result::Ptr> Args;
+ for (unsigned i = 0, e = D->getNumArgs(); i < e; ++i)
+ Args.push_back(getCodeForDagArg(D, i, Scope, Param));
+ if (Op->isSubClassOf("IRBuilderBase")) {
+ std::set<unsigned> AddressArgs;
+ std::map<unsigned, std::string> IntegerArgs;
+ for (Record *sp : Op->getValueAsListOfDefs("special_params")) {
+ unsigned Index = sp->getValueAsInt("index");
+ if (sp->isSubClassOf("IRBuilderAddrParam")) {
+ AddressArgs.insert(Index);
+ } else if (sp->isSubClassOf("IRBuilderIntParam")) {
+ IntegerArgs[Index] = sp->getValueAsString("type");
+ }
+ }
+ return std::make_shared<IRBuilderResult>(Op->getValueAsString("prefix"),
+ Args, AddressArgs, IntegerArgs);
+ } else if (Op->isSubClassOf("IRIntBase")) {
+ std::vector<const Type *> ParamTypes;
+ for (Record *RParam : Op->getValueAsListOfDefs("params"))
+ ParamTypes.push_back(getType(RParam, Param));
+ std::string IntName = Op->getValueAsString("intname");
+ if (Op->getValueAsBit("appendKind"))
+ IntName += "_" + toLetter(cast<ScalarType>(Param)->kind());
+ return std::make_shared<IRIntrinsicResult>(IntName, ParamTypes, Args);
+ } else {
+ PrintFatalError("Unsupported dag node " + Op->getName());
+ }
+ }
+}
+
+Result::Ptr MveEmitter::getCodeForDagArg(DagInit *D, unsigned ArgNum,
+ const Result::Scope &Scope,
+ const Type *Param) {
+ Init *Arg = D->getArg(ArgNum);
+ StringRef Name = D->getArgNameStr(ArgNum);
+
+ if (!Name.empty()) {
+ if (!isa<UnsetInit>(Arg))
+ PrintFatalError(
+ "dag operator argument should not have both a value and a name");
+ auto it = Scope.find(Name);
+ if (it == Scope.end())
+ PrintFatalError("unrecognized variable name '" + Name + "'");
+ return it->second;
+ }
+
+ if (auto *II = dyn_cast<IntInit>(Arg))
+ return std::make_shared<IntLiteralResult>(getScalarType("u32"),
+ II->getValue());
+
+ if (auto *DI = dyn_cast<DagInit>(Arg))
+ return getCodeForDag(DI, Scope, Param);
+
+ if (auto *DI = dyn_cast<DefInit>(Arg)) {
+ Record *Rec = DI->getDef();
+ if (Rec->isSubClassOf("Type")) {
+ const Type *T = getType(Rec, Param);
+ return std::make_shared<TypeResult>(T);
+ }
+ }
+
+ PrintFatalError("bad dag argument type for code generation");
+}
+
+Result::Ptr MveEmitter::getCodeForArg(unsigned ArgNum, const Type *ArgType,
+ bool Promote, bool Immediate) {
+ Result::Ptr V = std::make_shared<BuiltinArgResult>(
+ ArgNum, isa<PointerType>(ArgType), Immediate);
+
+ if (Promote) {
+ if (const auto *ST = dyn_cast<ScalarType>(ArgType)) {
+ if (ST->isInteger() && ST->sizeInBits() < 32)
+ V = std::make_shared<IntCastResult>(getScalarType("u32"), V);
+ } else if (const auto *PT = dyn_cast<PredicateType>(ArgType)) {
+ V = std::make_shared<IntCastResult>(getScalarType("u32"), V);
+ V = std::make_shared<IRIntrinsicResult>("arm_mve_pred_i2v",
+ std::vector<const Type *>{PT},
+ std::vector<Result::Ptr>{V});
+ }
+ }
+
+ return V;
+}
+
+ACLEIntrinsic::ACLEIntrinsic(MveEmitter &ME, Record *R, const Type *Param)
+ : ReturnType(ME.getType(R->getValueAsDef("ret"), Param)) {
+ // Derive the intrinsic's full name, by taking the name of the
+ // Tablegen record (or override) and appending the suffix from its
+ // parameter type. (If the intrinsic is unparametrised, its
+ // parameter type will be given as Void, which returns the empty
+ // string for acleSuffix.)
+ StringRef BaseName =
+ (R->isSubClassOf("NameOverride") ? R->getValueAsString("basename")
+ : R->getName());
+ StringRef overrideLetter = R->getValueAsString("overrideKindLetter");
+ FullName = (Twine(BaseName) + Param->acleSuffix(overrideLetter)).str();
+
+ // Derive the intrinsic's polymorphic name, by removing components from the
+ // full name as specified by its 'pnt' member ('polymorphic name type'),
+ // which indicates how many type suffixes to remove, and any other piece of
+ // the name that should be removed.
+ Record *PolymorphicNameType = R->getValueAsDef("pnt");
+ SmallVector<StringRef, 8> NameParts;
+ StringRef(FullName).split(NameParts, '_');
+ for (unsigned i = 0, e = PolymorphicNameType->getValueAsInt(
+ "NumTypeSuffixesToDiscard");
+ i < e; ++i)
+ NameParts.pop_back();
+ if (!PolymorphicNameType->isValueUnset("ExtraSuffixToDiscard")) {
+ StringRef ExtraSuffix =
+ PolymorphicNameType->getValueAsString("ExtraSuffixToDiscard");
+ auto it = NameParts.end();
+ while (it != NameParts.begin()) {
+ --it;
+ if (*it == ExtraSuffix) {
+ NameParts.erase(it);
+ break;
+ }
+ }
+ }
+ ShortName = join(std::begin(NameParts), std::end(NameParts), "_");
+
+ PolymorphicOnly = R->getValueAsBit("polymorphicOnly");
+ NonEvaluating = R->getValueAsBit("nonEvaluating");
+
+ // Process the intrinsic's argument list.
+ DagInit *ArgsDag = R->getValueAsDag("args");
+ Result::Scope Scope;
+ for (unsigned i = 0, e = ArgsDag->getNumArgs(); i < e; ++i) {
+ Init *TypeInit = ArgsDag->getArg(i);
+
+ bool Promote = true;
+ if (auto TypeDI = dyn_cast<DefInit>(TypeInit))
+ if (TypeDI->getDef()->isSubClassOf("unpromoted"))
+ Promote = false;
+
+ // Work out the type of the argument, for use in the function prototype in
+ // the header file.
+ const Type *ArgType = ME.getType(TypeInit, Param);
+ ArgTypes.push_back(ArgType);
+
+ // If the argument is a subclass of Immediate, record the details about
+ // what values it can take, for Sema checking.
+ bool Immediate = false;
+ if (auto TypeDI = dyn_cast<DefInit>(TypeInit)) {
+ Record *TypeRec = TypeDI->getDef();
+ if (TypeRec->isSubClassOf("Immediate")) {
+ Immediate = true;
+
+ Record *Bounds = TypeRec->getValueAsDef("bounds");
+ ImmediateArg &IA = ImmediateArgs[i];
+ if (Bounds->isSubClassOf("IB_ConstRange")) {
+ IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;
+ IA.i1 = Bounds->getValueAsInt("lo");
+ IA.i2 = Bounds->getValueAsInt("hi");
+ } else if (Bounds->getName() == "IB_UEltValue") {
+ IA.boundsType = ImmediateArg::BoundsType::UInt;
+ IA.i1 = Param->sizeInBits();
+ } else if (Bounds->getName() == "IB_LaneIndex") {
+ IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;
+ IA.i1 = 0;
+ IA.i2 = 128 / Param->sizeInBits() - 1;
+ } else if (Bounds->isSubClassOf("IB_EltBit")) {
+ IA.boundsType = ImmediateArg::BoundsType::ExplicitRange;
+ IA.i1 = Bounds->getValueAsInt("base");
+ const Type *T = ME.getType(Bounds->getValueAsDef("type"), Param);
+ IA.i2 = IA.i1 + T->sizeInBits() - 1;
+ } else {
+ PrintFatalError("unrecognised ImmediateBounds subclass");
+ }
+
+ IA.ArgType = ArgType;
+
+ if (!TypeRec->isValueUnset("extra")) {
+ IA.ExtraCheckType = TypeRec->getValueAsString("extra");
+ if (!TypeRec->isValueUnset("extraarg"))
+ IA.ExtraCheckArgs = TypeRec->getValueAsString("extraarg");
+ }
+ }
+ }
+
+ // The argument will usually have a name in the arguments dag, which goes
+ // into the variable-name scope that the code gen will refer to.
+ StringRef ArgName = ArgsDag->getArgNameStr(i);
+ if (!ArgName.empty())
+ Scope[ArgName] = ME.getCodeForArg(i, ArgType, Promote, Immediate);
+ }
+
+ // Finally, go through the codegen dag and translate it into a Result object
+ // (with an arbitrary DAG of depended-on Results hanging off it).
+ DagInit *CodeDag = R->getValueAsDag("codegen");
+ Record *MainOp = cast<DefInit>(CodeDag->getOperator())->getDef();
+ if (MainOp->isSubClassOf("CustomCodegen")) {
+ // Or, if it's the special case of CustomCodegen, just accumulate
+ // a list of parameters we're going to assign to variables before
+ // breaking from the loop.
+ CustomCodeGenArgs["CustomCodeGenType"] =
+ (Twine("CustomCodeGen::") + MainOp->getValueAsString("type")).str();
+ for (unsigned i = 0, e = CodeDag->getNumArgs(); i < e; ++i) {
+ StringRef Name = CodeDag->getArgNameStr(i);
+ if (Name.empty()) {
+ PrintFatalError("Operands to CustomCodegen should have names");
+ } else if (auto *II = dyn_cast<IntInit>(CodeDag->getArg(i))) {
+ CustomCodeGenArgs[Name] = itostr(II->getValue());
+ } else if (auto *SI = dyn_cast<StringInit>(CodeDag->getArg(i))) {
+ CustomCodeGenArgs[Name] = SI->getValue();
+ } else {
+ PrintFatalError("Operands to CustomCodegen should be integers");
+ }
+ }
+ } else {
+ Code = ME.getCodeForDag(CodeDag, Scope, Param);
+ }
+}
+
+MveEmitter::MveEmitter(RecordKeeper &Records) {
+ // Construct the whole MveEmitter.
+
+ // First, look up all the instances of PrimitiveType. This gives us the list
+ // of vector typedefs we have to put in arm_mve.h, and also allows us to
+ // collect all the useful ScalarType instances into a big list so that we can
+ // use it for operations such as 'find the unsigned version of this signed
+ // integer type'.
+ for (Record *R : Records.getAllDerivedDefinitions("PrimitiveType"))
+ ScalarTypes[R->getName()] = std::make_unique<ScalarType>(R);
+
+ // Now go through the instances of Intrinsic, and for each one, iterate
+ // through its list of type parameters making an ACLEIntrinsic for each one.
+ for (Record *R : Records.getAllDerivedDefinitions("Intrinsic")) {
+ for (Record *RParam : R->getValueAsListOfDefs("params")) {
+ const Type *Param = getType(RParam, getVoidType());
+ auto Intrinsic = std::make_unique<ACLEIntrinsic>(*this, R, Param);
+ ACLEIntrinsics[Intrinsic->fullName()] = std::move(Intrinsic);
+ }
+ }
+}
+
+/// A wrapper on raw_string_ostream that contains its own buffer rather than
+/// having to point it at one elsewhere. (In other words, it works just like
+/// std::ostringstream; also, this makes it convenient to declare a whole array
+/// of them at once.)
+///
+/// We have to set this up using multiple inheritance, to ensure that the
+/// string member has been constructed before raw_string_ostream's constructor
+/// is given a pointer to it.
+class string_holder {
+protected:
+ std::string S;
+};
+class raw_self_contained_string_ostream : private string_holder,
+ public raw_string_ostream {
+public:
+ raw_self_contained_string_ostream()
+ : string_holder(), raw_string_ostream(S) {}
+};
+
+void MveEmitter::EmitHeader(raw_ostream &OS) {
+ // Accumulate pieces of the header file that will be enabled under various
+ // different combinations of #ifdef. The index into parts[] is made up of
+ // the following bit flags.
+ constexpr unsigned Float = 1;
+ constexpr unsigned UseUserNamespace = 2;
+
+ constexpr unsigned NumParts = 4;
+ raw_self_contained_string_ostream parts[NumParts];
+
+ // Write typedefs for all the required vector types, and a few scalar
+ // types that don't already have the name we want them to have.
+
+ parts[0] << "typedef uint16_t mve_pred16_t;\n";
+ parts[Float] << "typedef __fp16 float16_t;\n"
+ "typedef float float32_t;\n";
+ for (const auto &kv : ScalarTypes) {
+ const ScalarType *ST = kv.second.get();
+ if (ST->hasNonstandardName())
+ continue;
+ raw_ostream &OS = parts[ST->requiresFloat() ? Float : 0];
+ const VectorType *VT = getVectorType(ST);
+
+ OS << "typedef __attribute__((neon_vector_type(" << VT->lanes() << "))) "
+ << ST->cName() << " " << VT->cName() << ";\n";
+
+ // Every vector type also comes with a pair of multi-vector types for
+ // the VLD2 and VLD4 instructions.
+ for (unsigned n = 2; n <= 4; n += 2) {
+ const MultiVectorType *MT = getMultiVectorType(n, VT);
+ OS << "typedef struct { " << VT->cName() << " val[" << n << "]; } "
+ << MT->cName() << ";\n";
+ }
+ }
+ parts[0] << "\n";
+ parts[Float] << "\n";
+
+ // Write declarations for all the intrinsics.
+
+ for (const auto &kv : ACLEIntrinsics) {
+ const ACLEIntrinsic &Int = *kv.second;
+
+ // We generate each intrinsic twice, under its full unambiguous
+ // name and its shorter polymorphic name (if the latter exists).
+ for (bool Polymorphic : {false, true}) {
+ if (Polymorphic && !Int.polymorphic())
+ continue;
+ if (!Polymorphic && Int.polymorphicOnly())
+ continue;
+
+ // We also generate each intrinsic under a name like __arm_vfooq
+ // (which is in C language implementation namespace, so it's
+ // safe to define in any conforming user program) and a shorter
+ // one like vfooq (which is in user namespace, so a user might
+ // reasonably have used it for something already). If so, they
+ // can #define __ARM_MVE_PRESERVE_USER_NAMESPACE before
+ // including the header, which will suppress the shorter names
+ // and leave only the implementation-namespace ones. Then they
+ // have to write __arm_vfooq everywhere, of course.
+
+ for (bool UserNamespace : {false, true}) {
+ raw_ostream &OS = parts[(Int.requiresFloat() ? Float : 0) |
+ (UserNamespace ? UseUserNamespace : 0)];
+
+ // Make the name of the function in this declaration.
+
+ std::string FunctionName =
+ Polymorphic ? Int.shortName() : Int.fullName();
+ if (!UserNamespace)
+ FunctionName = "__arm_" + FunctionName;
+
+ // Make strings for the types involved in the function's
+ // prototype.
+
+ std::string RetTypeName = Int.returnType()->cName();
+ if (!StringRef(RetTypeName).endswith("*"))
+ RetTypeName += " ";
+
+ std::vector<std::string> ArgTypeNames;
+ for (const Type *ArgTypePtr : Int.argTypes())
+ ArgTypeNames.push_back(ArgTypePtr->cName());
+ std::string ArgTypesString =
+ join(std::begin(ArgTypeNames), std::end(ArgTypeNames), ", ");
+
+ // Emit the actual declaration. All these functions are
+ // declared 'static inline' without a body, which is fine
+ // provided clang recognizes them as builtins, and has the
+ // effect that this type signature is used in place of the one
+ // that Builtins.def didn't provide. That's how we can get
+ // structure types that weren't defined until this header was
+ // included to be part of the type signature of a builtin that
+ // was known to clang already.
+ //
+ // The declarations use __attribute__(__clang_arm_mve_alias),
+ // so that each function declared will be recognized as the
+ // appropriate MVE builtin in spite of its user-facing name.
+ //
+ // (That's better than making them all wrapper functions,
+ // partly because it avoids any compiler error message citing
+ // the wrapper function definition instead of the user's code,
+ // and mostly because some MVE intrinsics have arguments
+ // required to be compile-time constants, and that property
+ // can't be propagated through a wrapper function. It can be
+ // propagated through a macro, but macros can't be overloaded
+ // on argument types very easily - you have to use _Generic,
+ // which makes error messages very confusing when the user
+ // gets it wrong.)
+ //
+ // Finally, the polymorphic versions of the intrinsics are
+ // also defined with __attribute__(overloadable), so that when
+ // the same name is defined with several type signatures, the
+ // right thing happens. Each one of the overloaded
+ // declarations is given a different builtin id, which
+ // has exactly the effect we want: first clang resolves the
+ // overload to the right function, then it knows which builtin
+ // it's referring to, and then the Sema checking for that
+ // builtin can check further things like the constant
+ // arguments.
+ //
+ // One more subtlety is the newline just before the return
+ // type name. That's a cosmetic tweak to make the error
+ // messages legible if the user gets the types wrong in a call
+ // to a polymorphic function: this way, clang will print just
+ // the _final_ line of each declaration in the header, to show
+ // the type signatures that would have been legal. So all the
+ // confusing machinery with __attribute__ is left out of the
+ // error message, and the user sees something that's more or
+ // less self-documenting: "here's a list of actually readable
+ // type signatures for vfooq(), and here's why each one didn't
+ // match your call".
+
+ OS << "static __inline__ __attribute__(("
+ << (Polymorphic ? "overloadable, " : "")
+ << "__clang_arm_mve_alias(__builtin_arm_mve_" << Int.fullName()
+ << ")))\n"
+ << RetTypeName << FunctionName << "(" << ArgTypesString << ");\n";
+ }
+ }
+ }
+ for (auto &part : parts)
+ part << "\n";
+
+ // Now we've finished accumulating bits and pieces into the parts[] array.
+ // Put it all together to write the final output file.
+
+ OS << "/*===---- arm_mve.h - ARM MVE intrinsics "
+ "-----------------------------------===\n"
+ " *\n"
+ " *\n"
+ " * Part of the LLVM Project, under the Apache License v2.0 with LLVM "
+ "Exceptions.\n"
+ " * See https://llvm.org/LICENSE.txt for license information.\n"
+ " * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
+ " *\n"
+ " *===-------------------------------------------------------------"
+ "----"
+ "------===\n"
+ " */\n"
+ "\n"
+ "#ifndef __ARM_MVE_H\n"
+ "#define __ARM_MVE_H\n"
+ "\n"
+ "#if !__ARM_FEATURE_MVE\n"
+ "#error \"MVE support not enabled\"\n"
+ "#endif\n"
+ "\n"
+ "#include <stdint.h>\n"
+ "\n";
+
+ for (size_t i = 0; i < NumParts; ++i) {
+ std::vector<std::string> conditions;
+ if (i & Float)
+ conditions.push_back("(__ARM_FEATURE_MVE & 2)");
+ if (i & UseUserNamespace)
+ conditions.push_back("(!defined __ARM_MVE_PRESERVE_USER_NAMESPACE)");
+
+ std::string condition =
+ join(std::begin(conditions), std::end(conditions), " && ");
+ if (!condition.empty())
+ OS << "#if " << condition << "\n\n";
+ OS << parts[i].str();
+ if (!condition.empty())
+ OS << "#endif /* " << condition << " */\n\n";
+ }
+
+ OS << "#endif /* __ARM_MVE_H */\n";
+}
+
+void MveEmitter::EmitBuiltinDef(raw_ostream &OS) {
+ for (const auto &kv : ACLEIntrinsics) {
+ const ACLEIntrinsic &Int = *kv.second;
+ OS << "TARGET_HEADER_BUILTIN(__builtin_arm_mve_" << Int.fullName()
+ << ", \"\", \"n\", \"arm_mve.h\", ALL_LANGUAGES, \"\")\n";
+ }
+
+ std::set<std::string> ShortNamesSeen;
+
+ for (const auto &kv : ACLEIntrinsics) {
+ const ACLEIntrinsic &Int = *kv.second;
+ if (Int.polymorphic()) {
+ StringRef Name = Int.shortName();
+ if (ShortNamesSeen.find(Name) == ShortNamesSeen.end()) {
+ OS << "BUILTIN(__builtin_arm_mve_" << Name << ", \"vi.\", \"nt";
+ if (Int.nonEvaluating())
+ OS << "u"; // indicate that this builtin doesn't evaluate its args
+ OS << "\")\n";
+ ShortNamesSeen.insert(Name);
+ }
+ }
+ }
+}
+
+void MveEmitter::EmitBuiltinSema(raw_ostream &OS) {
+ std::map<std::string, std::set<std::string>> Checks;
+
+ for (const auto &kv : ACLEIntrinsics) {
+ const ACLEIntrinsic &Int = *kv.second;
+ std::string Check = Int.genSema();
+ if (!Check.empty())
+ Checks[Check].insert(Int.fullName());
+ }
+
+ for (const auto &kv : Checks) {
+ for (StringRef Name : kv.second)
+ OS << "case ARM::BI__builtin_arm_mve_" << Name << ":\n";
+ OS << kv.first;
+ }
+}
+
+// Machinery for the grouping of intrinsics by similar codegen.
+//
+// The general setup is that 'MergeableGroup' stores the things that a set of
+// similarly shaped intrinsics have in common: the text of their code
+// generation, and the number and type of their parameter variables.
+// MergeableGroup is the key in a std::map whose value is a set of
+// OutputIntrinsic, which stores the ways in which a particular intrinsic
+// specializes the MergeableGroup's generic description: the function name and
+// the _values_ of the parameter variables.
+
+struct ComparableStringVector : std::vector<std::string> {
+ // Infrastructure: a derived class of vector<string> which comes with an
+ // ordering, so that it can be used as a key in maps and an element in sets.
+ // There's no requirement on the ordering beyond being deterministic.
+ bool operator<(const ComparableStringVector &rhs) const {
+ if (size() != rhs.size())
+ return size() < rhs.size();
+ for (size_t i = 0, e = size(); i < e; ++i)
+ if ((*this)[i] != rhs[i])
+ return (*this)[i] < rhs[i];
+ return false;
+ }
+};
+
+struct OutputIntrinsic {
+ const ACLEIntrinsic *Int;
+ std::string Name;
+ ComparableStringVector ParamValues;
+ bool operator<(const OutputIntrinsic &rhs) const {
+ if (Name != rhs.Name)
+ return Name < rhs.Name;
+ return ParamValues < rhs.ParamValues;
+ }
+};
+struct MergeableGroup {
+ std::string Code;
+ ComparableStringVector ParamTypes;
+ bool operator<(const MergeableGroup &rhs) const {
+ if (Code != rhs.Code)
+ return Code < rhs.Code;
+ return ParamTypes < rhs.ParamTypes;
+ }
+};
+
+void MveEmitter::EmitBuiltinCG(raw_ostream &OS) {
+ // Pass 1: generate code for all the intrinsics as if every type or constant
+ // that can possibly be abstracted out into a parameter variable will be.
+ // This identifies the sets of intrinsics we'll group together into a single
+ // piece of code generation.
+
+ std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroupsPrelim;
+
+ for (const auto &kv : ACLEIntrinsics) {
+ const ACLEIntrinsic &Int = *kv.second;
+
+ MergeableGroup MG;
+ OutputIntrinsic OI;
+
+ OI.Int = &Int;
+ OI.Name = Int.fullName();
+ CodeGenParamAllocator ParamAllocPrelim{&MG.ParamTypes, &OI.ParamValues};
+ raw_string_ostream OS(MG.Code);
+ Int.genCode(OS, ParamAllocPrelim, 1);
+ OS.flush();
+
+ MergeableGroupsPrelim[MG].insert(OI);
+ }
+
+ // Pass 2: for each of those groups, optimize the parameter variable set by
+ // eliminating 'parameters' that are the same for all intrinsics in the
+ // group, and merging together pairs of parameter variables that take the
+ // same values as each other for all intrinsics in the group.
+
+ std::map<MergeableGroup, std::set<OutputIntrinsic>> MergeableGroups;
+
+ for (const auto &kv : MergeableGroupsPrelim) {
+ const MergeableGroup &MG = kv.first;
+ std::vector<int> ParamNumbers;
+ std::map<ComparableStringVector, int> ParamNumberMap;
+
+ // Loop over the parameters for this group.
+ for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) {
+ // Is this parameter the same for all intrinsics in the group?
+ const OutputIntrinsic &OI_first = *kv.second.begin();
+ bool Constant = all_of(kv.second, [&](const OutputIntrinsic &OI) {
+ return OI.ParamValues[i] == OI_first.ParamValues[i];
+ });
+
+ // If so, record it as -1, meaning 'no parameter variable needed'. Then
+ // the corresponding call to allocParam in pass 2 will not generate a
+ // variable at all, and just use the value inline.
+ if (Constant) {
+ ParamNumbers.push_back(-1);
+ continue;
+ }
+
+ // Otherwise, make a list of the values this parameter takes for each
+ // intrinsic, and see if that value vector matches anything we already
+ // have. We also record the parameter type, so that we don't accidentally
+ // match up two parameter variables with different types. (Not that
+ // there's much chance of them having textually equivalent values, but in
+ // _principle_ it could happen.)
+ ComparableStringVector key;
+ key.push_back(MG.ParamTypes[i]);
+ for (const auto &OI : kv.second)
+ key.push_back(OI.ParamValues[i]);
+
+ auto Found = ParamNumberMap.find(key);
+ if (Found != ParamNumberMap.end()) {
+ // Yes, an existing parameter variable can be reused for this.
+ ParamNumbers.push_back(Found->second);
+ continue;
+ }
+
+ // No, we need a new parameter variable.
+ int ExistingIndex = ParamNumberMap.size();
+ ParamNumberMap[key] = ExistingIndex;
+ ParamNumbers.push_back(ExistingIndex);
+ }
+
+ // Now we're ready to do the pass 2 code generation, which will emit the
+ // reduced set of parameter variables we've just worked out.
+
+ for (const auto &OI_prelim : kv.second) {
+ const ACLEIntrinsic *Int = OI_prelim.Int;
+
+ MergeableGroup MG;
+ OutputIntrinsic OI;
+
+ OI.Int = OI_prelim.Int;
+ OI.Name = OI_prelim.Name;
+ CodeGenParamAllocator ParamAlloc{&MG.ParamTypes, &OI.ParamValues,
+ &ParamNumbers};
+ raw_string_ostream OS(MG.Code);
+ Int->genCode(OS, ParamAlloc, 2);
+ OS.flush();
+
+ MergeableGroups[MG].insert(OI);
+ }
+ }
+
+ // Output the actual C++ code.
+
+ for (const auto &kv : MergeableGroups) {
+ const MergeableGroup &MG = kv.first;
+
+ // List of case statements in the main switch on BuiltinID, and an open
+ // brace.
+ const char *prefix = "";
+ for (const auto &OI : kv.second) {
+ OS << prefix << "case ARM::BI__builtin_arm_mve_" << OI.Name << ":";
+ prefix = "\n";
+ }
+ OS << " {\n";
+
+ if (!MG.ParamTypes.empty()) {
+ // If we've got some parameter variables, then emit their declarations...
+ for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i) {
+ StringRef Type = MG.ParamTypes[i];
+ OS << " " << Type;
+ if (!Type.endswith("*"))
+ OS << " ";
+ OS << " Param" << utostr(i) << ";\n";
+ }
+
+ // ... and an inner switch on BuiltinID that will fill them in with each
+ // individual intrinsic's values.
+ OS << " switch (BuiltinID) {\n";
+ for (const auto &OI : kv.second) {
+ OS << " case ARM::BI__builtin_arm_mve_" << OI.Name << ":\n";
+ for (size_t i = 0, e = MG.ParamTypes.size(); i < e; ++i)
+ OS << " Param" << utostr(i) << " = " << OI.ParamValues[i] << ";\n";
+ OS << " break;\n";
+ }
+ OS << " }\n";
+ }
+
+ // And finally, output the code, and close the outer pair of braces. (The
+ // code will always end with a 'return' statement, so we need not insert a
+ // 'break' here.)
+ OS << MG.Code << "}\n";
+ }
+}
+
+void MveEmitter::EmitBuiltinAliases(raw_ostream &OS) {
+ for (const auto &kv : ACLEIntrinsics) {
+ const ACLEIntrinsic &Int = *kv.second;
+ OS << "case ARM::BI__builtin_arm_mve_" << Int.fullName() << ":\n"
+ << " return AliasName == \"" << Int.fullName() << "\"";
+ if (Int.polymorphic())
+ OS << " || AliasName == \"" << Int.shortName() << "\"";
+ OS << ";\n";
+ }
+}
+
+} // namespace
+
+namespace clang {
+
+void EmitMveHeader(RecordKeeper &Records, raw_ostream &OS) {
+ MveEmitter(Records).EmitHeader(OS);
+}
+
+void EmitMveBuiltinDef(RecordKeeper &Records, raw_ostream &OS) {
+ MveEmitter(Records).EmitBuiltinDef(OS);
+}
+
+void EmitMveBuiltinSema(RecordKeeper &Records, raw_ostream &OS) {
+ MveEmitter(Records).EmitBuiltinSema(OS);
+}
+
+void EmitMveBuiltinCG(RecordKeeper &Records, raw_ostream &OS) {
+ MveEmitter(Records).EmitBuiltinCG(OS);
+}
+
+void EmitMveBuiltinAliases(RecordKeeper &Records, raw_ostream &OS) {
+ MveEmitter(Records).EmitBuiltinAliases(OS);
+}
+
+} // end namespace clang
diff --git a/clang/utils/TableGen/NeonEmitter.cpp b/clang/utils/TableGen/NeonEmitter.cpp
index 9d668a281534..a0f3fb2ddc08 100644
--- a/clang/utils/TableGen/NeonEmitter.cpp
+++ b/clang/utils/TableGen/NeonEmitter.cpp
@@ -140,7 +140,15 @@ class Type {
private:
TypeSpec TS;
- bool Float, Signed, Immediate, Void, Poly, Constant, Pointer;
+ enum TypeKind {
+ Void,
+ Float,
+ SInt,
+ UInt,
+ Poly,
+ };
+ TypeKind Kind;
+ bool Immediate, Constant, Pointer;
// ScalarForMangling and NoManglingQ are really not suited to live here as
// they are not related to the type. But they live in the TypeSpec (not the
// prototype), so this is really the only place to store them.
@@ -149,16 +157,15 @@ private:
public:
Type()
- : Float(false), Signed(false), Immediate(false), Void(true), Poly(false),
- Constant(false), Pointer(false), ScalarForMangling(false),
- NoManglingQ(false), Bitwidth(0), ElementBitwidth(0), NumVectors(0) {}
+ : Kind(Void), Immediate(false), Constant(false),
+ Pointer(false), ScalarForMangling(false), NoManglingQ(false),
+ Bitwidth(0), ElementBitwidth(0), NumVectors(0) {}
- Type(TypeSpec TS, char CharMod)
- : TS(std::move(TS)), Float(false), Signed(false), Immediate(false),
- Void(false), Poly(false), Constant(false), Pointer(false),
- ScalarForMangling(false), NoManglingQ(false), Bitwidth(0),
- ElementBitwidth(0), NumVectors(0) {
- applyModifier(CharMod);
+ Type(TypeSpec TS, StringRef CharMods)
+ : TS(std::move(TS)), Kind(Void), Immediate(false),
+ Constant(false), Pointer(false), ScalarForMangling(false),
+ NoManglingQ(false), Bitwidth(0), ElementBitwidth(0), NumVectors(0) {
+ applyModifiers(CharMods);
}
/// Returns a type representing "void".
@@ -174,21 +181,23 @@ public:
bool noManglingQ() const { return NoManglingQ; }
bool isPointer() const { return Pointer; }
- bool isFloating() const { return Float; }
- bool isInteger() const { return !Float && !Poly; }
- bool isSigned() const { return Signed; }
+ bool isValue() const { return !isVoid() && !isPointer(); }
+ bool isScalar() const { return isValue() && NumVectors == 0; }
+ bool isVector() const { return isValue() && NumVectors > 0; }
+ bool isConstPointer() const { return Constant; }
+ bool isFloating() const { return Kind == Float; }
+ bool isInteger() const { return Kind == SInt || Kind == UInt; }
+ bool isPoly() const { return Kind == Poly; }
+ bool isSigned() const { return Kind == SInt; }
bool isImmediate() const { return Immediate; }
- bool isScalar() const { return NumVectors == 0; }
- bool isVector() const { return NumVectors > 0; }
- bool isFloat() const { return Float && ElementBitwidth == 32; }
- bool isDouble() const { return Float && ElementBitwidth == 64; }
- bool isHalf() const { return Float && ElementBitwidth == 16; }
- bool isPoly() const { return Poly; }
+ bool isFloat() const { return isFloating() && ElementBitwidth == 32; }
+ bool isDouble() const { return isFloating() && ElementBitwidth == 64; }
+ bool isHalf() const { return isFloating() && ElementBitwidth == 16; }
bool isChar() const { return ElementBitwidth == 8; }
- bool isShort() const { return !Float && ElementBitwidth == 16; }
- bool isInt() const { return !Float && ElementBitwidth == 32; }
- bool isLong() const { return !Float && ElementBitwidth == 64; }
- bool isVoid() const { return Void; }
+ bool isShort() const { return isInteger() && ElementBitwidth == 16; }
+ bool isInt() const { return isInteger() && ElementBitwidth == 32; }
+ bool isLong() const { return isInteger() && ElementBitwidth == 64; }
+ bool isVoid() const { return Kind == Void; }
unsigned getNumElements() const { return Bitwidth / ElementBitwidth; }
unsigned getSizeInBits() const { return Bitwidth; }
unsigned getElementSizeInBits() const { return ElementBitwidth; }
@@ -197,21 +206,24 @@ public:
//
// Mutator functions
//
- void makeUnsigned() { Signed = false; }
- void makeSigned() { Signed = true; }
+ void makeUnsigned() {
+ assert(!isVoid() && "not a potentially signed type");
+ Kind = UInt;
+ }
+ void makeSigned() {
+ assert(!isVoid() && "not a potentially signed type");
+ Kind = SInt;
+ }
void makeInteger(unsigned ElemWidth, bool Sign) {
- Float = false;
- Poly = false;
- Signed = Sign;
+ assert(!isVoid() && "converting void to int probably not useful");
+ Kind = Sign ? SInt : UInt;
Immediate = false;
ElementBitwidth = ElemWidth;
}
void makeImmediate(unsigned ElemWidth) {
- Float = false;
- Poly = false;
- Signed = true;
+ Kind = SInt;
Immediate = true;
ElementBitwidth = ElemWidth;
}
@@ -257,8 +269,8 @@ private:
/// seen. This is needed by applyModifier as some modifiers
/// only take effect if the type size was changed by "Q" or "H".
void applyTypespec(bool &Quad);
- /// Applies a prototype modifier to the type.
- void applyModifier(char Mod);
+ /// Applies prototype modifiers to the type.
+ void applyModifiers(StringRef Mods);
};
//===----------------------------------------------------------------------===//
@@ -289,8 +301,8 @@ class Intrinsic {
/// The Record this intrinsic was created from.
Record *R;
- /// The unmangled name and prototype.
- std::string Name, Proto;
+ /// The unmangled name.
+ std::string Name;
/// The input and output typespecs. InTS == OutTS except when
/// CartesianProductOfTypes is 1 - this is the case for vreinterpret.
TypeSpec OutTS, InTS;
@@ -313,6 +325,8 @@ class Intrinsic {
/// The types of return value [0] and parameters [1..].
std::vector<Type> Types;
+ /// The index of the key type passed to CGBuiltin.cpp for polymorphic calls.
+ int PolymorphicKeyType;
/// The local variables defined.
std::map<std::string, Variable> Variables;
/// NeededEarly - set if any other intrinsic depends on this intrinsic.
@@ -348,34 +362,39 @@ public:
Intrinsic(Record *R, StringRef Name, StringRef Proto, TypeSpec OutTS,
TypeSpec InTS, ClassKind CK, ListInit *Body, NeonEmitter &Emitter,
StringRef Guard, bool IsUnavailable, bool BigEndianSafe)
- : R(R), Name(Name.str()), Proto(Proto.str()), OutTS(OutTS), InTS(InTS),
- CK(CK), Body(Body), Guard(Guard.str()), IsUnavailable(IsUnavailable),
- BigEndianSafe(BigEndianSafe), NeededEarly(false), UseMacro(false),
- BaseType(OutTS, 'd'), InBaseType(InTS, 'd'), Emitter(Emitter) {
- // If this builtin takes an immediate argument, we need to #define it rather
- // than use a standard declaration, so that SemaChecking can range check
- // the immediate passed by the user.
- if (Proto.find('i') != std::string::npos)
- UseMacro = true;
-
- // Pointer arguments need to use macros to avoid hiding aligned attributes
- // from the pointer type.
- if (Proto.find('p') != std::string::npos ||
- Proto.find('c') != std::string::npos)
- UseMacro = true;
-
- // It is not permitted to pass or return an __fp16 by value, so intrinsics
- // taking a scalar float16_t must be implemented as macros.
- if (OutTS.find('h') != std::string::npos &&
- Proto.find('s') != std::string::npos)
- UseMacro = true;
-
+ : R(R), Name(Name.str()), OutTS(OutTS), InTS(InTS), CK(CK), Body(Body),
+ Guard(Guard.str()), IsUnavailable(IsUnavailable),
+ BigEndianSafe(BigEndianSafe), PolymorphicKeyType(0), NeededEarly(false),
+ UseMacro(false), BaseType(OutTS, "."), InBaseType(InTS, "."),
+ Emitter(Emitter) {
// Modify the TypeSpec per-argument to get a concrete Type, and create
// known variables for each.
// Types[0] is the return value.
- Types.emplace_back(OutTS, Proto[0]);
- for (unsigned I = 1; I < Proto.size(); ++I)
- Types.emplace_back(InTS, Proto[I]);
+ unsigned Pos = 0;
+ Types.emplace_back(OutTS, getNextModifiers(Proto, Pos));
+ StringRef Mods = getNextModifiers(Proto, Pos);
+ while (!Mods.empty()) {
+ Types.emplace_back(InTS, Mods);
+ if (Mods.find("!") != StringRef::npos)
+ PolymorphicKeyType = Types.size() - 1;
+
+ Mods = getNextModifiers(Proto, Pos);
+ }
+
+ for (auto Type : Types) {
+ // If this builtin takes an immediate argument, we need to #define it rather
+ // than use a standard declaration, so that SemaChecking can range check
+ // the immediate passed by the user.
+
+ // Pointer arguments need to use macros to avoid hiding aligned attributes
+ // from the pointer type.
+
+ // It is not permitted to pass or return an __fp16 by value, so intrinsics
+ // taking a scalar float16_t must be implemented as macros.
+ if (Type.isImmediate() || Type.isPointer() ||
+ (Type.isScalar() && Type.isHalf()))
+ UseMacro = true;
+ }
}
/// Get the Record that this intrinsic is based off.
@@ -391,37 +410,26 @@ public:
/// Return true if the intrinsic takes an immediate operand.
bool hasImmediate() const {
- return Proto.find('i') != std::string::npos;
+ return std::any_of(Types.begin(), Types.end(),
+ [](const Type &T) { return T.isImmediate(); });
}
/// Return the parameter index of the immediate operand.
unsigned getImmediateIdx() const {
- assert(hasImmediate());
- unsigned Idx = Proto.find('i');
- assert(Idx > 0 && "Can't return an immediate!");
- return Idx - 1;
+ for (unsigned Idx = 0; Idx < Types.size(); ++Idx)
+ if (Types[Idx].isImmediate())
+ return Idx - 1;
+ llvm_unreachable("Intrinsic has no immediate");
}
- /// Return true if the intrinsic takes an splat operand.
- bool hasSplat() const { return Proto.find('a') != std::string::npos; }
-
- /// Return the parameter index of the splat operand.
- unsigned getSplatIdx() const {
- assert(hasSplat());
- unsigned Idx = Proto.find('a');
- assert(Idx > 0 && "Can't return a splat!");
- return Idx - 1;
- }
- unsigned getNumParams() const { return Proto.size() - 1; }
+ unsigned getNumParams() const { return Types.size() - 1; }
Type getReturnType() const { return Types[0]; }
Type getParamType(unsigned I) const { return Types[I + 1]; }
Type getBaseType() const { return BaseType; }
- /// Return the raw prototype string.
- std::string getProto() const { return Proto; }
+ Type getPolymorphicKeyType() const { return Types[PolymorphicKeyType]; }
/// Return true if the prototype has a scalar argument.
- /// This does not return true for the "splat" code ('a').
bool protoHasScalar() const;
/// Return the index that parameter PIndex will sit at
@@ -473,6 +481,8 @@ public:
void indexBody();
private:
+ StringRef getNextModifiers(StringRef Proto, unsigned &Pos) const;
+
std::string mangleName(std::string Name, ClassKind CK) const;
void initVariables();
@@ -582,16 +592,16 @@ public:
//===----------------------------------------------------------------------===//
std::string Type::str() const {
- if (Void)
+ if (isVoid())
return "void";
std::string S;
- if (!Signed && isInteger())
+ if (isInteger() && !isSigned())
S += "u";
- if (Poly)
+ if (isPoly())
S += "poly";
- else if (Float)
+ else if (isFloating())
S += "float";
else
S += "int";
@@ -616,10 +626,14 @@ std::string Type::builtin_str() const {
if (isVoid())
return "v";
- if (Pointer)
+ if (isPointer()) {
// All pointers are void pointers.
- S += "v";
- else if (isInteger())
+ S = "v";
+ if (isConstPointer())
+ S += "C";
+ S += "*";
+ return S;
+ } else if (isInteger())
switch (ElementBitwidth) {
case 8: S += "c"; break;
case 16: S += "s"; break;
@@ -636,10 +650,11 @@ std::string Type::builtin_str() const {
default: llvm_unreachable("Unhandled case!");
}
- if (isChar() && !Pointer && Signed)
+ // FIXME: NECESSARY???????????????????????????????????????????????????????????????????????
+ if (isChar() && !isPointer() && isSigned())
// Make chars explicitly signed.
S = "S" + S;
- else if (isInteger() && !Pointer && !Signed)
+ else if (isInteger() && !isSigned())
S = "U" + S;
// Constant indices are "int", but have the "constant expression" modifier.
@@ -648,11 +663,8 @@ std::string Type::builtin_str() const {
S = "I" + S;
}
- if (isScalar()) {
- if (Constant) S += "C";
- if (Pointer) S += "*";
+ if (isScalar())
return S;
- }
std::string Ret;
for (unsigned I = 0; I < NumVectors; ++I)
@@ -673,20 +685,20 @@ unsigned Type::getNeonEnum() const {
}
unsigned Base = (unsigned)NeonTypeFlags::Int8 + Addend;
- if (Poly) {
+ if (isPoly()) {
// Adjustment needed because Poly32 doesn't exist.
if (Addend >= 2)
--Addend;
Base = (unsigned)NeonTypeFlags::Poly8 + Addend;
}
- if (Float) {
+ if (isFloating()) {
assert(Addend != 0 && "Float8 doesn't exist!");
Base = (unsigned)NeonTypeFlags::Float16 + (Addend - 1);
}
if (Bitwidth == 128)
Base |= (unsigned)NeonTypeFlags::QuadFlag;
- if (isInteger() && !Signed)
+ if (isInteger() && !isSigned())
Base |= (unsigned)NeonTypeFlags::UnsignedFlag;
return Base;
@@ -694,22 +706,18 @@ unsigned Type::getNeonEnum() const {
Type Type::fromTypedefName(StringRef Name) {
Type T;
- T.Void = false;
- T.Float = false;
- T.Poly = false;
+ T.Kind = SInt;
if (Name.front() == 'u') {
- T.Signed = false;
+ T.Kind = UInt;
Name = Name.drop_front();
- } else {
- T.Signed = true;
}
if (Name.startswith("float")) {
- T.Float = true;
+ T.Kind = Float;
Name = Name.drop_front(5);
} else if (Name.startswith("poly")) {
- T.Poly = true;
+ T.Kind = Poly;
Name = Name.drop_front(4);
} else {
assert(Name.startswith("int"));
@@ -760,10 +768,8 @@ Type Type::fromTypedefName(StringRef Name) {
void Type::applyTypespec(bool &Quad) {
std::string S = TS;
ScalarForMangling = false;
- Void = false;
- Poly = Float = false;
+ Kind = SInt;
ElementBitwidth = ~0U;
- Signed = true;
NumVectors = 1;
for (char I : S) {
@@ -779,28 +785,28 @@ void Type::applyTypespec(bool &Quad) {
Quad = true;
break;
case 'P':
- Poly = true;
+ Kind = Poly;
break;
case 'U':
- Signed = false;
+ Kind = UInt;
break;
case 'c':
ElementBitwidth = 8;
break;
case 'h':
- Float = true;
+ Kind = Float;
LLVM_FALLTHROUGH;
case 's':
ElementBitwidth = 16;
break;
case 'f':
- Float = true;
+ Kind = Float;
LLVM_FALLTHROUGH;
case 'i':
ElementBitwidth = 32;
break;
case 'd':
- Float = true;
+ Kind = Float;
LLVM_FALLTHROUGH;
case 'l':
ElementBitwidth = 64;
@@ -808,7 +814,7 @@ void Type::applyTypespec(bool &Quad) {
case 'k':
ElementBitwidth = 128;
// Poly doesn't have a 128x1 type.
- if (Poly)
+ if (isPoly())
NumVectors = 0;
break;
default:
@@ -820,220 +826,77 @@ void Type::applyTypespec(bool &Quad) {
Bitwidth = Quad ? 128 : 64;
}
-void Type::applyModifier(char Mod) {
+void Type::applyModifiers(StringRef Mods) {
bool AppliedQuad = false;
applyTypespec(AppliedQuad);
- switch (Mod) {
- case 'v':
- Void = true;
- break;
- case 't':
- if (Poly) {
- Poly = false;
- Signed = false;
+ for (char Mod : Mods) {
+ switch (Mod) {
+ case '.':
+ break;
+ case 'v':
+ Kind = Void;
+ break;
+ case 'S':
+ Kind = SInt;
+ break;
+ case 'U':
+ Kind = UInt;
+ break;
+ case 'F':
+ Kind = Float;
+ break;
+ case 'P':
+ Kind = Poly;
+ break;
+ case '>':
+ assert(ElementBitwidth < 128);
+ ElementBitwidth *= 2;
+ break;
+ case '<':
+ assert(ElementBitwidth > 8);
+ ElementBitwidth /= 2;
+ break;
+ case '1':
+ NumVectors = 0;
+ break;
+ case '2':
+ NumVectors = 2;
+ break;
+ case '3':
+ NumVectors = 3;
+ break;
+ case '4':
+ NumVectors = 4;
+ break;
+ case '*':
+ Pointer = true;
+ break;
+ case 'c':
+ Constant = true;
+ break;
+ case 'Q':
+ Bitwidth = 128;
+ break;
+ case 'q':
+ Bitwidth = 64;
+ break;
+ case 'I':
+ Kind = SInt;
+ ElementBitwidth = Bitwidth = 32;
+ NumVectors = 0;
+ Immediate = true;
+ break;
+ case 'p':
+ if (isPoly())
+ Kind = UInt;
+ break;
+ case '!':
+ // Key type, handled elsewhere.
+ break;
+ default:
+ llvm_unreachable("Unhandled character!");
}
- break;
- case 'b':
- Signed = false;
- Float = false;
- Poly = false;
- NumVectors = 0;
- Bitwidth = ElementBitwidth;
- break;
- case '$':
- Signed = true;
- Float = false;
- Poly = false;
- NumVectors = 0;
- Bitwidth = ElementBitwidth;
- break;
- case 'u':
- Signed = false;
- Poly = false;
- Float = false;
- break;
- case 'x':
- Signed = true;
- assert(!Poly && "'u' can't be used with poly types!");
- Float = false;
- break;
- case 'o':
- Bitwidth = ElementBitwidth = 64;
- NumVectors = 0;
- Float = true;
- break;
- case 'y':
- Bitwidth = ElementBitwidth = 32;
- NumVectors = 0;
- Float = true;
- break;
- case 'Y':
- Bitwidth = ElementBitwidth = 16;
- NumVectors = 0;
- Float = true;
- break;
- case 'I':
- Bitwidth = ElementBitwidth = 32;
- NumVectors = 0;
- Float = false;
- Signed = true;
- break;
- case 'L':
- Bitwidth = ElementBitwidth = 64;
- NumVectors = 0;
- Float = false;
- Signed = true;
- break;
- case 'U':
- Bitwidth = ElementBitwidth = 32;
- NumVectors = 0;
- Float = false;
- Signed = false;
- break;
- case 'O':
- Bitwidth = ElementBitwidth = 64;
- NumVectors = 0;
- Float = false;
- Signed = false;
- break;
- case 'f':
- Float = true;
- ElementBitwidth = 32;
- break;
- case 'F':
- Float = true;
- ElementBitwidth = 64;
- break;
- case 'H':
- Float = true;
- ElementBitwidth = 16;
- break;
- case '0':
- Float = true;
- if (AppliedQuad)
- Bitwidth /= 2;
- ElementBitwidth = 16;
- break;
- case '1':
- Float = true;
- if (!AppliedQuad)
- Bitwidth *= 2;
- ElementBitwidth = 16;
- break;
- case 'g':
- if (AppliedQuad)
- Bitwidth /= 2;
- break;
- case 'j':
- if (!AppliedQuad)
- Bitwidth *= 2;
- break;
- case 'w':
- ElementBitwidth *= 2;
- Bitwidth *= 2;
- break;
- case 'n':
- ElementBitwidth *= 2;
- break;
- case 'i':
- Float = false;
- Poly = false;
- ElementBitwidth = Bitwidth = 32;
- NumVectors = 0;
- Signed = true;
- Immediate = true;
- break;
- case 'l':
- Float = false;
- Poly = false;
- ElementBitwidth = Bitwidth = 64;
- NumVectors = 0;
- Signed = false;
- Immediate = true;
- break;
- case 'z':
- ElementBitwidth /= 2;
- Bitwidth = ElementBitwidth;
- NumVectors = 0;
- break;
- case 'r':
- ElementBitwidth *= 2;
- Bitwidth = ElementBitwidth;
- NumVectors = 0;
- break;
- case 's':
- case 'a':
- Bitwidth = ElementBitwidth;
- NumVectors = 0;
- break;
- case 'k':
- Bitwidth *= 2;
- break;
- case 'c':
- Constant = true;
- LLVM_FALLTHROUGH;
- case 'p':
- Pointer = true;
- Bitwidth = ElementBitwidth;
- NumVectors = 0;
- break;
- case 'h':
- ElementBitwidth /= 2;
- break;
- case 'q':
- ElementBitwidth /= 2;
- Bitwidth *= 2;
- break;
- case 'e':
- ElementBitwidth /= 2;
- Signed = false;
- break;
- case 'm':
- ElementBitwidth /= 2;
- Bitwidth /= 2;
- break;
- case 'd':
- break;
- case '2':
- NumVectors = 2;
- break;
- case '3':
- NumVectors = 3;
- break;
- case '4':
- NumVectors = 4;
- break;
- case 'B':
- NumVectors = 2;
- if (!AppliedQuad)
- Bitwidth *= 2;
- break;
- case 'C':
- NumVectors = 3;
- if (!AppliedQuad)
- Bitwidth *= 2;
- break;
- case 'D':
- NumVectors = 4;
- if (!AppliedQuad)
- Bitwidth *= 2;
- break;
- case '7':
- if (AppliedQuad)
- Bitwidth /= 2;
- ElementBitwidth = 8;
- break;
- case '8':
- ElementBitwidth = 8;
- break;
- case '9':
- if (!AppliedQuad)
- Bitwidth *= 2;
- ElementBitwidth = 8;
- break;
- default:
- llvm_unreachable("Unhandled character!");
}
}
@@ -1041,6 +904,19 @@ void Type::applyModifier(char Mod) {
// Intrinsic implementation
//===----------------------------------------------------------------------===//
+StringRef Intrinsic::getNextModifiers(StringRef Proto, unsigned &Pos) const {
+ if (Proto.size() == Pos)
+ return StringRef();
+ else if (Proto[Pos] != '(')
+ return Proto.substr(Pos++, 1);
+
+ size_t Start = Pos + 1;
+ size_t End = Proto.find(')', Start);
+ assert_with_loc(End != StringRef::npos, "unmatched modifier group paren");
+ Pos = End + 1;
+ return Proto.slice(Start, End);
+}
+
std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const {
char typeCode = '\0';
bool printNumber = true;
@@ -1079,10 +955,6 @@ std::string Intrinsic::getInstTypeCode(Type T, ClassKind CK) const {
return S;
}
-static bool isFloatingPointProtoModifier(char Mod) {
- return Mod == 'F' || Mod == 'f' || Mod == 'H' || Mod == 'Y' || Mod == 'I';
-}
-
std::string Intrinsic::getBuiltinTypeStr() {
ClassKind LocalCK = getClassKind(true);
std::string S;
@@ -1101,11 +973,10 @@ std::string Intrinsic::getBuiltinTypeStr() {
} else {
if (RetT.isPoly())
RetT.makeInteger(RetT.getElementSizeInBits(), false);
- if (!RetT.isScalar() && !RetT.isSigned())
+ if (!RetT.isScalar() && RetT.isInteger() && !RetT.isSigned())
RetT.makeSigned();
- bool ForcedVectorFloatingType = isFloatingPointProtoModifier(Proto[0]);
- if (LocalCK == ClassB && !RetT.isScalar() && !ForcedVectorFloatingType)
+ if (LocalCK == ClassB && RetT.isValue() && !RetT.isScalar())
// Cast to vector of 8-bit elements.
RetT.makeInteger(8, true);
@@ -1117,14 +988,13 @@ std::string Intrinsic::getBuiltinTypeStr() {
if (T.isPoly())
T.makeInteger(T.getElementSizeInBits(), false);
- bool ForcedFloatingType = isFloatingPointProtoModifier(Proto[I + 1]);
- if (LocalCK == ClassB && !T.isScalar() && !ForcedFloatingType)
+ if (LocalCK == ClassB && !T.isScalar())
T.makeInteger(8, true);
// Halves always get converted to 8-bit elements.
if (T.isHalf() && T.isVector() && !T.isScalarForMangling())
T.makeInteger(8, true);
- if (LocalCK == ClassI)
+ if (LocalCK == ClassI && T.isInteger())
T.makeSigned();
if (hasImmediate() && getImmediateIdx() == I)
@@ -1222,7 +1092,7 @@ void Intrinsic::initVariables() {
// Modify the TypeSpec per-argument to get a concrete Type, and create
// known variables for each.
- for (unsigned I = 1; I < Proto.size(); ++I) {
+ for (unsigned I = 1; I < Types.size(); ++I) {
char NameC = '0' + (I - 1);
std::string Name = "p";
Name.push_back(NameC);
@@ -1343,7 +1213,7 @@ void Intrinsic::emitShadowedArgs() {
for (unsigned I = 0; I < getNumParams(); ++I) {
// Do not create a temporary for an immediate argument.
// That would defeat the whole point of using a macro!
- if (hasImmediate() && Proto[I+1] == 'i')
+ if (getParamType(I).isImmediate())
continue;
// Do not create a temporary for pointer arguments. The input
// pointer may have an alignment hint.
@@ -1366,16 +1236,10 @@ void Intrinsic::emitShadowedArgs() {
}
}
-// We don't check 'a' in this function, because for builtin function the
-// argument matching to 'a' uses a vector type splatted from a scalar type.
bool Intrinsic::protoHasScalar() const {
- return (Proto.find('s') != std::string::npos ||
- Proto.find('z') != std::string::npos ||
- Proto.find('r') != std::string::npos ||
- Proto.find('b') != std::string::npos ||
- Proto.find('$') != std::string::npos ||
- Proto.find('y') != std::string::npos ||
- Proto.find('o') != std::string::npos);
+ return std::any_of(Types.begin(), Types.end(), [](const Type &T) {
+ return T.isScalar() && !T.isImmediate();
+ });
}
void Intrinsic::emitBodyAsBuiltinCall() {
@@ -1386,12 +1250,6 @@ void Intrinsic::emitBodyAsBuiltinCall() {
bool SRet = getReturnType().getNumVectors() >= 2;
StringRef N = Name;
- if (hasSplat()) {
- // Call the non-splat builtin: chop off the "_n" suffix from the name.
- assert(N.endswith("_n"));
- N = N.drop_back(2);
- }
-
ClassKind LocalCK = CK;
if (!protoHasScalar())
LocalCK = ClassB;
@@ -1425,21 +1283,8 @@ void Intrinsic::emitBodyAsBuiltinCall() {
continue;
}
- std::string Arg;
+ std::string Arg = V.getName();
Type CastToType = T;
- if (hasSplat() && I == getSplatIdx()) {
- Arg = "(" + BaseType.str() + ") {";
- for (unsigned J = 0; J < BaseType.getNumElements(); ++J) {
- if (J != 0)
- Arg += ", ";
- Arg += V.getName();
- }
- Arg += "}";
-
- CastToType = BaseType;
- } else {
- Arg = V.getName();
- }
// Check if an explicit cast is needed.
if (CastToType.isVector() &&
@@ -1447,7 +1292,8 @@ void Intrinsic::emitBodyAsBuiltinCall() {
CastToType.makeInteger(8, true);
Arg = "(" + CastToType.str() + ")" + Arg;
} else if (CastToType.isVector() && LocalCK == ClassI) {
- CastToType.makeSigned();
+ if (CastToType.isInteger())
+ CastToType.makeSigned();
Arg = "(" + CastToType.str() + ")" + Arg;
}
@@ -1456,13 +1302,7 @@ void Intrinsic::emitBodyAsBuiltinCall() {
// Extra constant integer to hold type class enum for this function, e.g. s8
if (getClassKind(true) == ClassB) {
- Type ThisTy = getReturnType();
- if (Proto[0] == 'v' || isFloatingPointProtoModifier(Proto[0]))
- ThisTy = getParamType(0);
- if (ThisTy.isPointer())
- ThisTy = getParamType(1);
-
- S += utostr(ThisTy.getNeonEnum());
+ S += utostr(getPolymorphicKeyType().getNeonEnum());
} else {
// Remove extraneous ", ".
S.pop_back();
@@ -2067,9 +1907,9 @@ void NeonEmitter::createIntrinsic(Record *R,
std::vector<std::pair<TypeSpec, TypeSpec>> NewTypeSpecs;
for (auto TS : TypeSpecs) {
if (CartesianProductOfTypes) {
- Type DefaultT(TS, 'd');
+ Type DefaultT(TS, ".");
for (auto SrcTS : TypeSpecs) {
- Type DefaultSrcT(SrcTS, 'd');
+ Type DefaultSrcT(SrcTS, ".");
if (TS == SrcTS ||
DefaultSrcT.getSizeInBits() != DefaultT.getSizeInBits())
continue;
@@ -2107,10 +1947,6 @@ void NeonEmitter::genBuiltinsDef(raw_ostream &OS,
for (auto *Def : Defs) {
if (Def->hasBody())
continue;
- // Functions with 'a' (the splat code) in the type prototype should not get
- // their own builtin as they use the non-splat variant.
- if (Def->hasSplat())
- continue;
std::string S = "BUILTIN(__builtin_neon_" + Def->getMangledName() + ", \"";
@@ -2147,41 +1983,25 @@ void NeonEmitter::genOverloadTypeCheckCode(raw_ostream &OS,
// __builtin_neon_* so we don't need to generate a definition for it.
if (Def->hasBody())
continue;
- // Functions with 'a' (the splat code) in the type prototype should not get
- // their own builtin as they use the non-splat variant.
- if (Def->hasSplat())
- continue;
// Functions which have a scalar argument cannot be overloaded, no need to
// check them if we are emitting the type checking code.
if (Def->protoHasScalar())
continue;
uint64_t Mask = 0ULL;
- Type Ty = Def->getReturnType();
- if (Def->getProto()[0] == 'v' ||
- isFloatingPointProtoModifier(Def->getProto()[0]))
- Ty = Def->getParamType(0);
- if (Ty.isPointer())
- Ty = Def->getParamType(1);
-
- Mask |= 1ULL << Ty.getNeonEnum();
+ Mask |= 1ULL << Def->getPolymorphicKeyType().getNeonEnum();
// Check if the function has a pointer or const pointer argument.
- std::string Proto = Def->getProto();
int PtrArgNum = -1;
bool HasConstPtr = false;
for (unsigned I = 0; I < Def->getNumParams(); ++I) {
- char ArgType = Proto[I + 1];
- if (ArgType == 'c') {
- HasConstPtr = true;
+ const auto &Type = Def->getParamType(I);
+ if (Type.isPointer()) {
PtrArgNum = I;
- break;
- }
- if (ArgType == 'p') {
- PtrArgNum = I;
- break;
+ HasConstPtr = Type.isConstPointer();
}
}
+
// For sret builtins, adjust the pointer argument index.
if (PtrArgNum >= 0 && Def->getReturnType().getNumVectors() > 1)
PtrArgNum += 1;
@@ -2231,10 +2051,6 @@ void NeonEmitter::genIntrinsicRangeCheckCode(raw_ostream &OS,
for (auto *Def : Defs) {
if (Def->hasBody())
continue;
- // Functions with 'a' (the splat code) in the type prototype should not get
- // their own builtin as they use the non-splat variant.
- if (Def->hasSplat())
- continue;
// Functions which do not have an immediate do not need to have range
// checking code emitted.
if (!Def->hasImmediate())
@@ -2409,8 +2225,8 @@ void NeonEmitter::run(raw_ostream &OS) {
bool InIfdef = false;
for (auto &TS : TDTypeVec) {
bool IsA64 = false;
- Type T(TS, 'd');
- if (T.isDouble() || (T.isPoly() && T.isLong()))
+ Type T(TS, ".");
+ if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64))
IsA64 = true;
if (InIfdef && !IsA64) {
@@ -2442,8 +2258,8 @@ void NeonEmitter::run(raw_ostream &OS) {
for (unsigned NumMembers = 2; NumMembers <= 4; ++NumMembers) {
for (auto &TS : TDTypeVec) {
bool IsA64 = false;
- Type T(TS, 'd');
- if (T.isDouble() || (T.isPoly() && T.isLong()))
+ Type T(TS, ".");
+ if (T.isDouble() || (T.isPoly() && T.getElementSizeInBits() == 64))
IsA64 = true;
if (InIfdef && !IsA64) {
@@ -2455,8 +2271,8 @@ void NeonEmitter::run(raw_ostream &OS) {
InIfdef = true;
}
- char M = '2' + (NumMembers - 2);
- Type VT(TS, M);
+ const char Mods[] = { static_cast<char>('2' + (NumMembers - 2)), 0};
+ Type VT(TS, Mods);
OS << "typedef struct " << VT.str() << " {\n";
OS << " " << T.str() << " val";
OS << "[" << NumMembers << "]";
diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp
index d0f8a7564496..6ba90cee4aae 100644
--- a/clang/utils/TableGen/TableGen.cpp
+++ b/clang/utils/TableGen/TableGen.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "TableGenBackends.h" // Declares all backends.
+#include "ASTTableGen.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
@@ -41,6 +42,8 @@ enum ActionType {
GenClangAttrParsedAttrKinds,
GenClangAttrTextNodeDump,
GenClangAttrNodeTraverse,
+ GenClangBasicReader,
+ GenClangBasicWriter,
GenClangDiagsDefs,
GenClangDiagGroups,
GenClangDiagsIndexName,
@@ -48,6 +51,8 @@ enum ActionType {
GenClangDeclNodes,
GenClangStmtNodes,
GenClangTypeNodes,
+ GenClangTypeReader,
+ GenClangTypeWriter,
GenClangOpcodes,
GenClangSACheckers,
GenClangCommentHTMLTags,
@@ -60,6 +65,11 @@ enum ActionType {
GenArmFP16,
GenArmNeonSema,
GenArmNeonTest,
+ GenArmMveHeader,
+ GenArmMveBuiltinDef,
+ GenArmMveBuiltinSema,
+ GenArmMveBuiltinCG,
+ GenArmMveBuiltinAliases,
GenAttrDocs,
GenDiagDocs,
GenOptDocs,
@@ -125,6 +135,10 @@ cl::opt<ActionType> 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",
@@ -133,6 +147,10 @@ cl::opt<ActionType> Action(
"Generate Clang AST statement nodes"),
clEnumValN(GenClangTypeNodes, "gen-clang-type-nodes",
"Generate Clang AST type nodes"),
+ clEnumValN(GenClangTypeReader, "gen-clang-type-reader",
+ "Generate Clang AbstractTypeReader class"),
+ clEnumValN(GenClangTypeWriter, "gen-clang-type-writer",
+ "Generate Clang AbstractTypeWriter class"),
clEnumValN(GenClangOpcodes, "gen-clang-opcodes",
"Generate Clang constexpr interpreter opcodes"),
clEnumValN(GenClangSACheckers, "gen-clang-sa-checkers",
@@ -162,6 +180,16 @@ cl::opt<ActionType> Action(
"Generate ARM NEON sema support for clang"),
clEnumValN(GenArmNeonTest, "gen-arm-neon-test",
"Generate ARM NEON tests for clang"),
+ clEnumValN(GenArmMveHeader, "gen-arm-mve-header",
+ "Generate arm_mve.h for clang"),
+ clEnumValN(GenArmMveBuiltinDef, "gen-arm-mve-builtin-def",
+ "Generate ARM MVE builtin definitions for clang"),
+ clEnumValN(GenArmMveBuiltinSema, "gen-arm-mve-builtin-sema",
+ "Generate ARM MVE builtin sema checks for clang"),
+ clEnumValN(GenArmMveBuiltinCG, "gen-arm-mve-builtin-codegen",
+ "Generate ARM MVE builtin code-generator for clang"),
+ clEnumValN(GenArmMveBuiltinAliases, "gen-arm-mve-builtin-aliases",
+ "Generate list of valid ARM MVE builtin aliases for clang"),
clEnumValN(GenAttrDocs, "gen-attr-docs",
"Generate attribute documentation"),
clEnumValN(GenDiagDocs, "gen-diag-docs",
@@ -248,18 +276,30 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
EmitClangDiagsIndexName(Records, OS);
break;
case GenClangCommentNodes:
- EmitClangASTNodes(Records, OS, "Comment", "");
+ EmitClangASTNodes(Records, OS, CommentNodeClassName, "");
break;
case GenClangDeclNodes:
- EmitClangASTNodes(Records, OS, "Decl", "Decl");
+ EmitClangASTNodes(Records, OS, DeclNodeClassName, "Decl");
EmitClangDeclContext(Records, OS);
break;
case GenClangStmtNodes:
- EmitClangASTNodes(Records, OS, "Stmt", "");
+ EmitClangASTNodes(Records, OS, StmtNodeClassName, "");
break;
case GenClangTypeNodes:
EmitClangTypeNodes(Records, OS);
break;
+ case GenClangTypeReader:
+ EmitClangTypeReader(Records, OS);
+ break;
+ case GenClangTypeWriter:
+ EmitClangTypeWriter(Records, OS);
+ break;
+ case GenClangBasicReader:
+ EmitClangBasicReader(Records, OS);
+ break;
+ case GenClangBasicWriter:
+ EmitClangBasicWriter(Records, OS);
+ break;
case GenClangOpcodes:
EmitClangOpcodes(Records, OS);
break;
@@ -296,6 +336,21 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenArmNeonTest:
EmitNeonTest(Records, OS);
break;
+ case GenArmMveHeader:
+ EmitMveHeader(Records, OS);
+ break;
+ case GenArmMveBuiltinDef:
+ EmitMveBuiltinDef(Records, OS);
+ break;
+ case GenArmMveBuiltinSema:
+ EmitMveBuiltinSema(Records, OS);
+ break;
+ case GenArmMveBuiltinCG:
+ EmitMveBuiltinCG(Records, OS);
+ break;
+ case GenArmMveBuiltinAliases:
+ EmitMveBuiltinAliases(Records, OS);
+ break;
case GenAttrDocs:
EmitClangAttrDocs(Records, OS);
break;
diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h
index cdd492b4e558..7ac2e0eeb1f3 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(
@@ -88,6 +91,12 @@ void EmitNeon2(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeonSema2(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitNeonTest2(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitMveHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitMveBuiltinDef(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitMveBuiltinSema(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitMveBuiltinCG(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitMveBuiltinAliases(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+
void EmitClangAttrDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitClangDiagDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitClangOptDocs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
diff --git a/clang/utils/convert_arm_neon.py b/clang/utils/convert_arm_neon.py
new file mode 100644
index 000000000000..c4b364529457
--- /dev/null
+++ b/clang/utils/convert_arm_neon.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python3
+
+# This script was committed on 20/11/2019 and it would probably make sense to remove
+# it after the next release branches.
+
+# This script is pipe based and converts an arm_neon.td (or arm_fp16.td) file
+# using the old single-char type modifiers to an equivalent new-style form where
+# each modifier is orthogonal and they can be composed.
+#
+# It was used to directly generate the .td files on master, so if you have any
+# local additions I would suggest implementing any modifiers here, and running
+# it over your entire pre-merge .td files rather than trying to resolve any
+# conflicts manually.
+
+import re, sys
+MOD_MAP = {
+ 'v': 'v',
+ 'x': 'S',
+ 'u': 'U',
+ 'd': '.',
+ 'g': 'q',
+ 'j': 'Q',
+ 'w': '>Q',
+ 'n': '>',
+ 'h': '<',
+ 'q': '<Q',
+ 'e': '<U',
+ 'm': '<q',
+ 'i': 'I',
+ 'l': 'IU>',
+ 's': '1',
+ 'z': '1<',
+ 'r': '1>',
+ 'b': '1U',
+ '$': '1S',
+ 'k': 'Q',
+ '2': '2',
+ '3': '3',
+ '4': '4',
+ 'B': '2Q',
+ 'C': '3Q',
+ 'D': '4Q',
+ 'p': '*',
+ 'c': 'c*',
+ '7': '<<q',
+ '8': '<<',
+ '9': '<<Q',
+ 't': 'p'
+ }
+
+
+def typespec_elt_size(typespec):
+ if 'c' in typespec:
+ return 8
+ elif 's' in typespec or 'h' in typespec:
+ return 16
+ elif 'i' in typespec or 'f' in typespec:
+ return 32
+ elif 'l' in typespec or 'd' in typespec:
+ return 64
+ elif 'k' in typespec:
+ return 128
+
+def get_resize(cur, desired):
+ res = ''
+ while cur < desired:
+ res += '>'
+ cur *= 2
+ while cur > desired:
+ res += '<'
+ cur /= 2
+ return res
+
+
+def remap_protocol(proto, typespec, name):
+ key_type = 0
+
+ # Conversions like to see the integer type so they know signedness.
+ if 'vcvt' in name and '_f' in name and name != 'vcvt_f32_f64' and name != 'vcvt_f64_f32':
+ key_type = 1
+ default_width = typespec_elt_size(typespec)
+ inconsistent_width = False
+ for elt in typespec:
+ new_width = typespec_elt_size(elt)
+ if new_width and new_width != default_width:
+ inconsistent_width = True
+
+ res = ''
+ for i, c in enumerate(proto):
+ # void and pointers make for bad discriminators in CGBuiltin.cpp.
+ if c in 'vcp':
+ key_type += 1
+
+ if c in MOD_MAP:
+ cur_mod = MOD_MAP[c]
+ elif inconsistent_width:
+ # Otherwise it's a fixed output width modifier.
+ sys.stderr.write(f'warning: {name} uses fixed output size but has inconsistent input widths: {proto} {typespec}\n')
+
+ if c == 'Y':
+ # y: scalar of half float
+ resize = get_resize(default_width, 16)
+ cur_mod = f'1F{resize}'
+ elif c == 'y':
+ # y: scalar of float
+ resize = get_resize(default_width, 32)
+ cur_mod = f'1F{resize}'
+ elif c == 'o':
+ # o: scalar of double
+ resize = get_resize(default_width, 64)
+ cur_mod = f'1F{resize}'
+ elif c == 'I':
+ # I: scalar of 32-bit signed
+ resize = get_resize(default_width, 32)
+ cur_mod = f'1S{resize}'
+ elif c == 'L':
+ # L: scalar of 64-bit signed
+ resize = get_resize(default_width, 64)
+ cur_mod = f'1S{resize}'
+ elif c == 'U':
+ # I: scalar of 32-bit unsigned
+ resize = get_resize(default_width, 32)
+ cur_mod = f'1U{resize}'
+ elif c == 'O':
+ # O: scalar of 64-bit unsigned
+ resize = get_resize(default_width, 64)
+ cur_mod = f'1U{resize}'
+ elif c == 'f':
+ # f: float (int args)
+ resize = get_resize(default_width, 32)
+ cur_mod = f'F{resize}'
+ elif c == 'F':
+ # F: double (int args)
+ resize = get_resize(default_width, 64)
+ cur_mod = f'F{resize}'
+ elif c == 'H':
+ # H: half (int args)
+ resize = get_resize(default_width, 16)
+ cur_mod = f'F{resize}'
+ elif c == '0':
+ # 0: half (int args), ignore 'Q' size modifier.
+ resize = get_resize(default_width, 16)
+ cur_mod = f'Fq{resize}'
+ elif c == '1':
+ # 1: half (int args), force 'Q' size modifier.
+ resize = get_resize(default_width, 16)
+ cur_mod = f'FQ{resize}'
+
+ if len(cur_mod) == 0:
+ raise Exception(f'WTF: {c} in {name}')
+
+ if key_type != 0 and key_type == i:
+ cur_mod += '!'
+
+ if len(cur_mod) == 1:
+ res += cur_mod
+ else:
+ res += '(' + cur_mod + ')'
+
+ return res
+
+def replace_insts(m):
+ start, end = m.span('proto')
+ start -= m.start()
+ end -= m.start()
+ new_proto = remap_protocol(m['proto'], m['kinds'], m['name'])
+ return m.group()[:start] + new_proto + m.group()[end:]
+
+INST = re.compile(r'Inst<"(?P<name>.*?)",\s*"(?P<proto>.*?)",\s*"(?P<kinds>.*?)"')
+
+new_td = INST.sub(replace_insts, sys.stdin.read())
+sys.stdout.write(new_td)