Author: Timm Bäder Date: 2024-07-25T18:51:36+02:00 New Revision: 7304936479a7eb61adc9edcaf6ce56e4792590ad
URL: https://github.com/llvm/llvm-project/commit/7304936479a7eb61adc9edcaf6ce56e4792590ad DIFF: https://github.com/llvm/llvm-project/commit/7304936479a7eb61adc9edcaf6ce56e4792590ad.diff LOG: [clang][Interp] Add preliminary __builtin_constant_p implementation This is not perfect or complete, but it helps us pass the simple tests and those tests where __builtin_constant_p is not the main subject of testing. Added: clang/test/AST/Interp/builtin-constant-p.cpp Modified: clang/lib/AST/Interp/ByteCodeEmitter.cpp clang/lib/AST/Interp/InterpBuiltin.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index fee4432a8f661..a01fa15dc0b7d 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -28,7 +28,8 @@ using namespace clang::interp; /// but that is not correct for our use cases. static bool isUnevaluatedBuiltin(unsigned BuiltinID) { return BuiltinID == Builtin::BI__builtin_classify_type || - BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size; + BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size || + BuiltinID == Builtin::BI__builtin_constant_p; } Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index c170042144acc..c59bbc8313edc 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "../ExprConstShared.h" #include "Boolean.h" +#include "Compiler.h" +#include "EvalEmitter.h" #include "Interp.h" #include "PrimType.h" #include "clang/AST/OSLog.h" @@ -1127,6 +1129,73 @@ 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()) { + InterpStack Stk; + Compiler<EvalEmitter> C(S.Ctx, S.P, S, Stk); + auto Res = C.interpretExpr(Arg, /*ConvertResultToRValue=*/Arg->isGLValue()); + if (Res.isInvalid()) { + C.cleanup(); + Stk.clear(); + } + + const APValue &LV = Res.toAPValue(); + if (!Res.isInvalid() && 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); + } + } + + return returnInt(!Res.isInvalid() && !Res.empty()); + } + + return returnInt(false); +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F, const CallExpr *Call) { const InterpFrame *Frame = S.Current; @@ -1456,6 +1525,11 @@ 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; + default: S.FFDiag(S.Current->getLocation(OpPC), diag::note_invalid_subexpr_in_const_expr) diff --git a/clang/test/AST/Interp/builtin-constant-p.cpp b/clang/test/AST/Interp/builtin-constant-p.cpp new file mode 100644 index 0000000000000..0d222d1c96277 --- /dev/null +++ b/clang/test/AST/Interp/builtin-constant-p.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=expected,both %s +// RUN: %clang_cc1 -verify=ref,both %s + + +static_assert(__builtin_constant_p(12), ""); +static_assert(__builtin_constant_p(1.0), ""); + +constexpr int I = 100; +static_assert(__builtin_constant_p(I), ""); +static_assert(__builtin_constant_p(I + 10), ""); +static_assert(__builtin_constant_p(I + 10.0), ""); +static_assert(__builtin_constant_p(nullptr), ""); +static_assert(__builtin_constant_p(&I), ""); // both-error {{failed due to requirement}} +static_assert(__builtin_constant_p((void)I), ""); // both-error {{failed due to requirement}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits