diff options
author | Nandor Licker <n@ndor.email> | 2019-09-13 09:46:16 +0000 |
---|---|---|
committer | Nandor Licker <n@ndor.email> | 2019-09-13 09:46:16 +0000 |
commit | 9e90d26fa6b8e98eb659c0cfa51cbd2bb93576d1 (patch) | |
tree | fecdbbfb2774f28756265db6384bbc9618d12875 /utils | |
parent | 56e226c3c38e4d61d7f07309b2deb288eeb9c4ab (diff) | |
download | clang-9e90d26fa6b8e98eb659c0cfa51cbd2bb93576d1.tar.gz |
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@371834 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'utils')
-rw-r--r-- | utils/TableGen/CMakeLists.txt | 1 | ||||
-rw-r--r-- | utils/TableGen/ClangOpcodesEmitter.cpp | 360 | ||||
-rw-r--r-- | utils/TableGen/TableGen.cpp | 6 | ||||
-rw-r--r-- | utils/TableGen/TableGenBackends.h | 1 |
4 files changed, 368 insertions, 0 deletions
diff --git a/utils/TableGen/CMakeLists.txt b/utils/TableGen/CMakeLists.txt index 3fc87d6552..d33ede2a75 100644 --- a/utils/TableGen/CMakeLists.txt +++ b/utils/TableGen/CMakeLists.txt @@ -8,6 +8,7 @@ add_tablegen(clang-tblgen CLANG ClangCommentHTMLTagsEmitter.cpp ClangDataCollectorsEmitter.cpp ClangDiagnosticsEmitter.cpp + ClangOpcodesEmitter.cpp ClangOpenCLBuiltinEmitter.cpp ClangOptionDocEmitter.cpp ClangSACheckersEmitter.cpp diff --git a/utils/TableGen/ClangOpcodesEmitter.cpp b/utils/TableGen/ClangOpcodesEmitter.cpp new file mode 100644 index 0000000000..1ff20f0f44 --- /dev/null +++ b/utils/TableGen/ClangOpcodesEmitter.cpp @@ -0,0 +1,360 @@ +//=== ClangOpcodesEmitter.cpp - constexpr interpreter opcodes ---*- 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 +// +//===----------------------------------------------------------------------===// +// +// These tablegen backends emit Clang AST node tables +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/TableGenBackend.h" + +using namespace llvm; + +namespace { +class ClangOpcodesEmitter { + RecordKeeper &Records; + Record Root; + unsigned NumTypes; + +public: + ClangOpcodesEmitter(RecordKeeper &R) + : Records(R), Root("Opcode", SMLoc(), R), + NumTypes(Records.getAllDerivedDefinitions("Type").size()) {} + + void run(raw_ostream &OS); + +private: + /// Emits the opcode name for the opcode enum. + /// The name is obtained by concatenating the name with the list of types. + void EmitEnum(raw_ostream &OS, StringRef N, Record *R); + + /// Emits the switch case and the invocation in the interpreter. + void EmitInterp(raw_ostream &OS, StringRef N, Record *R); + + /// Emits the disassembler. + void EmitDisasm(raw_ostream &OS, StringRef N, Record *R); + + /// Emits the byte code emitter method. + void EmitEmitter(raw_ostream &OS, StringRef N, Record *R); + + /// Emits the prototype. + void EmitProto(raw_ostream &OS, StringRef N, Record *R); + + /// Emits the prototype to dispatch from a type. + void EmitGroup(raw_ostream &OS, StringRef N, Record *R); + + /// Emits the evaluator method. + void EmitEval(raw_ostream &OS, StringRef N, Record *R); + + void PrintTypes(raw_ostream &OS, ArrayRef<Record *> Types); +}; + +void Enumerate(const Record *R, + StringRef N, + std::function<void(ArrayRef<Record *>, Twine)> &&F) { + llvm::SmallVector<Record *, 2> TypePath; + auto *Types = R->getValueAsListInit("Types"); + + std::function<void(size_t, const Twine &)> Rec; + Rec = [&TypePath, Types, &Rec, &F](size_t I, const Twine &ID) { + if (I >= Types->size()) { + F(TypePath, ID); + return; + } + + if (auto *TypeClass = dyn_cast<DefInit>(Types->getElement(I))) { + for (auto *Type : TypeClass->getDef()->getValueAsListOfDefs("Types")) { + TypePath.push_back(Type); + Rec(I + 1, ID + Type->getName()); + TypePath.pop_back(); + } + } else { + PrintFatalError("Expected a type class"); + } + }; + Rec(0, N); +} + +} // namespace + +void ClangOpcodesEmitter::run(raw_ostream &OS) { + for (auto *Opcode : Records.getAllDerivedDefinitions(Root.getName())) { + // The name is the record name, unless overriden. + StringRef N = Opcode->getValueAsString("Name"); + if (N.empty()) + N = Opcode->getName(); + + EmitEnum(OS, N, Opcode); + EmitInterp(OS, N, Opcode); + EmitDisasm(OS, N, Opcode); + EmitProto(OS, N, Opcode); + EmitGroup(OS, N, Opcode); + EmitEmitter(OS, N, Opcode); + EmitEval(OS, N, Opcode); + } +} + +void ClangOpcodesEmitter::EmitEnum(raw_ostream &OS, StringRef N, Record *R) { + OS << "#ifdef GET_OPCODE_NAMES\n"; + Enumerate(R, N, [&OS](ArrayRef<Record *>, const Twine &ID) { + OS << "OP_" << ID << ",\n"; + }); + OS << "#endif\n"; +} + +void ClangOpcodesEmitter::EmitInterp(raw_ostream &OS, StringRef N, Record *R) { + OS << "#ifdef GET_INTERP\n"; + + Enumerate(R, N, [this, R, &OS, &N](ArrayRef<Record *> TS, const Twine &ID) { + bool CanReturn = R->getValueAsBit("CanReturn"); + bool ChangesPC = R->getValueAsBit("ChangesPC"); + auto Args = R->getValueAsListOfDefs("Args"); + + OS << "case OP_" << ID << ": {\n"; + + // Emit calls to read arguments. + for (size_t I = 0, N = Args.size(); I < N; ++I) { + OS << "\tauto V" << I; + OS << " = "; + OS << "PC.read<" << Args[I]->getValueAsString("Name") << ">();\n"; + } + + // Emit a call to the template method and pass arguments. + OS << "\tif (!" << N; + PrintTypes(OS, TS); + OS << "(S"; + if (ChangesPC) + OS << ", PC"; + else + OS << ", OpPC"; + if (CanReturn) + OS << ", Result"; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << ", V" << I; + OS << "))\n"; + OS << "\t\treturn false;\n"; + + // Bail out if interpreter returned. + if (CanReturn) { + OS << "\tif (!S.Current || S.Current->isRoot())\n"; + OS << "\t\treturn true;\n"; + } + + OS << "\tcontinue;\n"; + OS << "}\n"; + }); + OS << "#endif\n"; +} + +void ClangOpcodesEmitter::EmitDisasm(raw_ostream &OS, StringRef N, Record *R) { + OS << "#ifdef GET_DISASM\n"; + Enumerate(R, N, [R, &OS](ArrayRef<Record *>, const Twine &ID) { + OS << "case OP_" << ID << ":\n"; + OS << "\tPrintName(\"" << ID << "\");\n"; + OS << "\tOS << \"\\t\""; + + for (auto *Arg : R->getValueAsListOfDefs("Args")) + OS << " << PC.read<" << Arg->getValueAsString("Name") << ">() << \" \""; + + OS << "<< \"\\n\";\n"; + OS << "\tcontinue;\n"; + }); + OS << "#endif\n"; +} + +void ClangOpcodesEmitter::EmitEmitter(raw_ostream &OS, StringRef N, Record *R) { + if (R->getValueAsBit("HasCustomLink")) + return; + + OS << "#ifdef GET_LINK_IMPL\n"; + Enumerate(R, N, [R, &OS](ArrayRef<Record *>, const Twine &ID) { + auto Args = R->getValueAsListOfDefs("Args"); + + // Emit the list of arguments. + OS << "bool ByteCodeEmitter::emit" << ID << "("; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << Args[I]->getValueAsString("Name") << " A" << I << ","; + OS << "const SourceInfo &L) {\n"; + + // Emit a call to write the opcodes. + OS << "\treturn emitOp<"; + for (size_t I = 0, N = Args.size(); I < N; ++I) { + if (I != 0) + OS << ", "; + OS << Args[I]->getValueAsString("Name"); + } + OS << ">(OP_" << ID; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << ", A" << I; + OS << ", L);\n"; + OS << "}\n"; + }); + OS << "#endif\n"; +} + +void ClangOpcodesEmitter::EmitProto(raw_ostream &OS, StringRef N, Record *R) { + OS << "#if defined(GET_EVAL_PROTO) || defined(GET_LINK_PROTO)\n"; + auto Args = R->getValueAsListOfDefs("Args"); + Enumerate(R, N, [&OS, &Args](ArrayRef<Record *> TS, const Twine &ID) { + OS << "bool emit" << ID << "("; + for (auto *Arg : Args) + OS << Arg->getValueAsString("Name") << ", "; + OS << "const SourceInfo &);\n"; + }); + + // Emit a template method for custom emitters to have less to implement. + auto TypeCount = R->getValueAsListInit("Types")->size(); + if (R->getValueAsBit("HasCustomEval") && TypeCount) { + OS << "#if defined(GET_EVAL_PROTO)\n"; + OS << "template<"; + for (size_t I = 0; I < TypeCount; ++I) { + if (I != 0) + OS << ", "; + OS << "PrimType"; + } + OS << ">\n"; + OS << "bool emit" << N << "("; + for (auto *Arg : Args) + OS << Arg->getValueAsString("Name") << ", "; + OS << "const SourceInfo &);\n"; + OS << "#endif\n"; + } + + OS << "#endif\n"; +} + +void ClangOpcodesEmitter::EmitGroup(raw_ostream &OS, StringRef N, Record *R) { + if (!R->getValueAsBit("HasGroup")) + return; + + auto *Types = R->getValueAsListInit("Types"); + auto Args = R->getValueAsListOfDefs("Args"); + + // Emit the prototype of the group emitter in the header. + OS << "#if defined(GET_EVAL_PROTO) || defined(GET_LINK_PROTO)\n"; + OS << "bool emit" << N << "("; + for (size_t I = 0, N = Types->size(); I < N; ++I) + OS << "PrimType, "; + for (auto *Arg : Args) + OS << Arg->getValueAsString("Name") << ", "; + OS << "const SourceInfo &I);\n"; + OS << "#endif\n"; + + // Emit the dispatch implementation in the source. + OS << "#if defined(GET_EVAL_IMPL) || defined(GET_LINK_IMPL)\n"; + OS << "bool \n"; + OS << "#if defined(GET_EVAL_IMPL)\n"; + OS << "EvalEmitter\n"; + OS << "#else\n"; + OS << "ByteCodeEmitter\n"; + OS << "#endif\n"; + OS << "::emit" << N << "("; + for (size_t I = 0, N = Types->size(); I < N; ++I) + OS << "PrimType T" << I << ", "; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << Args[I]->getValueAsString("Name") << " A" << I << ", "; + OS << "const SourceInfo &I) {\n"; + + std::function<void(size_t, const Twine &)> Rec; + llvm::SmallVector<Record *, 2> TS; + Rec = [this, &Rec, &OS, Types, &Args, R, &TS, N](size_t I, const Twine &ID) { + if (I >= Types->size()) { + // Print a call to the emitter method. + // Custom evaluator methods dispatch to template methods. + if (R->getValueAsBit("HasCustomEval")) { + OS << "#ifdef GET_LINK_IMPL\n"; + OS << "return emit" << ID << "\n"; + OS << "#else\n"; + OS << "return emit" << N; + PrintTypes(OS, TS); + OS << "\n#endif\n"; + } else { + OS << "return emit" << ID; + } + + OS << "("; + for (size_t I = 0; I < Args.size(); ++I) { + OS << "A" << I << ", "; + } + OS << "I);\n"; + return; + } + + // Print a switch statement selecting T. + if (auto *TypeClass = dyn_cast<DefInit>(Types->getElement(I))) { + OS << "switch (T" << I << "){\n"; + auto Cases = TypeClass->getDef()->getValueAsListOfDefs("Types"); + for (auto *Case : Cases) { + OS << "case PT_" << Case->getName() << ":\n"; + TS.push_back(Case); + Rec(I + 1, ID + Case->getName()); + TS.pop_back(); + } + // Emit a default case if not all types are present. + if (Cases.size() < NumTypes) + OS << "default: llvm_unreachable(\"invalid type\");\n"; + OS << "}\n"; + OS << "llvm_unreachable(\"invalid enum value\");\n"; + } else { + PrintFatalError("Expected a type class"); + } + }; + Rec(0, N); + + OS << "}\n"; + OS << "#endif\n"; +} + +void ClangOpcodesEmitter::EmitEval(raw_ostream &OS, StringRef N, Record *R) { + if (R->getValueAsBit("HasCustomEval")) + return; + + OS << "#ifdef GET_EVAL_IMPL\n"; + Enumerate(R, N, [this, R, &N, &OS](ArrayRef<Record *> TS, const Twine &ID) { + auto Args = R->getValueAsListOfDefs("Args"); + + OS << "bool EvalEmitter::emit" << ID << "("; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << Args[I]->getValueAsString("Name") << " A" << I << ","; + OS << "const SourceInfo &L) {\n"; + OS << "if (!isActive()) return true;\n"; + OS << "CurrentSource = L;\n"; + + OS << "return " << N; + PrintTypes(OS, TS); + OS << "(S, OpPC"; + for (size_t I = 0, N = Args.size(); I < N; ++I) + OS << ", A" << I; + OS << ");\n"; + OS << "}\n"; + }); + + OS << "#endif\n"; +} + +void ClangOpcodesEmitter::PrintTypes(raw_ostream &OS, ArrayRef<Record *> Types) { + if (Types.empty()) + return; + OS << "<"; + for (size_t I = 0, N = Types.size(); I < N; ++I) { + if (I != 0) + OS << ", "; + OS << "PT_" << Types[I]->getName(); + } + OS << ">"; +} + +namespace clang { + +void EmitClangOpcodes(RecordKeeper &Records, raw_ostream &OS) { + ClangOpcodesEmitter(Records).run(OS); +} + +} // end namespace clang diff --git a/utils/TableGen/TableGen.cpp b/utils/TableGen/TableGen.cpp index b9ec90fd5b..a923700193 100644 --- a/utils/TableGen/TableGen.cpp +++ b/utils/TableGen/TableGen.cpp @@ -47,6 +47,7 @@ enum ActionType { GenClangCommentNodes, GenClangDeclNodes, GenClangStmtNodes, + GenClangOpcodes, GenClangSACheckers, GenClangCommentHTMLTags, GenClangCommentHTMLTagsProperties, @@ -129,6 +130,8 @@ cl::opt<ActionType> Action( "Generate Clang AST declaration nodes"), clEnumValN(GenClangStmtNodes, "gen-clang-stmt-nodes", "Generate Clang AST statement nodes"), + clEnumValN(GenClangOpcodes, "gen-clang-opcodes", + "Generate Clang constexpr interpreter opcodes"), clEnumValN(GenClangSACheckers, "gen-clang-sa-checkers", "Generate Clang Static Analyzer checkers"), clEnumValN(GenClangCommentHTMLTags, "gen-clang-comment-html-tags", @@ -251,6 +254,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) { case GenClangStmtNodes: EmitClangASTNodes(Records, OS, "Stmt", ""); break; + case GenClangOpcodes: + EmitClangOpcodes(Records, OS); + break; case GenClangSACheckers: EmitClangSACheckers(Records, OS); break; diff --git a/utils/TableGen/TableGenBackends.h b/utils/TableGen/TableGenBackends.h index 02af66c5bf..f9ee477efd 100644 --- a/utils/TableGen/TableGenBackends.h +++ b/utils/TableGen/TableGenBackends.h @@ -77,6 +77,7 @@ void EmitClangCommentCommandInfo(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangCommentCommandList(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangOpcodes(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitNeon(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitFP16(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); |