https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/130143
>From dd4291322d7f00c4110ac64b6e39b0349d1122c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Thu, 6 Mar 2025 16:18:49 +0100 Subject: [PATCH] [clang][bytecode] Yet another __builtin_constant_p implementation --- clang/lib/AST/ByteCode/ByteCodeEmitter.cpp | 10 ++ clang/lib/AST/ByteCode/ByteCodeEmitter.h | 4 + clang/lib/AST/ByteCode/Compiler.cpp | 22 ++++ clang/lib/AST/ByteCode/Compiler.h | 5 +- clang/lib/AST/ByteCode/EvalEmitter.cpp | 27 +++++ clang/lib/AST/ByteCode/EvalEmitter.h | 4 + clang/lib/AST/ByteCode/Interp.cpp | 89 ++++++++++++++ clang/lib/AST/ByteCode/Interp.h | 22 ++++ clang/lib/AST/ByteCode/InterpBuiltin.cpp | 79 ------------- clang/lib/AST/ByteCode/InterpState.h | 3 + clang/lib/AST/ByteCode/Opcodes.td | 10 +- .../test/AST/ByteCode/builtin-constant-p.cpp | 110 +++++++++++++++++- clang/test/CodeGen/builtin-constant-p.c | 3 + clang/test/CodeGenCXX/builtin-constant-p.cpp | 1 + 14 files changed, 306 insertions(+), 83 deletions(-) diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp index 5bd1b73133d65..4162b55070da9 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp @@ -367,6 +367,16 @@ bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { return true; } +bool ByteCodeEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) { + const Expr *Arg = E->getArg(0); + PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr); + if (!this->emitBCP(getOffset(EndLabel), T, E)) + return false; + if (!this->visit(Arg)) + return false; + return true; +} + //===----------------------------------------------------------------------===// // Opcode emitters //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h index ac728830527a2..64670c32cbcf6 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h @@ -48,12 +48,16 @@ class ByteCodeEmitter { virtual bool visitFunc(const FunctionDecl *E) = 0; virtual bool visitExpr(const Expr *E, bool DestroyToplevelScope) = 0; virtual bool visitDeclAndReturn(const VarDecl *E, bool ConstantContext) = 0; + virtual bool visit(const Expr *E) = 0; + virtual bool emitBool(bool V, const Expr *E) = 0; /// Emits jumps. bool jumpTrue(const LabelTy &Label); bool jumpFalse(const LabelTy &Label); bool jump(const LabelTy &Label); bool fallthrough(const LabelTy &Label); + /// Speculative execution. + bool speculate(const CallExpr *E, const LabelTy &EndLabel); /// We're always emitting bytecode. bool isActive() const { return true; } diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 281fb7e14a57d..3eca12897681d 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -4681,6 +4681,28 @@ bool Compiler<Emitter>::visitAPValueInitializer(const APValue &Val, template <class Emitter> bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinID) { + + if (BuiltinID == Builtin::BI__builtin_constant_p) { + // Void argument is always invalid and harder to handle later. + if (E->getArg(0)->getType()->isVoidType()) { + if (DiscardResult) + return true; + return this->emitConst(0, E); + } + + if (!this->emitStartSpeculation(E)) + return false; + LabelTy EndLabel = this->getLabel(); + if (!this->speculate(E, EndLabel)) + return false; + this->fallthrough(EndLabel); + if (!this->emitEndSpeculation(E)) + return false; + if (DiscardResult) + return this->emitPop(classifyPrim(E), E); + return true; + } + const Function *Func = getFunction(E->getDirectCallee()); if (!Func) return false; diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 77fcc3d1b41ce..7683d60f869f6 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -274,7 +274,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, /// Evaluates an expression and places the result on the stack. If the /// expression is of composite type, a local variable will be created /// and a pointer to said variable will be placed on the stack. - bool visit(const Expr *E); + bool visit(const Expr *E) override; /// Compiles an initializer. This is like visit() but it will never /// create a variable and instead rely on a variable already having /// been created. visitInitializer() then relies on a pointer to this @@ -342,6 +342,9 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, /// Emits an integer constant. template <typename T> bool emitConst(T Value, PrimType Ty, const Expr *E); template <typename T> bool emitConst(T Value, const Expr *E); + bool emitBool(bool V, const Expr *E) override { + return this->emitConst(V, E); + } llvm::RoundingMode getRoundingMode(const Expr *E) const { FPOptions FPO = E->getFPFeaturesInEffect(Ctx.getLangOpts()); diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index 95149efbea992..5326862c506d1 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -127,6 +127,33 @@ bool EvalEmitter::fallthrough(const LabelTy &Label) { return true; } +bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) { + size_t StackSizeBefore = S.Stk.size(); + const Expr *Arg = E->getArg(0); + if (!this->visit(Arg)) { + S.Stk.clearTo(StackSizeBefore); + + if (S.inConstantContext() || Arg->HasSideEffects(S.getASTContext())) + return this->emitBool(false, E); + return Invalid(S, OpPC); + } + + PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr); + if (T == PT_Ptr) { + const auto &Ptr = S.Stk.pop<Pointer>(); + return this->emitBool(CheckBCPResult(S, Ptr), E); + } else if (T == PT_FnPtr) { + S.Stk.discard<FunctionPointer>(); + // Never accepted + return this->emitBool(false, E); + } + + // Otherwise, this is fine! + if (!this->emitPop(T, E)) + return false; + return this->emitBool(true, E); +} + template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) { if (!isActive()) return true; diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h index 2cac2ba2ef221..f53f86c31ec1e 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.h +++ b/clang/lib/AST/ByteCode/EvalEmitter.h @@ -55,12 +55,16 @@ class EvalEmitter : public SourceMapper { virtual bool visitExpr(const Expr *E, bool DestroyToplevelScope) = 0; virtual bool visitDeclAndReturn(const VarDecl *VD, bool ConstantContext) = 0; virtual bool visitFunc(const FunctionDecl *F) = 0; + virtual bool visit(const Expr *E) = 0; + virtual bool emitBool(bool V, const Expr *E) = 0; /// Emits jumps. bool jumpTrue(const LabelTy &Label); bool jumpFalse(const LabelTy &Label); bool jump(const LabelTy &Label); bool fallthrough(const LabelTy &Label); + /// Speculative execution. + bool speculate(const CallExpr *E, const LabelTy &EndLabel); /// Since expressions can only jump forward, predicated execution is /// used to deal with if-else statements. diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 1107c0c32792f..201fec3e864d5 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -54,6 +54,79 @@ static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) { return true; } +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", off) +#endif +// FIXME: We have the large switch over all opcodes here again, and in +// Interpret(). +static bool BCP(InterpState &S, CodePtr &RealPC, int32_t Offset, PrimType PT) { + [[maybe_unused]] CodePtr PCBefore = RealPC; + size_t StackSizeBefore = S.Stk.size(); + + auto SpeculativeInterp = [&S, RealPC]() -> bool { + const InterpFrame *StartFrame = S.Current; + CodePtr PC = RealPC; + + for (;;) { + auto Op = PC.read<Opcode>(); + if (Op == OP_EndSpeculation) + return true; + CodePtr OpPC = PC; + + switch (Op) { +#define GET_INTERP +#include "Opcodes.inc" +#undef GET_INTERP + } + } + llvm_unreachable("We didn't see an EndSpeculation op?"); + }; + + if (SpeculativeInterp()) { + if (PT == PT_Ptr) { + const auto &Ptr = S.Stk.pop<Pointer>(); + assert(S.Stk.size() == StackSizeBefore); + S.Stk.push<Integral<32, true>>( + Integral<32, true>::from(CheckBCPResult(S, Ptr))); + } else if (PT == PT_FnPtr) { + S.Stk.discard<FunctionPointer>(); + S.Stk.push<Integral<32, true>>(Integral<32, true>::from(0)); + } else { + // Pop the result from the stack and return success. + TYPE_SWITCH(PT, S.Stk.pop<T>();); + assert(S.Stk.size() == StackSizeBefore); + S.Stk.push<Integral<32, true>>(Integral<32, true>::from(1)); + } + } else { + if (!S.inConstantContext()) + return Invalid(S, RealPC); + + S.Stk.clearTo(StackSizeBefore); + S.Stk.push<Integral<32, true>>(Integral<32, true>::from(0)); + } + + // RealPC should not have been modified. + assert(*RealPC == *PCBefore); + + // Jump to end label. This is a little tricker than just RealPC += Offset + // because our usual jump instructions don't have any arguments, to the offset + // we get is a little too much and we need to subtract the size of the + // bool and PrimType arguments again. + int32_t ParamSize = align(sizeof(PrimType)); + assert(Offset >= ParamSize); + RealPC += Offset - ParamSize; + + [[maybe_unused]] CodePtr PCCopy = RealPC; + assert(PCCopy.read<Opcode>() == OP_EndSpeculation); + + return true; +} +// https://github.com/llvm/llvm-project/issues/102513 +#if defined(_MSC_VER) && !defined(__clang__) && !defined(NDEBUG) +#pragma optimize("", on) +#endif + static void diagnoseMissingInitializer(InterpState &S, CodePtr OpPC, const ValueDecl *VD) { const SourceInfo &E = S.Current->getSource(OpPC); @@ -290,6 +363,22 @@ void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC, TYPE_SWITCH(Ty, S.Stk.discard<T>()); } +bool CheckBCPResult(InterpState &S, const Pointer &Ptr) { + if (Ptr.isDummy()) + return false; + if (Ptr.isZero()) + return true; + if (Ptr.isIntegralPointer()) + return true; + if (Ptr.isTypeidPointer()) + return true; + + if (const Expr *Base = Ptr.getDeclDesc()->asExpr()) + return isa<StringLiteral>(Base); + + return false; +} + bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!Ptr.isExtern()) return true; diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index d8f90e45b0ced..401bbefaf9a92 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -159,6 +159,7 @@ bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T); bool InvalidShuffleVectorIndex(InterpState &S, CodePtr OpPC, uint32_t Index); bool CheckBitCast(InterpState &S, CodePtr OpPC, bool HasIndeterminateBits, bool TargetIsUCharOrByte); +bool CheckBCPResult(InterpState &S, const Pointer &Ptr); template <typename T> static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) { @@ -2776,8 +2777,29 @@ inline bool Unsupported(InterpState &S, CodePtr OpPC) { return false; } +inline bool StartSpeculation(InterpState &S, CodePtr OpPC) { + ++S.SpeculationDepth; + if (S.SpeculationDepth != 1) + return true; + + assert(S.PrevDiags == nullptr); + S.PrevDiags = S.getEvalStatus().Diag; + S.getEvalStatus().Diag = nullptr; + return true; +} +inline bool EndSpeculation(InterpState &S, CodePtr OpPC) { + assert(S.SpeculationDepth != 0); + --S.SpeculationDepth; + if (S.SpeculationDepth == 0) { + S.getEvalStatus().Diag = S.PrevDiags; + S.PrevDiags = nullptr; + } + return true; +} + /// Do nothing and just abort execution. inline bool Error(InterpState &S, CodePtr OpPC) { return false; } + inline bool SideEffect(InterpState &S, CodePtr OpPC) { return S.noteSideEffect(); } diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index df9c2bc24b15f..00f99745862ee 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1483,80 +1483,6 @@ static bool interp__builtin_ptrauth_string_discriminator( return true; } -// FIXME: This implementation is not complete. -// The Compiler instance we create cannot access the current stack frame, local -// variables, function parameters, etc. We also need protection from -// side-effects, fatal errors, etc. -static bool interp__builtin_constant_p(InterpState &S, CodePtr OpPC, - const InterpFrame *Frame, - const Function *Func, - const CallExpr *Call) { - const Expr *Arg = Call->getArg(0); - QualType ArgType = Arg->getType(); - - auto returnInt = [&S, Call](bool Value) -> bool { - pushInteger(S, Value, Call->getType()); - return true; - }; - - // __builtin_constant_p always has one operand. The rules which gcc follows - // are not precisely documented, but are as follows: - // - // - If the operand is of integral, floating, complex or enumeration type, - // and can be folded to a known value of that type, it returns 1. - // - If the operand can be folded to a pointer to the first character - // of a string literal (or such a pointer cast to an integral type) - // or to a null pointer or an integer cast to a pointer, it returns 1. - // - // Otherwise, it returns 0. - // - // FIXME: GCC also intends to return 1 for literals of aggregate types, but - // its support for this did not work prior to GCC 9 and is not yet well - // understood. - if (ArgType->isIntegralOrEnumerationType() || ArgType->isFloatingType() || - ArgType->isAnyComplexType() || ArgType->isPointerType() || - ArgType->isNullPtrType()) { - auto PrevDiags = S.getEvalStatus().Diag; - S.getEvalStatus().Diag = nullptr; - InterpStack Stk; - Compiler<EvalEmitter> C(S.Ctx, S.P, S, Stk); - auto Res = C.interpretExpr(Arg, /*ConvertResultToRValue=*/Arg->isGLValue()); - S.getEvalStatus().Diag = PrevDiags; - if (Res.isInvalid()) { - C.cleanup(); - Stk.clear(); - return returnInt(false); - } - - if (!Res.empty()) { - const APValue &LV = Res.toAPValue(); - if (LV.isLValue()) { - APValue::LValueBase Base = LV.getLValueBase(); - if (Base.isNull()) { - // A null base is acceptable. - return returnInt(true); - } else if (const auto *E = Base.dyn_cast<const Expr *>()) { - if (!isa<StringLiteral>(E)) - return returnInt(false); - return returnInt(LV.getLValueOffset().isZero()); - } else if (Base.is<TypeInfoLValue>()) { - // Surprisingly, GCC considers __builtin_constant_p(&typeid(int)) to - // evaluate to true. - return returnInt(true); - } else { - // Any other base is not constant enough for GCC. - return returnInt(false); - } - } - } - - // Otherwise, any constant value is good enough. - return returnInt(true); - } - - return returnInt(false); -} - static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const Function *Func, @@ -2468,11 +2394,6 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, return false; break; - case Builtin::BI__builtin_constant_p: - if (!interp__builtin_constant_p(S, OpPC, Frame, F, Call)) - return false; - break; - case Builtin::BI__noop: pushInteger(S, 0, Call->getType()); break; diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index d6adfff1a713a..74001b80d9c00 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -144,6 +144,9 @@ class InterpState final : public State, public SourceMapper { SourceLocation EvalLocation; /// Declaration we're initializing/evaluting, if any. const VarDecl *EvaluatingDecl = nullptr; + /// Things needed to do speculative execution. + SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr; + unsigned SpeculationDepth = 0; llvm::SmallVector< std::pair<const Expr *, const LifetimeExtendedTemporaryDecl *>> diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 41e4bae65c195..98f9818cb5ffb 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -58,7 +58,7 @@ def ArgRecordField : ArgType { let Name = "const Record::Field *"; } def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; } def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; } def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; } -def ArgCastKind : ArgType { let Name = "CastKind"; } +def ArgCastKind : ArgType { let Name = "interp::CastKind"; } def ArgCallExpr : ArgType { let Name = "const CallExpr *"; } def ArgExpr : ArgType { let Name = "const Expr *"; } def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; } @@ -172,6 +172,14 @@ def Jt : JumpOpcode; // [Bool] -> [], jumps if false. def Jf : JumpOpcode; +def StartSpeculation : Opcode; +def EndSpeculation : Opcode; +def BCP : Opcode { + let ChangesPC = 1; + let HasCustomEval = 1; + let Args = [ArgSint32, ArgPrimType]; +} + //===----------------------------------------------------------------------===// // Returns //===----------------------------------------------------------------------===// diff --git a/clang/test/AST/ByteCode/builtin-constant-p.cpp b/clang/test/AST/ByteCode/builtin-constant-p.cpp index 62899b60064c2..b309fa8296889 100644 --- a/clang/test/AST/ByteCode/builtin-constant-p.cpp +++ b/clang/test/AST/ByteCode/builtin-constant-p.cpp @@ -1,6 +1,7 @@ -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s -// RUN: %clang_cc1 -verify=ref,both %s +// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -std=c++20 -verify=ref,both %s +using intptr_t = __INTPTR_TYPE__; static_assert(__builtin_constant_p(12), ""); static_assert(__builtin_constant_p(1.0), ""); @@ -18,3 +19,108 @@ constexpr int foo(int &a) { return __builtin_constant_p(a); } static_assert(!foo(z)); + +static_assert(__builtin_constant_p(__builtin_constant_p(1))); + +constexpr bool nested(int& a) { + return __builtin_constant_p(__builtin_constant_p(a)); +} +static_assert(nested(z)); + +constexpr bool Local() { + int z = 10; + return __builtin_constant_p(z); +} +static_assert(Local()); + +constexpr bool Local2() { + int z = 10; + return __builtin_constant_p(&z); +} +static_assert(!Local2()); + +constexpr bool Parameter(int a) { + return __builtin_constant_p(a); +} +static_assert(Parameter(10)); + +constexpr bool InvalidLocal() { + int *z; + { + int b = 10; + z = &b; + } + return __builtin_constant_p(z); +} +static_assert(!InvalidLocal()); + +template<typename T> constexpr bool bcp(T t) { + return __builtin_constant_p(t); +} + +constexpr intptr_t ptr_to_int(const void *p) { + return __builtin_constant_p(1) ? (intptr_t)p : (intptr_t)p; // expected-note {{cast that performs the conversions of a reinterpret_cast}} +} + +/// This is from test/SemaCXX/builtin-constant-p.cpp, but it makes no sense. +/// ptr_to_int is called before bcp(), so it fails. GCC does not accept this either. +static_assert(bcp(ptr_to_int("foo"))); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} + +constexpr bool AndFold(const int &a, const int &b) { + return __builtin_constant_p(a && b); +} + +static_assert(AndFold(10, 20)); +static_assert(!AndFold(z, 10)); +static_assert(!AndFold(10, z)); + + +struct F { + int a; +}; + +constexpr F f{12}; +static_assert(__builtin_constant_p(f.a)); + +constexpr bool Member() { + F f; + return __builtin_constant_p(f.a); +} +static_assert(!Member()); + +constexpr bool Discard() { + (void)__builtin_constant_p(10); + return true; +} +static_assert(Discard()); + +static_assert(__builtin_constant_p((int*)123)); + +constexpr void func() {} +static_assert(!__builtin_constant_p(func)); + +/// This is from SemaCXX/builtin-constant-p and GCC agrees with the bytecode interpreter. +constexpr int mutate1() { + int n = 1; + int m = __builtin_constant_p(++n); + return n * 10 + m; +} +static_assert(mutate1() == 21); // ref-error {{static assertion failed}} \ + // ref-note {{evaluates to '10 == 21'}} + +/// Similar for this. GCC agrees with the bytecode interpreter. +constexpr int mutate_param(bool mutate, int ¶m) { + mutate = mutate; // Mutation of internal state is OK + if (mutate) + ++param; + return param; +} +constexpr int mutate6(bool mutate) { + int n = 1; + int m = __builtin_constant_p(mutate_param(mutate, n)); + return n * 10 + m; +} +static_assert(mutate6(false) == 11); +static_assert(mutate6(true) == 21); // ref-error {{static assertion failed}} \ + // ref-note {{evaluates to '10 == 21'}} diff --git a/clang/test/CodeGen/builtin-constant-p.c b/clang/test/CodeGen/builtin-constant-p.c index 8e35cb2b2ef83..07288069a5e3a 100644 --- a/clang/test/CodeGen/builtin-constant-p.c +++ b/clang/test/CodeGen/builtin-constant-p.c @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O2 | FileCheck %s // RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O0 | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O2 -fexperimental-new-constant-interpreter | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O0 -fexperimental-new-constant-interpreter | FileCheck %s + int a = 42; diff --git a/clang/test/CodeGenCXX/builtin-constant-p.cpp b/clang/test/CodeGenCXX/builtin-constant-p.cpp index 866faa5ec9761..39416b94375fe 100644 --- a/clang/test/CodeGenCXX/builtin-constant-p.cpp +++ b/clang/test/CodeGenCXX/builtin-constant-p.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s -fexperimental-new-constant-interpreter | FileCheck %s // Don't crash if the argument to __builtin_constant_p isn't scalar. template <typename T> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits