r370588 - Revert [Clang Interpreter] Initial patch for the constexpr interpreter
Nandor Licker via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 31 08:15:39 PDT 2019
Author: nand
Date: Sat Aug 31 08:15:39 2019
New Revision: 370588
URL: http://llvm.org/viewvc/llvm-project?rev=370588&view=rev
Log:
Revert [Clang Interpreter] Initial patch for the constexpr interpreter
This reverts r370584 (git commit afcb3de117265a69d21e5673356e925a454d7d02)
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=370587&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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/docs/index.rst (original)
+++ cfe/trunk/docs/index.rst Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Sat Aug 31 08:15:39 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=370587&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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/LangOptions.def (original)
+++ cfe/trunk/include/clang/Basic/LangOptions.def Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/Options.td (original)
+++ cfe/trunk/include/clang/Driver/Options.td Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CMakeLists.txt (original)
+++ cfe/trunk/lib/AST/CMakeLists.txt Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/ToolChains/Clang.cpp (original)
+++ cfe/trunk/lib/Driver/ToolChains/Clang.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constexpr-many-arguments.cpp (original)
+++ cfe/trunk/test/SemaCXX/constexpr-many-arguments.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/shift.cpp (original)
+++ cfe/trunk/test/SemaCXX/shift.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/CMakeLists.txt (original)
+++ cfe/trunk/utils/TableGen/CMakeLists.txt Sat Aug 31 08:15:39 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=370587&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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/TableGen.cpp (original)
+++ cfe/trunk/utils/TableGen/TableGen.cpp Sat Aug 31 08:15:39 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=370588&r1=370587&r2=370588&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/TableGenBackends.h (original)
+++ cfe/trunk/utils/TableGen/TableGenBackends.h Sat Aug 31 08:15:39 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);
More information about the cfe-commits
mailing list