When reverting a commit it is customary to mention *why* it is getting reverted.
On Mon, Sep 2, 2019 at 2:33 PM Nandor Licker via cfe-commits <cfe-commits@lists.llvm.org> wrote: > > Author: nand > Date: Mon Sep 2 04:34:47 2019 > New Revision: 370642 > > URL: http://llvm.org/viewvc/llvm-project?rev=370642&view=rev > Log: > Revert [Clang Interpreter] Initial patch for the constexpr interpreter > > This reverts r370636 (git commit 8327fed9475a14c3376b4860c75370c730e08f33) > > Removed: > cfe/trunk/docs/ConstantInterpreter.rst > cfe/trunk/include/clang/AST/OptionalDiagnostic.h > cfe/trunk/lib/AST/Interp/ > cfe/trunk/test/AST/Interp/ > cfe/trunk/utils/TableGen/ClangOpcodesEmitter.cpp > Modified: > cfe/trunk/docs/index.rst > cfe/trunk/include/clang/AST/ASTContext.h > cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td > cfe/trunk/include/clang/Basic/LangOptions.def > cfe/trunk/include/clang/Driver/Options.td > cfe/trunk/lib/AST/ASTContext.cpp > cfe/trunk/lib/AST/CMakeLists.txt > cfe/trunk/lib/AST/ExprConstant.cpp > cfe/trunk/lib/Driver/ToolChains/Clang.cpp > cfe/trunk/lib/Frontend/CompilerInvocation.cpp > cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp > cfe/trunk/test/SemaCXX/constexpr-many-arguments.cpp > cfe/trunk/test/SemaCXX/shift.cpp > cfe/trunk/utils/TableGen/CMakeLists.txt > cfe/trunk/utils/TableGen/TableGen.cpp > cfe/trunk/utils/TableGen/TableGenBackends.h > > Removed: cfe/trunk/docs/ConstantInterpreter.rst > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ConstantInterpreter.rst?rev=370641&view=auto > ============================================================================== > --- cfe/trunk/docs/ConstantInterpreter.rst (original) > +++ cfe/trunk/docs/ConstantInterpreter.rst (removed) > @@ -1,194 +0,0 @@ > -==================== > -Constant Interpreter > -==================== > - > -.. contents:: > - :local: > - > -Introduction > -============ > - > -The constexpr interpreter aims to replace the existing tree evaluator in > clang, improving performance on constructs which are executed inefficiently > by the evaluator. The interpreter is activated using the following flags: > - > -* ``-fexperimental-new-constant-interpreter`` enables the interpreter, > falling back to the evaluator for unsupported features > -* ``-fforce-experimental-new-constant-interpreter`` forces the use of the > interpreter, bailing out if an unsupported feature is encountered > - > -Bytecode Compilation > -==================== > - > -Bytecode compilation is handled in ``ByteCodeStmtGen.h`` for statements and > ``ByteCodeExprGen.h`` for expressions. The compiler has two different > backends: one to generate bytecode for functions (``ByteCodeEmitter``) and > one to directly evaluate expressions as they are compiled, without generating > bytecode (``EvalEmitter``). All functions are compiled to bytecode, while > toplevel expressions used in constant contexts are directly evaluated since > the bytecode would never be reused. This mechanism aims to pave the way > towards replacing the evaluator, improving its performance on functions and > loops, while being just as fast on single-use toplevel expressions. > - > -The interpreter relies on stack-based, strongly-typed opcodes. The glue > logic between the code generator, along with the enumeration and description > of opcodes, can be found in ``Opcodes.td``. The opcodes are implemented as > generic template methods in ``Interp.h`` and instantiated with the relevant > primitive types by the interpreter loop or by the evaluating emitter. > - > -Primitive Types > ---------------- > - > -* ``PT_{U|S}int{8|16|32|64}`` > - > - Signed or unsigned integers of a specific bit width, implemented using the > ```Integral``` type. > - > -* ``PT_{U|S}intFP`` > - > - Signed or unsigned integers of an arbitrary, but fixed width used to > implement > - integral types which are required by the target, but are not supported by > the host. > - Under the hood, they rely on APValue. The ``Integral`` specialisation for > these > - types is required by opcodes to share an implementation with fixed > integrals. > - > -* ``PT_Bool`` > - > - Representation for boolean types, essentially a 1-bit unsigned > ``Integral``. > - > -* ``PT_RealFP`` > - > - Arbitrary, but fixed precision floating point numbers. Could be > specialised in > - the future similarly to integers in order to improve floating point > performance. > - > -* ``PT_Ptr`` > - > - Pointer type, defined in ``"Pointer.h"``. > - > -* ``PT_FnPtr`` > - > - Function pointer type, can also be a null function pointer. Defined in > ``"Pointer.h"``. > - > -* ``PT_MemPtr`` > - > - Member pointer type, can also be a null member pointer. Defined in > ``"Pointer.h"`` > - > -Composite types > ---------------- > - > -The interpreter distinguishes two kinds of composite types: arrays and > records. Unions are represented as records, except a single field can be > marked as active. The contents of inactive fields are kept until they > -are reactivated and overwritten. > - > - > -Bytecode Execution > -================== > - > -Bytecode is executed using a stack-based interpreter. The execution context > consists of an ``InterpStack``, along with a chain of ``InterpFrame`` objects > storing the call frames. Frames are built by call instructions and destroyed > by return instructions. They perform one allocation to reserve space for all > locals in a single block. These objects store all the required information to > emit stack traces whenever evaluation fails. > - > -Memory Organisation > -=================== > - > -Memory management in the interpreter relies on 3 data structures: ``Block`` > -object which store the data and associated inline metadata, ``Pointer`` > objects > -which refer to or into blocks, and ``Descriptor`` structures which describe > -blocks and subobjects nested inside blocks. > - > -Blocks > ------- > - > -Blocks contain data interleaved with metadata. They are allocated either > statically > -in the code generator (globals, static members, dummy parameter values etc.) > or > -dynamically in the interpreter, when creating the frame containing the local > variables > -of a function. Blocks are associated with a descriptor that characterises > the entire > -allocation, along with a few additional attributes: > - > -* ``IsStatic`` indicates whether the block has static duration in the > interpreter, i.e. it is not a local in a frame. > - > -* ``IsExtern`` indicates that the block was created for an extern and the > storage cannot be read or written. > - > -* ``DeclID`` identifies each global declaration (it is set to an invalid and > irrelevant value for locals) in order to prevent illegal writes and reads > involving globals and temporaries with static storage duration. > - > -Static blocks are never deallocated, but local ones might be deallocated > even when there are live pointers to them. Pointers are only valid as long as > the blocks they point to are valid, so a block with pointers to it whose > lifetime ends is kept alive until all pointers to it go out of scope. Since > the frame is destroyed on function exit, such blocks are turned into a > ``DeadBlock`` and copied to storage managed by the interpreter itself, not > the frame. Reads and writes to these blocks are illegal and cause an > appropriate diagnostic to be emitted. When the last pointer goes out of > scope, dead blocks are also deallocated. > - > -The lifetime of blocks is managed through 3 methods stored in the descriptor > of the block: > - > -* **CtorFn**: initializes the metadata which is store in the block, > alongside actual data. Invokes the default constructors of objects which are > not trivial (``Pointer``, ``RealFP``, etc.) > -* **DtorFn**: invokes the destructors of non-trivial objects. > -* **MoveFn**: moves a block to dead storage. > - > -Non-static blocks track all the pointers into them through an intrusive > doubly-linked list, this is required in order to adjust all pointers when > transforming a block into a dead block. > - > -Descriptors > ------------ > - > -Descriptor are generated at bytecode compilation time and contain > information required to determine if a particular memory access is allowed in > constexpr. Even though there is a single descriptor object, it encodes > information for several kinds of objects: > - > -* **Primitives** > - > - A block containing a primitive reserved storage only for the primitive. > - > -* **Arrays of primitives** > - > - An array of primitives contains a pointer to an ``InitMap`` storage as its > first field: the initialisation map is a bit map indicating all elements of > the array which were initialised. If the pointer is null, no elements were > initialised, while a value of ``(InitMap)-1`` indicates that the object was > fully initialised. when all fields are initialised, the map is deallocated > and replaced with that token. > - > - Array elements are stored sequentially, without padding, after the pointer > to the map. > - > -* **Arrays of composites and records** > - > - Each element in an array of composites is preceded by an > ``InlineDescriptor``. Descriptors and elements are stored sequentially in the > block. Records are laid out identically to arrays of composites: each field > and base class is preceded by an inline descriptor. The ``InlineDescriptor`` > has the following field: > - > - * **Offset**: byte offset into the array or record, used to step back to > the parent array or record. > - * **IsConst**: flag indicating if the field is const-qualified. > - * **IsInitialized**: flag indicating whether the field or element was > initialized. For non-primitive fields, this is only relevant for base classes. > - * **IsBase**: flag indicating whether the record is a base class. In that > case, the offset can be used to identify the derived class. > - * **IsActive**: indicates if the field is the active field of a union. > - * **IsMutable**: indicates if the field is marked as mutable. > - > -Inline descriptors are filled in by the `CtorFn` of blocks, which leaves > storage in an uninitialised, but valid state. > - > -Pointers > --------- > - > -Pointers track a ``Pointee``, the block to which they point or ``nullptr`` > for null pointers, along with a ``Base`` and an ``Offset``. The base > identifies the innermost field, while the offset points to an array element > relative to the base (including one-past-end pointers). Most subobject the > pointer points to in block, while the offset identifies the array element the > pointer points to. These two fields allow all pointers to be uniquely > identified and disambiguated. > - > -As an example, consider the following structure: > - > -.. code-block:: c > - > - struct A { > - struct B { > - int x; > - int y; > - } b; > - struct C { > - int a; > - int b; > - } c[2]; > - int z; > - }; > - constexpr A a; > - > -On the target, ``&a`` and ``&a.b.x`` are equal. So are ``&a.c[0]`` and > ``&a.c[0].a``. In the interpreter, all these pointers must be distinguished > since the are all allowed to address distinct range of memory. > - > -In the interpreter, the object would require 240 bytes of storage and would > have its field interleaved with metadata. The pointers which can be derived > to the object are illustrated in the following diagram: > - > -:: > - > - 0 16 32 40 56 64 80 96 112 120 136 144 160 176 184 200 208 > 224 240 > - > +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ > - + B | D | D | x | D | y | D | D | D | a | D | b | D | D | a | D | b | D | > z | > - > +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ > - ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ > - | | | | | | | &a.c[0].b | | &a.c[1].b | > - a |&a.b.x &a.y &a.c |&a.c[0].a |&a.c[1].a | > - &a.b &a.c[0] &a.c[1] &a.z > - > -The ``Base`` offset of all pointers points to the start of a field or an > array and is preceded by an inline descriptor (unless ``Base == 0``, pointing > to the root). All the relevant attributes can be read from either the inline > descriptor or the descriptor of the block. > - > -Array elements are identified by the ``Offset`` field of pointers, pointing > to past the inline descriptors for composites and before the actual data in > the case of primitive arrays. The ``Offset`` points to the offset where > primitives can be read from. As an example, ``a.c + 1`` would have the same > base as ``a.c`` since it is an element of ``a.c``, but its offset would point > to ``&a.c[1]``. The ``*`` operation narrows the scope of the pointer, > adjusting the base to ``&a.c[1]``. The reverse operator, ``&``, expands the > scope of ``&a.c[1]``, turning it into ``a.c + 1``. When a one-past-end > pointer is narrowed, its offset is set to ``-1`` to indicate that it is an > invalid value (expanding returns the past-the-end pointer). As a special > case, narrowing ``&a.c`` results in ``&a.c[0]``. The `narrow` and `expand` > methods can be used to follow the chain of equivalent pointers. > - > -TODO > -==== > - > -Missing Language Features > -------------------------- > - > -* Definition of externs must override previous declaration > -* Changing the active field of unions > -* Union copy constructors > -* ``typeid`` > -* ``volatile`` > -* ``__builtin_constant_p`` > -* ``std::initializer_list`` > -* lambdas > -* range-based for loops > -* ``vector_size`` > -* ``dynamic_cast`` > - > -Known Bugs > ----------- > - > -* Pointer comparison for equality needs to narrow/expand pointers > -* If execution fails, memory storing APInts and APFloats is leaked when the > stack is cleared > > Modified: cfe/trunk/docs/index.rst > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/index.rst?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/docs/index.rst (original) > +++ cfe/trunk/docs/index.rst Mon Sep 2 04:34:47 2019 > @@ -88,7 +88,6 @@ Design Documents > PCHInternals > ItaniumMangleAbiTags > HardwareAssistedAddressSanitizerDesign.rst > - ConstantInterpreter > > > Indices and tables > > Modified: cfe/trunk/include/clang/AST/ASTContext.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/include/clang/AST/ASTContext.h (original) > +++ cfe/trunk/include/clang/AST/ASTContext.h Mon Sep 2 04:34:47 2019 > @@ -139,12 +139,6 @@ class FullComment; > > } // namespace comments > > -namespace interp { > - > -class Context; > - > -} // namespace interp > - > struct TypeInfo { > uint64_t Width = 0; > unsigned Align = 0; > @@ -570,7 +564,6 @@ private: > const TargetInfo *Target = nullptr; > const TargetInfo *AuxTarget = nullptr; > clang::PrintingPolicy PrintingPolicy; > - std::unique_ptr<interp::Context> InterpContext; > > public: > IdentifierTable &Idents; > @@ -580,9 +573,6 @@ public: > IntrusiveRefCntPtr<ExternalASTSource> ExternalSource; > ASTMutationListener *Listener = nullptr; > > - /// Returns the clang bytecode interpreter context. > - interp::Context &getInterpContext(); > - > /// Container for either a single DynTypedNode or for an ArrayRef to > /// DynTypedNode. For use with ParentMap. > class DynTypedNodeList { > > Removed: cfe/trunk/include/clang/AST/OptionalDiagnostic.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/OptionalDiagnostic.h?rev=370641&view=auto > ============================================================================== > --- cfe/trunk/include/clang/AST/OptionalDiagnostic.h (original) > +++ cfe/trunk/include/clang/AST/OptionalDiagnostic.h (removed) > @@ -1,78 +0,0 @@ > -//===- OptionalDiagnostic.h - An optional diagnostic ------------*- 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 > -// > -//===----------------------------------------------------------------------===// > -// > -/// \file > -/// Implements a partial diagnostic which may not be emitted. > -// > -//===----------------------------------------------------------------------===// > - > -#ifndef LLVM_CLANG_AST_OPTIONALDIAGNOSTIC_H > -#define LLVM_CLANG_AST_OPTIONALDIAGNOSTIC_H > - > -#include "clang/AST/APValue.h" > -#include "clang/Basic/PartialDiagnostic.h" > -#include "llvm/ADT/APFloat.h" > -#include "llvm/ADT/APSInt.h" > -#include "llvm/ADT/SmallVector.h" > -#include "llvm/ADT/StringRef.h" > - > -namespace clang { > - > -/// A partial diagnostic which we might know in advance that we are not going > -/// to emit. > -class OptionalDiagnostic { > - PartialDiagnostic *Diag; > - > -public: > - explicit OptionalDiagnostic(PartialDiagnostic *Diag = nullptr) : > Diag(Diag) {} > - > - template <typename T> OptionalDiagnostic &operator<<(const T &v) { > - if (Diag) > - *Diag << v; > - return *this; > - } > - > - OptionalDiagnostic &operator<<(const llvm::APSInt &I) { > - if (Diag) { > - SmallVector<char, 32> Buffer; > - I.toString(Buffer); > - *Diag << StringRef(Buffer.data(), Buffer.size()); > - } > - return *this; > - } > - > - OptionalDiagnostic &operator<<(const llvm::APFloat &F) { > - if (Diag) { > - // FIXME: Force the precision of the source value down so we don't > - // print digits which are usually useless (we don't really care here if > - // we truncate a digit by accident in edge cases). Ideally, > - // APFloat::toString would automatically print the shortest > - // representation which rounds to the correct value, but it's a bit > - // tricky to implement. Could use std::to_chars. > - unsigned precision = > llvm::APFloat::semanticsPrecision(F.getSemantics()); > - precision = (precision * 59 + 195) / 196; > - SmallVector<char, 32> Buffer; > - F.toString(Buffer, precision); > - *Diag << StringRef(Buffer.data(), Buffer.size()); > - } > - return *this; > - } > - > - OptionalDiagnostic &operator<<(const APFixedPoint &FX) { > - if (Diag) { > - SmallVector<char, 32> Buffer; > - FX.toString(Buffer); > - *Diag << StringRef(Buffer.data(), Buffer.size()); > - } > - return *this; > - } > -}; > - > -} // namespace clang > - > -#endif > > Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original) > +++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Mon Sep 2 04:34:47 > 2019 > @@ -228,8 +228,6 @@ def note_constexpr_bit_cast_invalid_subt > def note_constexpr_bit_cast_indet_dest : Note< > "indeterminate value can only initialize an object of type 'unsigned char'" > "%select{, 'char',|}1 or 'std::byte'; %0 is invalid">; > -def err_experimental_clang_interp_failed : Error< > - "the experimental clang interpreter failed to evaluate an expression">; > > def warn_integer_constant_overflow : Warning< > "overflow in expression; result is %0 with type %1">, > > Modified: cfe/trunk/include/clang/Basic/LangOptions.def > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/LangOptions.def?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/include/clang/Basic/LangOptions.def (original) > +++ cfe/trunk/include/clang/Basic/LangOptions.def Mon Sep 2 04:34:47 2019 > @@ -288,10 +288,6 @@ BENIGN_LANGOPT(ConstexprCallDepth, 32, 5 > "maximum constexpr call depth") > BENIGN_LANGOPT(ConstexprStepLimit, 32, 1048576, > "maximum constexpr evaluation steps") > -BENIGN_LANGOPT(EnableNewConstInterp, 1, 0, > - "enable the experimental new constant interpreter") > -BENIGN_LANGOPT(ForceNewConstInterp, 1, 0, > - "force the use of the experimental new constant interpreter") > BENIGN_LANGOPT(BracketDepth, 32, 256, > "maximum bracket nesting depth") > BENIGN_LANGOPT(NumLargeByValueCopy, 32, 0, > > Modified: cfe/trunk/include/clang/Driver/Options.td > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/include/clang/Driver/Options.td (original) > +++ cfe/trunk/include/clang/Driver/Options.td Mon Sep 2 04:34:47 2019 > @@ -838,10 +838,6 @@ def fconstant_cfstrings : Flag<["-"], "f > def fconstant_string_class_EQ : Joined<["-"], "fconstant-string-class=">, > Group<f_Group>; > def fconstexpr_depth_EQ : Joined<["-"], "fconstexpr-depth=">, Group<f_Group>; > def fconstexpr_steps_EQ : Joined<["-"], "fconstexpr-steps=">, Group<f_Group>; > -def fexperimental_new_constant_interpreter : Flag<["-"], > "fexperimental-new-constant-interpreter">, Group<f_Group>, > - HelpText<"Enable the experimental new constant interpreter">, > Flags<[CC1Option]>; > -def fforce_experimental_new_constant_interpreter : Flag<["-"], > "fforce-experimental-new-constant-interpreter">, Group<f_Group>, > - HelpText<"Force the use of the experimental new constant interpreter, > failing on missing features">, Flags<[CC1Option]>; > def fconstexpr_backtrace_limit_EQ : Joined<["-"], > "fconstexpr-backtrace-limit=">, > Group<f_Group>; > def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, > Group<f_clang_Group>, Flags<[NoArgumentUnused, CoreOption]>, > > Modified: cfe/trunk/lib/AST/ASTContext.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/lib/AST/ASTContext.cpp (original) > +++ cfe/trunk/lib/AST/ASTContext.cpp Mon Sep 2 04:34:47 2019 > @@ -12,7 +12,6 @@ > > #include "clang/AST/ASTContext.h" > #include "CXXABI.h" > -#include "Interp/Context.h" > #include "clang/AST/APValue.h" > #include "clang/AST/ASTMutationListener.h" > #include "clang/AST/ASTTypeTraits.h" > @@ -784,13 +783,6 @@ CXXABI *ASTContext::createCXXABI(const T > llvm_unreachable("Invalid CXXABI type!"); > } > > -interp::Context &ASTContext::getInterpContext() { > - if (!InterpContext) { > - InterpContext.reset(new interp::Context(*this)); > - } > - return *InterpContext.get(); > -} > - > static const LangASMap *getAddressSpaceMap(const TargetInfo &T, > const LangOptions &LOpts) { > if (LOpts.FakeAddressSpaceMap) { > > Modified: cfe/trunk/lib/AST/CMakeLists.txt > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CMakeLists.txt?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/lib/AST/CMakeLists.txt (original) > +++ cfe/trunk/lib/AST/CMakeLists.txt Mon Sep 2 04:34:47 2019 > @@ -4,8 +4,6 @@ set(LLVM_LINK_COMPONENTS > Support > ) > > -add_subdirectory(Interp) > - > add_clang_library(clangAST > APValue.cpp > ASTConsumer.cpp > @@ -83,6 +81,5 @@ add_clang_library(clangAST > > LINK_LIBS > clangBasic > - clangInterp > clangLex > ) > > Modified: cfe/trunk/lib/AST/ExprConstant.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/lib/AST/ExprConstant.cpp (original) > +++ cfe/trunk/lib/AST/ExprConstant.cpp Mon Sep 2 04:34:47 2019 > @@ -32,11 +32,6 @@ > // > > //===----------------------------------------------------------------------===// > > -#include <cstring> > -#include <functional> > -#include "Interp/Context.h" > -#include "Interp/Frame.h" > -#include "Interp/State.h" > #include "clang/AST/APValue.h" > #include "clang/AST/ASTContext.h" > #include "clang/AST/ASTDiagnostic.h" > @@ -46,7 +41,6 @@ > #include "clang/AST/CurrentSourceLocExprScope.h" > #include "clang/AST/Expr.h" > #include "clang/AST/OSLog.h" > -#include "clang/AST/OptionalDiagnostic.h" > #include "clang/AST/RecordLayout.h" > #include "clang/AST/StmtVisitor.h" > #include "clang/AST/TypeLoc.h" > @@ -57,6 +51,8 @@ > #include "llvm/ADT/SmallBitVector.h" > #include "llvm/Support/SaveAndRestore.h" > #include "llvm/Support/raw_ostream.h" > +#include <cstring> > +#include <functional> > > #define DEBUG_TYPE "exprconstant" > > @@ -70,8 +66,8 @@ static bool IsGlobalLValue(APValue::LVal > > namespace { > struct LValue; > - class CallStackFrame; > - class EvalInfo; > + struct CallStackFrame; > + struct EvalInfo; > > using SourceLocExprScopeGuard = > CurrentSourceLocExprScope::SourceLocExprScopeGuard; > @@ -226,6 +222,12 @@ namespace { > return MostDerivedLength; > } > > + // The order of this enum is important for diagnostics. > + enum CheckSubobjectKind { > + CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex, > + CSK_Real, CSK_Imag > + }; > + > /// A path from a glvalue to a subobject of that glvalue. > struct SubobjectDesignator { > /// True if the subobject was named in a manner not supported by C++11. > Such > @@ -478,8 +480,7 @@ namespace { > }; > > /// A stack frame in the constexpr call stack. > - class CallStackFrame : public interp::Frame { > - public: > + struct CallStackFrame { > EvalInfo &Info; > > /// Parent - The caller of this stack frame. > @@ -573,12 +574,6 @@ namespace { > } > > APValue &createTemporary(const void *Key, bool IsLifetimeExtended); > - > - void describe(llvm::raw_ostream &OS) override; > - > - Frame *getCaller() const override { return Caller; } > - SourceLocation getCallLocation() const override { return CallLoc; } > - const FunctionDecl *getCallee() const override { return Callee; } > }; > > /// Temporarily override 'this'. > @@ -597,6 +592,59 @@ namespace { > const LValue *OldThis; > }; > > + /// A partial diagnostic which we might know in advance that we are not > going > + /// to emit. > + class OptionalDiagnostic { > + PartialDiagnostic *Diag; > + > + public: > + explicit OptionalDiagnostic(PartialDiagnostic *Diag = nullptr) > + : Diag(Diag) {} > + > + template<typename T> > + OptionalDiagnostic &operator<<(const T &v) { > + if (Diag) > + *Diag << v; > + return *this; > + } > + > + OptionalDiagnostic &operator<<(const APSInt &I) { > + if (Diag) { > + SmallVector<char, 32> Buffer; > + I.toString(Buffer); > + *Diag << StringRef(Buffer.data(), Buffer.size()); > + } > + return *this; > + } > + > + OptionalDiagnostic &operator<<(const APFloat &F) { > + if (Diag) { > + // FIXME: Force the precision of the source value down so we don't > + // print digits which are usually useless (we don't really care here > if > + // we truncate a digit by accident in edge cases). Ideally, > + // APFloat::toString would automatically print the shortest > + // representation which rounds to the correct value, but it's a bit > + // tricky to implement. > + unsigned precision = > + llvm::APFloat::semanticsPrecision(F.getSemantics()); > + precision = (precision * 59 + 195) / 196; > + SmallVector<char, 32> Buffer; > + F.toString(Buffer, precision); > + *Diag << StringRef(Buffer.data(), Buffer.size()); > + } > + return *this; > + } > + > + OptionalDiagnostic &operator<<(const APFixedPoint &FX) { > + if (Diag) { > + SmallVector<char, 32> Buffer; > + FX.toString(Buffer); > + *Diag << StringRef(Buffer.data(), Buffer.size()); > + } > + return *this; > + } > + }; > + > /// A cleanup, and a flag indicating whether it is lifetime-extended. > class Cleanup { > llvm::PointerIntPair<APValue*, 1, bool> Value; > @@ -659,8 +707,7 @@ namespace { > /// rules. For example, the RHS of (0 && foo()) is not evaluated. We can > /// evaluate the expression regardless of what the RHS is, but C only > allows > /// certain things in certain situations. > - class EvalInfo : public interp::State { > - public: > + struct EvalInfo { > ASTContext &Ctx; > > /// EvalStatus - Contains information about the evaluation. > @@ -680,13 +727,6 @@ namespace { > /// we will evaluate. > unsigned StepsLeft; > > - /// Force the use of the experimental new constant interpreter, bailing > out > - /// with an error if a feature is not supported. > - bool ForceNewConstInterp; > - > - /// Enable the experimental new constant interpreter. > - bool EnableNewConstInterp; > - > /// BottomFrame - The frame in which evaluation started. This must be > /// initialized after CurrentCall and CallStackDepth. > CallStackFrame BottomFrame; > @@ -797,7 +837,7 @@ namespace { > > /// Are we checking whether the expression is a potential constant > /// expression? > - bool checkingPotentialConstantExpression() const override { > + bool checkingPotentialConstantExpression() const { > return EvalMode == EM_PotentialConstantExpression || > EvalMode == EM_PotentialConstantExpressionUnevaluated; > } > @@ -805,28 +845,25 @@ namespace { > /// Are we checking an expression for overflow? > // FIXME: We should check for any kind of undefined or suspicious > behavior > // in such constructs, not just overflow. > - bool checkingForOverflow() const override { > - return EvalMode == EM_EvaluateForOverflow; > - } > + bool checkingForOverflow() { return EvalMode == EM_EvaluateForOverflow; } > > EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) > - : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), > CurrentCall(nullptr), > - CallStackDepth(0), NextCallIndex(1), > - StepsLeft(getLangOpts().ConstexprStepLimit), > - ForceNewConstInterp(getLangOpts().ForceNewConstInterp), > - EnableNewConstInterp(ForceNewConstInterp || > - getLangOpts().EnableNewConstInterp), > - BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), > - EvaluatingDecl((const ValueDecl *)nullptr), > - EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), > - HasFoldFailureDiagnostic(false), InConstantContext(false), > - EvalMode(Mode) {} > + : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), > CurrentCall(nullptr), > + CallStackDepth(0), NextCallIndex(1), > + StepsLeft(getLangOpts().ConstexprStepLimit), > + BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), > + EvaluatingDecl((const ValueDecl *)nullptr), > + EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), > + HasFoldFailureDiagnostic(false), > + InConstantContext(false), EvalMode(Mode) {} > > void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { > EvaluatingDecl = Base; > EvaluatingDeclValue = &Value; > } > > + const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); } > + > bool CheckCallLimit(SourceLocation Loc) { > // Don't perform any constexpr calls (other than the call we're > checking) > // when checking a potential constant expression. > @@ -870,52 +907,118 @@ namespace { > } > > private: > - interp::Frame *getCurrentFrame() override { return CurrentCall; } > - const interp::Frame *getBottomFrame() const override { return > &BottomFrame; } > + /// Add a diagnostic to the diagnostics list. > + PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) { > + PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator()); > + EvalStatus.Diag->push_back(std::make_pair(Loc, PD)); > + return EvalStatus.Diag->back().second; > + } > > - bool hasActiveDiagnostic() override { return HasActiveDiagnostic; } > - void setActiveDiagnostic(bool Flag) override { HasActiveDiagnostic = > Flag; } > + /// Add notes containing a call stack to the current point of evaluation. > + void addCallStack(unsigned Limit); > > - void setFoldFailureDiagnostic(bool Flag) override { > - HasFoldFailureDiagnostic = Flag; > - } > + private: > + OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId, > + unsigned ExtraNotes, bool IsCCEDiag) { > > - Expr::EvalStatus &getEvalStatus() const override { return EvalStatus; } > - > - ASTContext &getCtx() const override { return Ctx; } > - > - // If we have a prior diagnostic, it will be noting that the expression > - // isn't a constant expression. This diagnostic is more important, > - // unless we require this evaluation to produce a constant expression. > - // > - // FIXME: We might want to show both diagnostics to the user in > - // EM_ConstantFold mode. > - bool hasPriorDiagnostic() override { > - if (!EvalStatus.Diag->empty()) { > - switch (EvalMode) { > - case EM_ConstantFold: > - case EM_IgnoreSideEffects: > - case EM_EvaluateForOverflow: > - if (!HasFoldFailureDiagnostic) > - break; > - // We've already failed to fold something. Keep that diagnostic. > - LLVM_FALLTHROUGH; > - case EM_ConstantExpression: > - case EM_PotentialConstantExpression: > - case EM_ConstantExpressionUnevaluated: > - case EM_PotentialConstantExpressionUnevaluated: > - setActiveDiagnostic(false); > - return true; > + if (EvalStatus.Diag) { > + // If we have a prior diagnostic, it will be noting that the > expression > + // isn't a constant expression. This diagnostic is more important, > + // unless we require this evaluation to produce a constant > expression. > + // > + // FIXME: We might want to show both diagnostics to the user in > + // EM_ConstantFold mode. > + if (!EvalStatus.Diag->empty()) { > + switch (EvalMode) { > + case EM_ConstantFold: > + case EM_IgnoreSideEffects: > + case EM_EvaluateForOverflow: > + if (!HasFoldFailureDiagnostic) > + break; > + // We've already failed to fold something. Keep that diagnostic. > + LLVM_FALLTHROUGH; > + case EM_ConstantExpression: > + case EM_PotentialConstantExpression: > + case EM_ConstantExpressionUnevaluated: > + case EM_PotentialConstantExpressionUnevaluated: > + HasActiveDiagnostic = false; > + return OptionalDiagnostic(); > + } > } > + > + unsigned CallStackNotes = CallStackDepth - 1; > + unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit(); > + if (Limit) > + CallStackNotes = std::min(CallStackNotes, Limit + 1); > + if (checkingPotentialConstantExpression()) > + CallStackNotes = 0; > + > + HasActiveDiagnostic = true; > + HasFoldFailureDiagnostic = !IsCCEDiag; > + EvalStatus.Diag->clear(); > + EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes); > + addDiag(Loc, DiagId); > + if (!checkingPotentialConstantExpression()) > + addCallStack(Limit); > + return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second); > } > - return false; > + HasActiveDiagnostic = false; > + return OptionalDiagnostic(); > + } > + public: > + // Diagnose that the evaluation could not be folded (FF => FoldFailure) > + OptionalDiagnostic > + FFDiag(SourceLocation Loc, > + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, > + unsigned ExtraNotes = 0) { > + return Diag(Loc, DiagId, ExtraNotes, false); > + } > + > + OptionalDiagnostic FFDiag(const Expr *E, diag::kind DiagId > + = diag::note_invalid_subexpr_in_const_expr, > + unsigned ExtraNotes = 0) { > + if (EvalStatus.Diag) > + return Diag(E->getExprLoc(), DiagId, ExtraNotes, /*IsCCEDiag*/false); > + HasActiveDiagnostic = false; > + return OptionalDiagnostic(); > } > > - unsigned getCallStackDepth() override { > - return CallStackDepth; > + /// Diagnose that the evaluation does not produce a C++11 core constant > + /// expression. > + /// > + /// FIXME: Stop evaluating if we're in EM_ConstantExpression or > + /// EM_PotentialConstantExpression mode and we produce one of these. > + OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId > + = diag::note_invalid_subexpr_in_const_expr, > + unsigned ExtraNotes = 0) { > + // Don't override a previous diagnostic. Don't bother collecting > + // diagnostics if we're evaluating for overflow. > + if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) { > + HasActiveDiagnostic = false; > + return OptionalDiagnostic(); > + } > + return Diag(Loc, DiagId, ExtraNotes, true); > + } > + OptionalDiagnostic CCEDiag(const Expr *E, diag::kind DiagId > + = diag::note_invalid_subexpr_in_const_expr, > + unsigned ExtraNotes = 0) { > + return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes); > + } > + /// Add a note to a prior diagnostic. > + OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId) { > + if (!HasActiveDiagnostic) > + return OptionalDiagnostic(); > + return OptionalDiagnostic(&addDiag(Loc, DiagId)); > + } > + > + /// Add a stack of notes to a prior diagnostic. > + void addNotes(ArrayRef<PartialDiagnosticAt> Diags) { > + if (HasActiveDiagnostic) { > + EvalStatus.Diag->insert(EvalStatus.Diag->end(), > + Diags.begin(), Diags.end()); > + } > } > > - public: > /// Should we continue evaluation after encountering a side-effect that > we > /// couldn't model? > bool keepEvaluatingAfterSideEffect() { > @@ -961,14 +1064,14 @@ namespace { > /// Note that we hit something that was technically undefined behavior, > but > /// that we can evaluate past it (such as signed overflow or > floating-point > /// division by zero.) > - bool noteUndefinedBehavior() override { > + bool noteUndefinedBehavior() { > EvalStatus.HasUndefinedBehavior = true; > return keepEvaluatingAfterUndefinedBehavior(); > } > > /// Should we continue evaluation as much as possible after encountering > a > /// construct which can't be reduced to a value? > - bool keepEvaluatingAfterFailure() const override { > + bool keepEvaluatingAfterFailure() { > if (!StepsLeft) > return false; > > @@ -1218,6 +1321,62 @@ APValue &CallStackFrame::createTemporary > return Result; > } > > +static void describeCall(CallStackFrame *Frame, raw_ostream &Out); > + > +void EvalInfo::addCallStack(unsigned Limit) { > + // Determine which calls to skip, if any. > + unsigned ActiveCalls = CallStackDepth - 1; > + unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart; > + if (Limit && Limit < ActiveCalls) { > + SkipStart = Limit / 2 + Limit % 2; > + SkipEnd = ActiveCalls - Limit / 2; > + } > + > + // Walk the call stack and add the diagnostics. > + unsigned CallIdx = 0; > + for (CallStackFrame *Frame = CurrentCall; Frame != &BottomFrame; > + Frame = Frame->Caller, ++CallIdx) { > + // Skip this call? > + if (CallIdx >= SkipStart && CallIdx < SkipEnd) { > + if (CallIdx == SkipStart) { > + // Note that we're skipping calls. > + addDiag(Frame->CallLoc, diag::note_constexpr_calls_suppressed) > + << unsigned(ActiveCalls - Limit); > + } > + continue; > + } > + > + // Use a different note for an inheriting constructor, because from the > + // user's perspective it's not really a function at all. > + if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(Frame->Callee)) { > + if (CD->isInheritingConstructor()) { > + addDiag(Frame->CallLoc, > diag::note_constexpr_inherited_ctor_call_here) > + << CD->getParent(); > + continue; > + } > + } > + > + SmallVector<char, 128> Buffer; > + llvm::raw_svector_ostream Out(Buffer); > + describeCall(Frame, Out); > + addDiag(Frame->CallLoc, diag::note_constexpr_call_here) << Out.str(); > + } > +} > + > +/// Kinds of access we can perform on an object, for diagnostics. Note that > +/// we consider a member function call to be a kind of access, even though > +/// it is not formally an access of the object, because it has (largely) the > +/// same set of semantic restrictions. > +enum AccessKinds { > + AK_Read, > + AK_Assign, > + AK_Increment, > + AK_Decrement, > + AK_MemberCall, > + AK_DynamicCast, > + AK_TypeId, > +}; > + > static bool isModification(AccessKinds AK) { > switch (AK) { > case AK_Read: > @@ -1585,36 +1744,36 @@ static void negateAsSigned(APSInt &Int) > } > > /// Produce a string describing the given constexpr call. > -void CallStackFrame::describe(raw_ostream &Out) { > +static void describeCall(CallStackFrame *Frame, raw_ostream &Out) { > unsigned ArgIndex = 0; > - bool IsMemberCall = isa<CXXMethodDecl>(Callee) && > - !isa<CXXConstructorDecl>(Callee) && > - cast<CXXMethodDecl>(Callee)->isInstance(); > + bool IsMemberCall = isa<CXXMethodDecl>(Frame->Callee) && > + !isa<CXXConstructorDecl>(Frame->Callee) && > + cast<CXXMethodDecl>(Frame->Callee)->isInstance(); > > if (!IsMemberCall) > - Out << *Callee << '('; > + Out << *Frame->Callee << '('; > > - if (This && IsMemberCall) { > + if (Frame->This && IsMemberCall) { > APValue Val; > - This->moveInto(Val); > - Val.printPretty(Out, Info.Ctx, > - This->Designator.MostDerivedType); > + Frame->This->moveInto(Val); > + Val.printPretty(Out, Frame->Info.Ctx, > + Frame->This->Designator.MostDerivedType); > // FIXME: Add parens around Val if needed. > - Out << "->" << *Callee << '('; > + Out << "->" << *Frame->Callee << '('; > IsMemberCall = false; > } > > - for (FunctionDecl::param_const_iterator I = Callee->param_begin(), > - E = Callee->param_end(); I != E; ++I, ++ArgIndex) { > + for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(), > + E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) { > if (ArgIndex > (unsigned)IsMemberCall) > Out << ", "; > > const ParmVarDecl *Param = *I; > - const APValue &Arg = Arguments[ArgIndex]; > - Arg.printPretty(Out, Info.Ctx, Param->getType()); > + const APValue &Arg = Frame->Arguments[ArgIndex]; > + Arg.printPretty(Out, Frame->Info.Ctx, Param->getType()); > > if (ArgIndex == 0 && IsMemberCall) > - Out << "->" << *Callee << '('; > + Out << "->" << *Frame->Callee << '('; > } > > Out << ')'; > @@ -12117,18 +12276,6 @@ static bool EvaluateInPlace(APValue &Res > /// EvaluateAsRValue - Try to evaluate this expression, performing an > implicit > /// lvalue-to-rvalue cast if it is an lvalue. > static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) > { > - if (Info.EnableNewConstInterp) { > - auto &InterpCtx = Info.Ctx.getInterpContext(); > - switch (InterpCtx.evaluateAsRValue(Info, E, Result)) { > - case interp::InterpResult::Success: > - return true; > - case interp::InterpResult::Fail: > - return false; > - case interp::InterpResult::Bail: > - break; > - } > - } > - > if (E->getType().isNull()) > return false; > > @@ -12336,29 +12483,11 @@ bool Expr::EvaluateAsInitializer(APValue > Expr::EvalStatus EStatus; > EStatus.Diag = &Notes; > > - EvalInfo Info(Ctx, EStatus, VD->isConstexpr() > + EvalInfo InitInfo(Ctx, EStatus, VD->isConstexpr() > ? EvalInfo::EM_ConstantExpression > : EvalInfo::EM_ConstantFold); > - Info.setEvaluatingDecl(VD, Value); > - Info.InConstantContext = true; > - > - SourceLocation DeclLoc = VD->getLocation(); > - QualType DeclTy = VD->getType(); > - > - if (Info.EnableNewConstInterp) { > - auto &InterpCtx = const_cast<ASTContext &>(Ctx).getInterpContext(); > - switch (InterpCtx.evaluateAsInitializer(Info, VD, Value)) { > - case interp::InterpResult::Fail: > - // Bail out if an error was encountered. > - return false; > - case interp::InterpResult::Success: > - // Evaluation succeeded and value was set. > - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); > - case interp::InterpResult::Bail: > - // Evaluate the value again for the tree evaluator to use. > - break; > - } > - } > + InitInfo.setEvaluatingDecl(VD, Value); > + InitInfo.InConstantContext = true; > > LValue LVal; > LVal.set(VD); > @@ -12368,19 +12497,20 @@ bool Expr::EvaluateAsInitializer(APValue > // zero-initialized before any other initialization takes place. > // This behavior is not present in C. > if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() && > - !DeclTy->isReferenceType()) { > - ImplicitValueInitExpr VIE(DeclTy); > - if (!EvaluateInPlace(Value, Info, LVal, &VIE, > + !VD->getType()->isReferenceType()) { > + ImplicitValueInitExpr VIE(VD->getType()); > + if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE, > /*AllowNonLiteralTypes=*/true)) > return false; > } > > - if (!EvaluateInPlace(Value, Info, LVal, this, > + if (!EvaluateInPlace(Value, InitInfo, LVal, this, > /*AllowNonLiteralTypes=*/true) || > EStatus.HasSideEffects) > return false; > > - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); > + return CheckConstantExpression(InitInfo, VD->getLocation(), VD->getType(), > + Value); > } > > /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be > @@ -13055,18 +13185,6 @@ bool Expr::isPotentialConstantExpr(const > EvalInfo::EM_PotentialConstantExpression); > Info.InConstantContext = true; > > - // The constexpr VM attempts to compile all methods to bytecode here. > - if (Info.EnableNewConstInterp) { > - auto &InterpCtx = Info.Ctx.getInterpContext(); > - switch (InterpCtx.isPotentialConstantExpr(Info, FD)) { > - case interp::InterpResult::Success: > - case interp::InterpResult::Fail: > - return Diags.empty(); > - case interp::InterpResult::Bail: > - break; > - } > - } > - > const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD); > const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : > nullptr; > > > Modified: cfe/trunk/lib/Driver/ToolChains/Clang.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChains/Clang.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/lib/Driver/ToolChains/Clang.cpp (original) > +++ cfe/trunk/lib/Driver/ToolChains/Clang.cpp Mon Sep 2 04:34:47 2019 > @@ -4489,12 +4489,6 @@ void Clang::ConstructJob(Compilation &C, > CmdArgs.push_back(A->getValue()); > } > > - if (Args.hasArg(options::OPT_fexperimental_new_constant_interpreter)) > - CmdArgs.push_back("-fexperimental-new-constant-interpreter"); > - > - if (Args.hasArg(options::OPT_fforce_experimental_new_constant_interpreter)) > - CmdArgs.push_back("-fforce-experimental-new-constant-interpreter"); > - > if (Arg *A = Args.getLastArg(options::OPT_fbracket_depth_EQ)) { > CmdArgs.push_back("-fbracket-depth"); > CmdArgs.push_back(A->getValue()); > > Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original) > +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Mon Sep 2 04:34:47 2019 > @@ -2783,10 +2783,6 @@ static void ParseLangArgs(LangOptions &O > getLastArgIntValue(Args, OPT_fconstexpr_depth, 512, Diags); > Opts.ConstexprStepLimit = > getLastArgIntValue(Args, OPT_fconstexpr_steps, 1048576, Diags); > - Opts.EnableNewConstInterp = > - Args.hasArg(OPT_fexperimental_new_constant_interpreter); > - Opts.ForceNewConstInterp = > - Args.hasArg(OPT_fforce_experimental_new_constant_interpreter); > Opts.BracketDepth = getLastArgIntValue(Args, OPT_fbracket_depth, 256, > Diags); > Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing); > Opts.NumLargeByValueCopy = > > Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original) > +++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Mon Sep 2 04:34:47 > 2019 > @@ -414,6 +414,125 @@ namespace TypeId { > static_assert(side_effects()); > } > > +namespace Union { > + struct Base { > + int y; // expected-note {{here}} > + }; > + struct A : Base { > + int x; > + int arr[3]; > + union { int p, q; }; > + }; > + union B { > + A a; > + int b; > + }; > + constexpr int read_wrong_member() { // expected-error {{never produces a > constant}} > + B b = {.b = 1}; > + return b.a.x; // expected-note {{read of member 'a' of union with active > member 'b'}} > + } > + constexpr int change_member() { > + B b = {.b = 1}; > + b.a.x = 1; > + return b.a.x; > + } > + static_assert(change_member() == 1); > + constexpr int change_member_then_read_wrong_member() { // expected-error > {{never produces a constant}} > + B b = {.b = 1}; > + b.a.x = 1; > + return b.b; // expected-note {{read of member 'b' of union with active > member 'a'}} > + } > + constexpr int read_wrong_member_indirect() { // expected-error {{never > produces a constant}} > + B b = {.b = 1}; > + int *p = &b.a.y; > + return *p; // expected-note {{read of member 'a' of union with active > member 'b'}} > + } > + constexpr int read_uninitialized() { > + B b = {.b = 1}; > + int *p = &b.a.y; > + b.a.x = 1; > + return *p; // expected-note {{read of uninitialized object}} > + } > + static_assert(read_uninitialized() == 0); // expected-error {{constant}} > expected-note {{in call}} > + constexpr void write_wrong_member_indirect() { // expected-error {{never > produces a constant}} > + B b = {.b = 1}; > + int *p = &b.a.y; > + *p = 1; // expected-note {{assignment to member 'a' of union with active > member 'b'}} > + } > + constexpr int write_uninitialized() { > + B b = {.b = 1}; > + int *p = &b.a.y; > + b.a.x = 1; > + *p = 1; > + return *p; > + } > + static_assert(write_uninitialized() == 1); > + constexpr int change_member_indirectly() { > + B b = {.b = 1}; > + b.a.arr[1] = 1; > + int &r = b.a.y; > + r = 123; > + > + b.b = 2; > + b.a.y = 3; > + b.a.arr[2] = 4; > + return b.a.arr[2]; > + } > + static_assert(change_member_indirectly() == 4); > + constexpr B return_uninit() { > + B b = {.b = 1}; > + b.a.x = 2; > + return b; > + } > + constexpr B uninit = return_uninit(); // expected-error {{constant > expression}} expected-note {{subobject of type 'int' is not initialized}} > + static_assert(return_uninit().a.x == 2); > + constexpr A return_uninit_struct() { > + B b = {.b = 1}; > + b.a.x = 2; > + return b.a; > + } > + // FIXME: It's unclear that this should be valid. Copying a B involves > + // copying the object representation of the union, but copying an A > invokes a > + // copy constructor that copies the object elementwise, and reading from > + // b.a.y is undefined. > + static_assert(return_uninit_struct().x == 2); > + constexpr B return_init_all() { > + B b = {.b = 1}; > + b.a.x = 2; > + b.a.y = 3; > + b.a.arr[0] = 4; > + b.a.arr[1] = 5; > + b.a.arr[2] = 6; > + return b; > + } > + static_assert(return_init_all().a.x == 2); > + static_assert(return_init_all().a.y == 3); > + static_assert(return_init_all().a.arr[0] == 4); > + static_assert(return_init_all().a.arr[1] == 5); > + static_assert(return_init_all().a.arr[2] == 6); > + static_assert(return_init_all().a.p == 7); // expected-error {{}} > expected-note {{read of member 'p' of union with no active member}} > + static_assert(return_init_all().a.q == 8); // expected-error {{}} > expected-note {{read of member 'q' of union with no active member}} > + constexpr B init_all = return_init_all(); > + > + constexpr bool test_no_member_change = []{ > + union U { char dummy = {}; }; > + U u1; > + U u2; > + u1 = u2; > + return true; > + }(); > + > + struct S1 { > + int n; > + }; > + struct S2 : S1 {}; > + struct S3 : S2 {}; > + void f() { > + S3 s; > + s.n = 0; > + } > +} > + > namespace TwosComplementShifts { > using uint32 = __UINT32_TYPE__; > using int32 = __INT32_TYPE__; > > Modified: cfe/trunk/test/SemaCXX/constexpr-many-arguments.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constexpr-many-arguments.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/test/SemaCXX/constexpr-many-arguments.cpp (original) > +++ cfe/trunk/test/SemaCXX/constexpr-many-arguments.cpp Mon Sep 2 04:34:47 > 2019 > @@ -12,7 +12,7 @@ struct type2 > typedef type1 T; > constexpr type2(T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T > a08, T a09, > T a10, T a11, T a12, T a13, T a14, T a15, T a16, T > a17, T a18, T a19, > - T a20, T a21, T a22) > + T a20, T a21, T a22) > : my_data{a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, > a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, > a20, a21, a22} > @@ -32,7 +32,7 @@ constexpr type3 g > {0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, > {0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, > {0},{0},{0} > - }, > + }, > { > {0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, > {0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, > > Modified: cfe/trunk/test/SemaCXX/shift.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/shift.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/test/SemaCXX/shift.cpp (original) > +++ cfe/trunk/test/SemaCXX/shift.cpp Mon Sep 2 04:34:47 2019 > @@ -82,8 +82,3 @@ void vect_shift_2(vec16 *x, vec16 y) { * > void vect_shift_3(vec16 *x, vec8 y) { > *x = *x << y; // expected-error {{vector operands do not have the same > number of elements}} > } > -static_assert(-1 >> 1 == -1); > -static_assert(-1 >> 31 == -1); > -static_assert(-2 >> 1 == -1); > -static_assert(-3 >> 1 == -2); > -static_assert(-4 >> 1 == -2); > > Modified: cfe/trunk/utils/TableGen/CMakeLists.txt > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/CMakeLists.txt?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/utils/TableGen/CMakeLists.txt (original) > +++ cfe/trunk/utils/TableGen/CMakeLists.txt Mon Sep 2 04:34:47 2019 > @@ -8,7 +8,6 @@ add_tablegen(clang-tblgen CLANG > ClangCommentHTMLTagsEmitter.cpp > ClangDataCollectorsEmitter.cpp > ClangDiagnosticsEmitter.cpp > - ClangOpcodesEmitter.cpp > ClangOpenCLBuiltinEmitter.cpp > ClangOptionDocEmitter.cpp > ClangSACheckersEmitter.cpp > > Removed: cfe/trunk/utils/TableGen/ClangOpcodesEmitter.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangOpcodesEmitter.cpp?rev=370641&view=auto > ============================================================================== > --- cfe/trunk/utils/TableGen/ClangOpcodesEmitter.cpp (original) > +++ cfe/trunk/utils/TableGen/ClangOpcodesEmitter.cpp (removed) > @@ -1,360 +0,0 @@ > -//=== 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 > > Modified: cfe/trunk/utils/TableGen/TableGen.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/TableGen.cpp?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/utils/TableGen/TableGen.cpp (original) > +++ cfe/trunk/utils/TableGen/TableGen.cpp Mon Sep 2 04:34:47 2019 > @@ -47,7 +47,6 @@ enum ActionType { > GenClangCommentNodes, > GenClangDeclNodes, > GenClangStmtNodes, > - GenClangOpcodes, > GenClangSACheckers, > GenClangCommentHTMLTags, > GenClangCommentHTMLTagsProperties, > @@ -130,8 +129,6 @@ 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", > @@ -254,9 +251,6 @@ bool ClangTableGenMain(raw_ostream &OS, > case GenClangStmtNodes: > EmitClangASTNodes(Records, OS, "Stmt", ""); > break; > - case GenClangOpcodes: > - EmitClangOpcodes(Records, OS); > - break; > case GenClangSACheckers: > EmitClangSACheckers(Records, OS); > break; > > Modified: cfe/trunk/utils/TableGen/TableGenBackends.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/TableGenBackends.h?rev=370642&r1=370641&r2=370642&view=diff > ============================================================================== > --- cfe/trunk/utils/TableGen/TableGenBackends.h (original) > +++ cfe/trunk/utils/TableGen/TableGenBackends.h Mon Sep 2 04:34:47 2019 > @@ -77,7 +77,6 @@ void EmitClangCommentCommandInfo(llvm::R > 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); > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits