llvm-project/clang/lib/AST/ByteCode/InterpBuiltin.cpp
Timm Baeder adba24aa3c
[clang][bytecode] Add missing __builtin_memcpy checks (#135975)
Add a test for type punning and tests and the necessary checks for
non-trivially-copyable types and incomplete types.
2025-04-17 06:19:37 +02:00

2890 lines
101 KiB
C++

//===--- InterpBuiltin.cpp - Interpreter for the constexpr VM ---*- 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
//
//===----------------------------------------------------------------------===//
#include "../ExprConstShared.h"
#include "Boolean.h"
#include "Compiler.h"
#include "EvalEmitter.h"
#include "Interp.h"
#include "InterpBuiltinBitCast.h"
#include "PrimType.h"
#include "clang/AST/OSLog.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetBuiltins.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/SipHash.h"
namespace clang {
namespace interp {
static unsigned callArgSize(const InterpState &S, const CallExpr *C) {
unsigned O = 0;
for (const Expr *E : C->arguments()) {
O += align(primSize(*S.getContext().classify(E)));
}
return O;
}
template <typename T>
static T getParam(const InterpFrame *Frame, unsigned Index) {
assert(Frame->getFunction()->getNumParams() > Index);
unsigned Offset = Frame->getFunction()->getParamOffset(Index);
return Frame->getParam<T>(Offset);
}
static APSInt getAPSIntParam(const InterpFrame *Frame, unsigned Index) {
APSInt R;
unsigned Offset = Frame->getFunction()->getParamOffset(Index);
INT_TYPE_SWITCH(Frame->getFunction()->getParamType(Index),
R = Frame->getParam<T>(Offset).toAPSInt());
return R;
}
static PrimType getIntPrimType(const InterpState &S) {
const TargetInfo &TI = S.getASTContext().getTargetInfo();
unsigned IntWidth = TI.getIntWidth();
if (IntWidth == 32)
return PT_Sint32;
else if (IntWidth == 16)
return PT_Sint16;
llvm_unreachable("Int isn't 16 or 32 bit?");
}
static PrimType getLongPrimType(const InterpState &S) {
const TargetInfo &TI = S.getASTContext().getTargetInfo();
unsigned LongWidth = TI.getLongWidth();
if (LongWidth == 64)
return PT_Sint64;
else if (LongWidth == 32)
return PT_Sint32;
else if (LongWidth == 16)
return PT_Sint16;
llvm_unreachable("long isn't 16, 32 or 64 bit?");
}
/// Peek an integer value from the stack into an APSInt.
static APSInt peekToAPSInt(InterpStack &Stk, PrimType T, size_t Offset = 0) {
if (Offset == 0)
Offset = align(primSize(T));
APSInt R;
INT_TYPE_SWITCH(T, R = Stk.peek<T>(Offset).toAPSInt());
return R;
}
/// Pushes \p Val on the stack as the type given by \p QT.
static void pushInteger(InterpState &S, const APSInt &Val, QualType QT) {
assert(QT->isSignedIntegerOrEnumerationType() ||
QT->isUnsignedIntegerOrEnumerationType());
std::optional<PrimType> T = S.getContext().classify(QT);
assert(T);
unsigned BitWidth = S.getASTContext().getTypeSize(QT);
if (QT->isSignedIntegerOrEnumerationType()) {
int64_t V = Val.getSExtValue();
INT_TYPE_SWITCH(*T, { S.Stk.push<T>(T::from(V, BitWidth)); });
} else {
assert(QT->isUnsignedIntegerOrEnumerationType());
uint64_t V = Val.getZExtValue();
INT_TYPE_SWITCH(*T, { S.Stk.push<T>(T::from(V, BitWidth)); });
}
}
template <typename T>
static void pushInteger(InterpState &S, T Val, QualType QT) {
if constexpr (std::is_same_v<T, APInt>)
pushInteger(S, APSInt(Val, !std::is_signed_v<T>), QT);
else if constexpr (std::is_same_v<T, APSInt>)
pushInteger(S, Val, QT);
else
pushInteger(S,
APSInt(APInt(sizeof(T) * 8, static_cast<uint64_t>(Val),
std::is_signed_v<T>),
!std::is_signed_v<T>),
QT);
}
static void assignInteger(Pointer &Dest, PrimType ValueT, const APSInt &Value) {
INT_TYPE_SWITCH_NO_BOOL(
ValueT, { Dest.deref<T>() = T::from(static_cast<T>(Value)); });
}
static bool retPrimValue(InterpState &S, CodePtr OpPC,
std::optional<PrimType> &T) {
if (!T)
return RetVoid(S, OpPC);
#define RET_CASE(X) \
case X: \
return Ret<X>(S, OpPC);
switch (*T) {
RET_CASE(PT_Ptr);
RET_CASE(PT_Float);
RET_CASE(PT_Bool);
RET_CASE(PT_Sint8);
RET_CASE(PT_Uint8);
RET_CASE(PT_Sint16);
RET_CASE(PT_Uint16);
RET_CASE(PT_Sint32);
RET_CASE(PT_Uint32);
RET_CASE(PT_Sint64);
RET_CASE(PT_Uint64);
RET_CASE(PT_IntAP);
RET_CASE(PT_IntAPS);
default:
llvm_unreachable("Unsupported return type for builtin function");
}
#undef RET_CASE
}
static QualType getElemType(const Pointer &P) {
const Descriptor *Desc = P.getFieldDesc();
QualType T = Desc->getType();
if (Desc->isPrimitive())
return T;
if (T->isPointerType())
return T->getAs<PointerType>()->getPointeeType();
if (Desc->isArray())
return Desc->getElemQualType();
if (const auto *AT = T->getAsArrayTypeUnsafe())
return AT->getElementType();
return T;
}
static void diagnoseNonConstexprBuiltin(InterpState &S, CodePtr OpPC,
unsigned ID) {
auto Loc = S.Current->getSource(OpPC);
if (S.getLangOpts().CPlusPlus11)
S.CCEDiag(Loc, diag::note_constexpr_invalid_function)
<< /*isConstexpr=*/0 << /*isConstructor=*/0
<< S.getASTContext().BuiltinInfo.getQuotedName(ID);
else
S.CCEDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
}
static bool interp__builtin_is_constant_evaluated(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const CallExpr *Call) {
unsigned Depth = S.Current->getDepth();
auto isStdCall = [](const FunctionDecl *F) -> bool {
return F && F->isInStdNamespace() && F->getIdentifier() &&
F->getIdentifier()->isStr("is_constant_evaluated");
};
const InterpFrame *Caller = Frame->Caller;
// The current frame is the one for __builtin_is_constant_evaluated.
// The one above that, potentially the one for std::is_constant_evaluated().
if (S.inConstantContext() && !S.checkingPotentialConstantExpression() &&
S.getEvalStatus().Diag &&
(Depth == 1 || (Depth == 2 && isStdCall(Caller->getCallee())))) {
if (Caller->Caller && isStdCall(Caller->getCallee())) {
const Expr *E = Caller->Caller->getExpr(Caller->getRetPC());
S.report(E->getExprLoc(),
diag::warn_is_constant_evaluated_always_true_constexpr)
<< "std::is_constant_evaluated" << E->getSourceRange();
} else {
const Expr *E = Frame->Caller->getExpr(Frame->getRetPC());
S.report(E->getExprLoc(),
diag::warn_is_constant_evaluated_always_true_constexpr)
<< "__builtin_is_constant_evaluated" << E->getSourceRange();
}
}
S.Stk.push<Boolean>(Boolean::from(S.inConstantContext()));
return true;
}
static bool interp__builtin_strcmp(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
unsigned ID = Func->getBuiltinID();
const Pointer &A = getParam<Pointer>(Frame, 0);
const Pointer &B = getParam<Pointer>(Frame, 1);
if (ID == Builtin::BIstrcmp || ID == Builtin::BIstrncmp ||
ID == Builtin::BIwcscmp || ID == Builtin::BIwcsncmp)
diagnoseNonConstexprBuiltin(S, OpPC, ID);
uint64_t Limit = ~static_cast<uint64_t>(0);
if (ID == Builtin::BIstrncmp || ID == Builtin::BI__builtin_strncmp ||
ID == Builtin::BIwcsncmp || ID == Builtin::BI__builtin_wcsncmp)
Limit = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)))
.getZExtValue();
if (Limit == 0) {
pushInteger(S, 0, Call->getType());
return true;
}
if (!CheckLive(S, OpPC, A, AK_Read) || !CheckLive(S, OpPC, B, AK_Read))
return false;
if (A.isDummy() || B.isDummy())
return false;
bool IsWide = ID == Builtin::BIwcscmp || ID == Builtin::BIwcsncmp ||
ID == Builtin::BI__builtin_wcscmp ||
ID == Builtin::BI__builtin_wcsncmp;
assert(A.getFieldDesc()->isPrimitiveArray());
assert(B.getFieldDesc()->isPrimitiveArray());
assert(getElemType(A).getTypePtr() == getElemType(B).getTypePtr());
PrimType ElemT = *S.getContext().classify(getElemType(A));
auto returnResult = [&](int V) -> bool {
pushInteger(S, V, Call->getType());
return true;
};
unsigned IndexA = A.getIndex();
unsigned IndexB = B.getIndex();
uint64_t Steps = 0;
for (;; ++IndexA, ++IndexB, ++Steps) {
if (Steps >= Limit)
break;
const Pointer &PA = A.atIndex(IndexA);
const Pointer &PB = B.atIndex(IndexB);
if (!CheckRange(S, OpPC, PA, AK_Read) ||
!CheckRange(S, OpPC, PB, AK_Read)) {
return false;
}
if (IsWide) {
INT_TYPE_SWITCH(ElemT, {
T CA = PA.deref<T>();
T CB = PB.deref<T>();
if (CA > CB)
return returnResult(1);
else if (CA < CB)
return returnResult(-1);
else if (CA.isZero() || CB.isZero())
return returnResult(0);
});
continue;
}
uint8_t CA = PA.deref<uint8_t>();
uint8_t CB = PB.deref<uint8_t>();
if (CA > CB)
return returnResult(1);
else if (CA < CB)
return returnResult(-1);
if (CA == 0 || CB == 0)
return returnResult(0);
}
return returnResult(0);
}
static bool interp__builtin_strlen(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
unsigned ID = Func->getBuiltinID();
const Pointer &StrPtr = getParam<Pointer>(Frame, 0);
if (ID == Builtin::BIstrlen || ID == Builtin::BIwcslen)
diagnoseNonConstexprBuiltin(S, OpPC, ID);
if (!CheckArray(S, OpPC, StrPtr))
return false;
if (!CheckLive(S, OpPC, StrPtr, AK_Read))
return false;
if (!CheckDummy(S, OpPC, StrPtr, AK_Read))
return false;
assert(StrPtr.getFieldDesc()->isPrimitiveArray());
unsigned ElemSize = StrPtr.getFieldDesc()->getElemSize();
if (ID == Builtin::BI__builtin_wcslen || ID == Builtin::BIwcslen) {
[[maybe_unused]] const ASTContext &AC = S.getASTContext();
assert(ElemSize == AC.getTypeSizeInChars(AC.getWCharType()).getQuantity());
}
size_t Len = 0;
for (size_t I = StrPtr.getIndex();; ++I, ++Len) {
const Pointer &ElemPtr = StrPtr.atIndex(I);
if (!CheckRange(S, OpPC, ElemPtr, AK_Read))
return false;
uint32_t Val;
switch (ElemSize) {
case 1:
Val = ElemPtr.deref<uint8_t>();
break;
case 2:
Val = ElemPtr.deref<uint16_t>();
break;
case 4:
Val = ElemPtr.deref<uint32_t>();
break;
default:
llvm_unreachable("Unsupported char size");
}
if (Val == 0)
break;
}
pushInteger(S, Len, Call->getType());
return true;
}
static bool interp__builtin_nan(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
bool Signaling) {
const Pointer &Arg = getParam<Pointer>(Frame, 0);
if (!CheckLoad(S, OpPC, Arg))
return false;
assert(Arg.getFieldDesc()->isPrimitiveArray());
// Convert the given string to an integer using StringRef's API.
llvm::APInt Fill;
std::string Str;
assert(Arg.getNumElems() >= 1);
for (unsigned I = 0;; ++I) {
const Pointer &Elem = Arg.atIndex(I);
if (!CheckLoad(S, OpPC, Elem))
return false;
if (Elem.deref<int8_t>() == 0)
break;
Str += Elem.deref<char>();
}
// Treat empty strings as if they were zero.
if (Str.empty())
Fill = llvm::APInt(32, 0);
else if (StringRef(Str).getAsInteger(0, Fill))
return false;
const llvm::fltSemantics &TargetSemantics =
S.getASTContext().getFloatTypeSemantics(F->getDecl()->getReturnType());
Floating Result;
if (S.getASTContext().getTargetInfo().isNan2008()) {
if (Signaling)
Result = Floating(
llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill));
else
Result = Floating(
llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, &Fill));
} else {
// Prior to IEEE 754-2008, architectures were allowed to choose whether
// the first bit of their significand was set for qNaN or sNaN. MIPS chose
// a different encoding to what became a standard in 2008, and for pre-
// 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as
// sNaN. This is now known as "legacy NaN" encoding.
if (Signaling)
Result = Floating(
llvm::APFloat::getQNaN(TargetSemantics, /*Negative=*/false, &Fill));
else
Result = Floating(
llvm::APFloat::getSNaN(TargetSemantics, /*Negative=*/false, &Fill));
}
S.Stk.push<Floating>(Result);
return true;
}
static bool interp__builtin_inf(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F) {
const llvm::fltSemantics &TargetSemantics =
S.getASTContext().getFloatTypeSemantics(F->getDecl()->getReturnType());
S.Stk.push<Floating>(Floating::getInf(TargetSemantics));
return true;
}
static bool interp__builtin_copysign(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F) {
const Floating &Arg1 = getParam<Floating>(Frame, 0);
const Floating &Arg2 = getParam<Floating>(Frame, 1);
APFloat Copy = Arg1.getAPFloat();
Copy.copySign(Arg2.getAPFloat());
S.Stk.push<Floating>(Floating(Copy));
return true;
}
static bool interp__builtin_fmin(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
bool IsNumBuiltin) {
const Floating &LHS = getParam<Floating>(Frame, 0);
const Floating &RHS = getParam<Floating>(Frame, 1);
if (IsNumBuiltin)
S.Stk.push<Floating>(llvm::minimumnum(LHS.getAPFloat(), RHS.getAPFloat()));
else
S.Stk.push<Floating>(minnum(LHS.getAPFloat(), RHS.getAPFloat()));
return true;
}
static bool interp__builtin_fmax(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *Func,
bool IsNumBuiltin) {
const Floating &LHS = getParam<Floating>(Frame, 0);
const Floating &RHS = getParam<Floating>(Frame, 1);
if (IsNumBuiltin)
S.Stk.push<Floating>(llvm::maximumnum(LHS.getAPFloat(), RHS.getAPFloat()));
else
S.Stk.push<Floating>(maxnum(LHS.getAPFloat(), RHS.getAPFloat()));
return true;
}
/// Defined as __builtin_isnan(...), to accommodate the fact that it can
/// take a float, double, long double, etc.
/// But for us, that's all a Floating anyway.
static bool interp__builtin_isnan(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
pushInteger(S, Arg.isNan(), Call->getType());
return true;
}
static bool interp__builtin_issignaling(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F,
const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
pushInteger(S, Arg.isSignaling(), Call->getType());
return true;
}
static bool interp__builtin_isinf(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
bool CheckSign, const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
bool IsInf = Arg.isInf();
if (CheckSign)
pushInteger(S, IsInf ? (Arg.isNegative() ? -1 : 1) : 0, Call->getType());
else
pushInteger(S, Arg.isInf(), Call->getType());
return true;
}
static bool interp__builtin_isfinite(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F, const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
pushInteger(S, Arg.isFinite(), Call->getType());
return true;
}
static bool interp__builtin_isnormal(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F, const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
pushInteger(S, Arg.isNormal(), Call->getType());
return true;
}
static bool interp__builtin_issubnormal(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F,
const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
pushInteger(S, Arg.isDenormal(), Call->getType());
return true;
}
static bool interp__builtin_iszero(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
pushInteger(S, Arg.isZero(), Call->getType());
return true;
}
static bool interp__builtin_signbit(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();
pushInteger(S, Arg.isNegative(), Call->getType());
return true;
}
static bool interp_floating_comparison(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F,
const CallExpr *Call) {
const Floating &RHS = S.Stk.peek<Floating>();
const Floating &LHS = S.Stk.peek<Floating>(align(2u * primSize(PT_Float)));
unsigned ID = F->getBuiltinID();
pushInteger(
S,
[&] {
switch (ID) {
case Builtin::BI__builtin_isgreater:
return LHS > RHS;
case Builtin::BI__builtin_isgreaterequal:
return LHS >= RHS;
case Builtin::BI__builtin_isless:
return LHS < RHS;
case Builtin::BI__builtin_islessequal:
return LHS <= RHS;
case Builtin::BI__builtin_islessgreater: {
ComparisonCategoryResult cmp = LHS.compare(RHS);
return cmp == ComparisonCategoryResult::Less ||
cmp == ComparisonCategoryResult::Greater;
}
case Builtin::BI__builtin_isunordered:
return LHS.compare(RHS) == ComparisonCategoryResult::Unordered;
default:
llvm_unreachable("Unexpected builtin ID: Should be a floating point "
"comparison function");
}
}(),
Call->getType());
return true;
}
/// First parameter to __builtin_isfpclass is the floating value, the
/// second one is an integral value.
static bool interp__builtin_isfpclass(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType FPClassArgT = *S.getContext().classify(Call->getArg(1)->getType());
APSInt FPClassArg = peekToAPSInt(S.Stk, FPClassArgT);
const Floating &F =
S.Stk.peek<Floating>(align(primSize(FPClassArgT) + primSize(PT_Float)));
int32_t Result =
static_cast<int32_t>((F.classify() & FPClassArg).getZExtValue());
pushInteger(S, Result, Call->getType());
return true;
}
/// Five int values followed by one floating value.
static bool interp__builtin_fpclassify(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
const Floating &Val = S.Stk.peek<Floating>();
unsigned Index;
switch (Val.getCategory()) {
case APFloat::fcNaN:
Index = 0;
break;
case APFloat::fcInfinity:
Index = 1;
break;
case APFloat::fcNormal:
Index = Val.isDenormal() ? 3 : 2;
break;
case APFloat::fcZero:
Index = 4;
break;
}
// The last argument is first on the stack.
assert(Index <= 4);
unsigned IntSize = primSize(getIntPrimType(S));
unsigned Offset =
align(primSize(PT_Float)) + ((1 + (4 - Index)) * align(IntSize));
APSInt I = peekToAPSInt(S.Stk, getIntPrimType(S), Offset);
pushInteger(S, I, Call->getType());
return true;
}
// The C standard says "fabs raises no floating-point exceptions,
// even if x is a signaling NaN. The returned value is independent of
// the current rounding direction mode." Therefore constant folding can
// proceed without regard to the floating point settings.
// Reference, WG14 N2478 F.10.4.3
static bool interp__builtin_fabs(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func) {
const Floating &Val = getParam<Floating>(Frame, 0);
S.Stk.push<Floating>(Floating::abs(Val));
return true;
}
static bool interp__builtin_abs(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *Func,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = peekToAPSInt(S.Stk, ArgT);
if (Val ==
APSInt(APInt::getSignedMinValue(Val.getBitWidth()), /*IsUnsigned=*/false))
return false;
if (Val.isNegative())
Val.negate();
pushInteger(S, Val, Call->getType());
return true;
}
static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = peekToAPSInt(S.Stk, ArgT);
pushInteger(S, Val.popcount(), Call->getType());
return true;
}
static bool interp__builtin_parity(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = peekToAPSInt(S.Stk, ArgT);
pushInteger(S, Val.popcount() % 2, Call->getType());
return true;
}
static bool interp__builtin_clrsb(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = peekToAPSInt(S.Stk, ArgT);
pushInteger(S, Val.getBitWidth() - Val.getSignificantBits(), Call->getType());
return true;
}
static bool interp__builtin_bitreverse(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = peekToAPSInt(S.Stk, ArgT);
pushInteger(S, Val.reverseBits(), Call->getType());
return true;
}
static bool interp__builtin_classify_type(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
// This is an unevaluated call, so there are no arguments on the stack.
assert(Call->getNumArgs() == 1);
const Expr *Arg = Call->getArg(0);
GCCTypeClass ResultClass =
EvaluateBuiltinClassifyType(Arg->getType(), S.getLangOpts());
int32_t ReturnVal = static_cast<int32_t>(ResultClass);
pushInteger(S, ReturnVal, Call->getType());
return true;
}
// __builtin_expect(long, long)
// __builtin_expect_with_probability(long, long, double)
static bool interp__builtin_expect(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
// The return value is simply the value of the first parameter.
// We ignore the probability.
unsigned NumArgs = Call->getNumArgs();
assert(NumArgs == 2 || NumArgs == 3);
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
unsigned Offset = align(primSize(getLongPrimType(S))) * 2;
if (NumArgs == 3)
Offset += align(primSize(PT_Float));
APSInt Val = peekToAPSInt(S.Stk, ArgT, Offset);
pushInteger(S, Val, Call->getType());
return true;
}
/// rotateleft(value, amount)
static bool interp__builtin_rotate(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call,
bool Right) {
PrimType AmountT = *S.getContext().classify(Call->getArg(1)->getType());
PrimType ValueT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Amount = peekToAPSInt(S.Stk, AmountT);
APSInt Value = peekToAPSInt(
S.Stk, ValueT, align(primSize(AmountT)) + align(primSize(ValueT)));
APSInt Result;
if (Right)
Result = APSInt(Value.rotr(Amount.urem(Value.getBitWidth())),
/*IsUnsigned=*/true);
else // Left.
Result = APSInt(Value.rotl(Amount.urem(Value.getBitWidth())),
/*IsUnsigned=*/true);
pushInteger(S, Result, Call->getType());
return true;
}
static bool interp__builtin_ffs(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *Func,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Value = peekToAPSInt(S.Stk, ArgT);
uint64_t N = Value.countr_zero();
pushInteger(S, N == Value.getBitWidth() ? 0 : N + 1, Call->getType());
return true;
}
static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
assert(Call->getArg(0)->isLValue());
PrimType PtrT = S.getContext().classify(Call->getArg(0)).value_or(PT_Ptr);
if (PtrT == PT_Ptr) {
const Pointer &Arg = S.Stk.peek<Pointer>();
S.Stk.push<Pointer>(Arg);
} else {
assert(false && "Unsupported pointer type passed to __builtin_addressof()");
}
return true;
}
static bool interp__builtin_move(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *Func,
const CallExpr *Call) {
PrimType ArgT = S.getContext().classify(Call->getArg(0)).value_or(PT_Ptr);
TYPE_SWITCH(ArgT, const T &Arg = S.Stk.peek<T>(); S.Stk.push<T>(Arg););
return Func->getDecl()->isConstexpr();
}
static bool interp__builtin_eh_return_data_regno(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Arg = peekToAPSInt(S.Stk, ArgT);
int Result = S.getASTContext().getTargetInfo().getEHDataRegisterNumber(
Arg.getZExtValue());
pushInteger(S, Result, Call->getType());
return true;
}
/// Just takes the first Argument to the call and puts it on the stack.
static bool noopPointer(InterpState &S, CodePtr OpPC, const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
const Pointer &Arg = S.Stk.peek<Pointer>();
S.Stk.push<Pointer>(Arg);
return true;
}
// Two integral values followed by a pointer (lhs, rhs, resultOut)
static bool interp__builtin_overflowop(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
Pointer &ResultPtr = S.Stk.peek<Pointer>();
if (ResultPtr.isDummy())
return false;
unsigned BuiltinOp = Func->getBuiltinID();
PrimType RHST = *S.getContext().classify(Call->getArg(1)->getType());
PrimType LHST = *S.getContext().classify(Call->getArg(0)->getType());
APSInt RHS = peekToAPSInt(S.Stk, RHST,
align(primSize(PT_Ptr)) + align(primSize(RHST)));
APSInt LHS = peekToAPSInt(S.Stk, LHST,
align(primSize(PT_Ptr)) + align(primSize(RHST)) +
align(primSize(LHST)));
QualType ResultType = Call->getArg(2)->getType()->getPointeeType();
PrimType ResultT = *S.getContext().classify(ResultType);
bool Overflow;
APSInt Result;
if (BuiltinOp == Builtin::BI__builtin_add_overflow ||
BuiltinOp == Builtin::BI__builtin_sub_overflow ||
BuiltinOp == Builtin::BI__builtin_mul_overflow) {
bool IsSigned = LHS.isSigned() || RHS.isSigned() ||
ResultType->isSignedIntegerOrEnumerationType();
bool AllSigned = LHS.isSigned() && RHS.isSigned() &&
ResultType->isSignedIntegerOrEnumerationType();
uint64_t LHSSize = LHS.getBitWidth();
uint64_t RHSSize = RHS.getBitWidth();
uint64_t ResultSize = S.getASTContext().getTypeSize(ResultType);
uint64_t MaxBits = std::max(std::max(LHSSize, RHSSize), ResultSize);
// Add an additional bit if the signedness isn't uniformly agreed to. We
// could do this ONLY if there is a signed and an unsigned that both have
// MaxBits, but the code to check that is pretty nasty. The issue will be
// caught in the shrink-to-result later anyway.
if (IsSigned && !AllSigned)
++MaxBits;
LHS = APSInt(LHS.extOrTrunc(MaxBits), !IsSigned);
RHS = APSInt(RHS.extOrTrunc(MaxBits), !IsSigned);
Result = APSInt(MaxBits, !IsSigned);
}
// Find largest int.
switch (BuiltinOp) {
default:
llvm_unreachable("Invalid value for BuiltinOp");
case Builtin::BI__builtin_add_overflow:
case Builtin::BI__builtin_sadd_overflow:
case Builtin::BI__builtin_saddl_overflow:
case Builtin::BI__builtin_saddll_overflow:
case Builtin::BI__builtin_uadd_overflow:
case Builtin::BI__builtin_uaddl_overflow:
case Builtin::BI__builtin_uaddll_overflow:
Result = LHS.isSigned() ? LHS.sadd_ov(RHS, Overflow)
: LHS.uadd_ov(RHS, Overflow);
break;
case Builtin::BI__builtin_sub_overflow:
case Builtin::BI__builtin_ssub_overflow:
case Builtin::BI__builtin_ssubl_overflow:
case Builtin::BI__builtin_ssubll_overflow:
case Builtin::BI__builtin_usub_overflow:
case Builtin::BI__builtin_usubl_overflow:
case Builtin::BI__builtin_usubll_overflow:
Result = LHS.isSigned() ? LHS.ssub_ov(RHS, Overflow)
: LHS.usub_ov(RHS, Overflow);
break;
case Builtin::BI__builtin_mul_overflow:
case Builtin::BI__builtin_smul_overflow:
case Builtin::BI__builtin_smull_overflow:
case Builtin::BI__builtin_smulll_overflow:
case Builtin::BI__builtin_umul_overflow:
case Builtin::BI__builtin_umull_overflow:
case Builtin::BI__builtin_umulll_overflow:
Result = LHS.isSigned() ? LHS.smul_ov(RHS, Overflow)
: LHS.umul_ov(RHS, Overflow);
break;
}
// In the case where multiple sizes are allowed, truncate and see if
// the values are the same.
if (BuiltinOp == Builtin::BI__builtin_add_overflow ||
BuiltinOp == Builtin::BI__builtin_sub_overflow ||
BuiltinOp == Builtin::BI__builtin_mul_overflow) {
// APSInt doesn't have a TruncOrSelf, so we use extOrTrunc instead,
// since it will give us the behavior of a TruncOrSelf in the case where
// its parameter <= its size. We previously set Result to be at least the
// type-size of the result, so getTypeSize(ResultType) <= Resu
APSInt Temp = Result.extOrTrunc(S.getASTContext().getTypeSize(ResultType));
Temp.setIsSigned(ResultType->isSignedIntegerOrEnumerationType());
if (!APSInt::isSameValue(Temp, Result))
Overflow = true;
Result = Temp;
}
// Write Result to ResultPtr and put Overflow on the stacl.
assignInteger(ResultPtr, ResultT, Result);
ResultPtr.initialize();
assert(Func->getDecl()->getReturnType()->isBooleanType());
S.Stk.push<Boolean>(Overflow);
return true;
}
/// Three integral values followed by a pointer (lhs, rhs, carry, carryOut).
static bool interp__builtin_carryop(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
unsigned BuiltinOp = Func->getBuiltinID();
PrimType LHST = *S.getContext().classify(Call->getArg(0)->getType());
PrimType RHST = *S.getContext().classify(Call->getArg(1)->getType());
PrimType CarryT = *S.getContext().classify(Call->getArg(2)->getType());
APSInt RHS = peekToAPSInt(S.Stk, RHST,
align(primSize(PT_Ptr)) + align(primSize(CarryT)) +
align(primSize(RHST)));
APSInt LHS =
peekToAPSInt(S.Stk, LHST,
align(primSize(PT_Ptr)) + align(primSize(RHST)) +
align(primSize(CarryT)) + align(primSize(LHST)));
APSInt CarryIn = peekToAPSInt(
S.Stk, LHST, align(primSize(PT_Ptr)) + align(primSize(CarryT)));
APSInt CarryOut;
APSInt Result;
// Copy the number of bits and sign.
Result = LHS;
CarryOut = LHS;
bool FirstOverflowed = false;
bool SecondOverflowed = false;
switch (BuiltinOp) {
default:
llvm_unreachable("Invalid value for BuiltinOp");
case Builtin::BI__builtin_addcb:
case Builtin::BI__builtin_addcs:
case Builtin::BI__builtin_addc:
case Builtin::BI__builtin_addcl:
case Builtin::BI__builtin_addcll:
Result =
LHS.uadd_ov(RHS, FirstOverflowed).uadd_ov(CarryIn, SecondOverflowed);
break;
case Builtin::BI__builtin_subcb:
case Builtin::BI__builtin_subcs:
case Builtin::BI__builtin_subc:
case Builtin::BI__builtin_subcl:
case Builtin::BI__builtin_subcll:
Result =
LHS.usub_ov(RHS, FirstOverflowed).usub_ov(CarryIn, SecondOverflowed);
break;
}
// It is possible for both overflows to happen but CGBuiltin uses an OR so
// this is consistent.
CarryOut = (uint64_t)(FirstOverflowed | SecondOverflowed);
Pointer &CarryOutPtr = S.Stk.peek<Pointer>();
QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType();
PrimType CarryOutT = *S.getContext().classify(CarryOutType);
assignInteger(CarryOutPtr, CarryOutT, CarryOut);
CarryOutPtr.initialize();
assert(Call->getType() == Call->getArg(0)->getType());
pushInteger(S, Result, Call->getType());
return true;
}
static bool interp__builtin_clz(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *Func,
const CallExpr *Call) {
unsigned CallSize = callArgSize(S, Call);
unsigned BuiltinOp = Func->getBuiltinID();
PrimType ValT = *S.getContext().classify(Call->getArg(0));
const APSInt &Val = peekToAPSInt(S.Stk, ValT, CallSize);
// When the argument is 0, the result of GCC builtins is undefined, whereas
// for Microsoft intrinsics, the result is the bit-width of the argument.
bool ZeroIsUndefined = BuiltinOp != Builtin::BI__lzcnt16 &&
BuiltinOp != Builtin::BI__lzcnt &&
BuiltinOp != Builtin::BI__lzcnt64;
if (Val == 0) {
if (Func->getBuiltinID() == Builtin::BI__builtin_clzg &&
Call->getNumArgs() == 2) {
// We have a fallback parameter.
PrimType FallbackT = *S.getContext().classify(Call->getArg(1));
const APSInt &Fallback = peekToAPSInt(S.Stk, FallbackT);
pushInteger(S, Fallback, Call->getType());
return true;
}
if (ZeroIsUndefined)
return false;
}
pushInteger(S, Val.countl_zero(), Call->getType());
return true;
}
static bool interp__builtin_ctz(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *Func,
const CallExpr *Call) {
unsigned CallSize = callArgSize(S, Call);
PrimType ValT = *S.getContext().classify(Call->getArg(0));
const APSInt &Val = peekToAPSInt(S.Stk, ValT, CallSize);
if (Val == 0) {
if (Func->getBuiltinID() == Builtin::BI__builtin_ctzg &&
Call->getNumArgs() == 2) {
// We have a fallback parameter.
PrimType FallbackT = *S.getContext().classify(Call->getArg(1));
const APSInt &Fallback = peekToAPSInt(S.Stk, FallbackT);
pushInteger(S, Fallback, Call->getType());
return true;
}
return false;
}
pushInteger(S, Val.countr_zero(), Call->getType());
return true;
}
static bool interp__builtin_bswap(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
PrimType ReturnT = *S.getContext().classify(Call->getType());
PrimType ValT = *S.getContext().classify(Call->getArg(0));
const APSInt &Val = peekToAPSInt(S.Stk, ValT);
assert(Val.getActiveBits() <= 64);
INT_TYPE_SWITCH(ReturnT,
{ S.Stk.push<T>(T::from(Val.byteSwap().getZExtValue())); });
return true;
}
/// bool __atomic_always_lock_free(size_t, void const volatile*)
/// bool __atomic_is_lock_free(size_t, void const volatile*)
/// bool __c11_atomic_is_lock_free(size_t)
static bool interp__builtin_atomic_lock_free(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
unsigned BuiltinOp = Func->getBuiltinID();
PrimType ValT = *S.getContext().classify(Call->getArg(0));
unsigned SizeValOffset = 0;
if (BuiltinOp != Builtin::BI__c11_atomic_is_lock_free)
SizeValOffset = align(primSize(ValT)) + align(primSize(PT_Ptr));
const APSInt &SizeVal = peekToAPSInt(S.Stk, ValT, SizeValOffset);
auto returnBool = [&S](bool Value) -> bool {
S.Stk.push<Boolean>(Value);
return true;
};
// For __atomic_is_lock_free(sizeof(_Atomic(T))), if the size is a power
// of two less than or equal to the maximum inline atomic width, we know it
// is lock-free. If the size isn't a power of two, or greater than the
// maximum alignment where we promote atomics, we know it is not lock-free
// (at least not in the sense of atomic_is_lock_free). Otherwise,
// the answer can only be determined at runtime; for example, 16-byte
// atomics have lock-free implementations on some, but not all,
// x86-64 processors.
// Check power-of-two.
CharUnits Size = CharUnits::fromQuantity(SizeVal.getZExtValue());
if (Size.isPowerOfTwo()) {
// Check against inlining width.
unsigned InlineWidthBits =
S.getASTContext().getTargetInfo().getMaxAtomicInlineWidth();
if (Size <= S.getASTContext().toCharUnitsFromBits(InlineWidthBits)) {
// OK, we will inline appropriately-aligned operations of this size,
// and _Atomic(T) is appropriately-aligned.
if (BuiltinOp == Builtin::BI__c11_atomic_is_lock_free ||
Size == CharUnits::One())
return returnBool(true);
// Same for null pointers.
assert(BuiltinOp != Builtin::BI__c11_atomic_is_lock_free);
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (Ptr.isZero())
return returnBool(true);
if (Ptr.isIntegralPointer()) {
uint64_t IntVal = Ptr.getIntegerRepresentation();
if (APSInt(APInt(64, IntVal, false), true).isAligned(Size.getAsAlign()))
return returnBool(true);
}
const Expr *PtrArg = Call->getArg(1);
// Otherwise, check if the type's alignment against Size.
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PtrArg)) {
// Drop the potential implicit-cast to 'const volatile void*', getting
// the underlying type.
if (ICE->getCastKind() == CK_BitCast)
PtrArg = ICE->getSubExpr();
}
if (auto PtrTy = PtrArg->getType()->getAs<PointerType>()) {
QualType PointeeType = PtrTy->getPointeeType();
if (!PointeeType->isIncompleteType() &&
S.getASTContext().getTypeAlignInChars(PointeeType) >= Size) {
// OK, we will inline operations on this object.
return returnBool(true);
}
}
}
}
if (BuiltinOp == Builtin::BI__atomic_always_lock_free)
return returnBool(false);
return false;
}
/// __builtin_complex(Float A, float B);
static bool interp__builtin_complex(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
const Floating &Arg2 = S.Stk.peek<Floating>();
const Floating &Arg1 = S.Stk.peek<Floating>(align(primSize(PT_Float)) * 2);
Pointer &Result = S.Stk.peek<Pointer>(align(primSize(PT_Float)) * 2 +
align(primSize(PT_Ptr)));
Result.atIndex(0).deref<Floating>() = Arg1;
Result.atIndex(0).initialize();
Result.atIndex(1).deref<Floating>() = Arg2;
Result.atIndex(1).initialize();
Result.initialize();
return true;
}
/// __builtin_is_aligned()
/// __builtin_align_up()
/// __builtin_align_down()
/// The first parameter is either an integer or a pointer.
/// The second parameter is the requested alignment as an integer.
static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
unsigned BuiltinOp = Func->getBuiltinID();
unsigned CallSize = callArgSize(S, Call);
PrimType AlignmentT = *S.Ctx.classify(Call->getArg(1));
const APSInt &Alignment = peekToAPSInt(S.Stk, AlignmentT);
if (Alignment < 0 || !Alignment.isPowerOf2()) {
S.FFDiag(Call, diag::note_constexpr_invalid_alignment) << Alignment;
return false;
}
unsigned SrcWidth = S.getASTContext().getIntWidth(Call->getArg(0)->getType());
APSInt MaxValue(APInt::getOneBitSet(SrcWidth, SrcWidth - 1));
if (APSInt::compareValues(Alignment, MaxValue) > 0) {
S.FFDiag(Call, diag::note_constexpr_alignment_too_big)
<< MaxValue << Call->getArg(0)->getType() << Alignment;
return false;
}
// The first parameter is either an integer or a pointer (but not a function
// pointer).
PrimType FirstArgT = *S.Ctx.classify(Call->getArg(0));
if (isIntegralType(FirstArgT)) {
const APSInt &Src = peekToAPSInt(S.Stk, FirstArgT, CallSize);
APSInt Align = Alignment.extOrTrunc(Src.getBitWidth());
if (BuiltinOp == Builtin::BI__builtin_align_up) {
APSInt AlignedVal =
APSInt((Src + (Align - 1)) & ~(Align - 1), Src.isUnsigned());
pushInteger(S, AlignedVal, Call->getType());
} else if (BuiltinOp == Builtin::BI__builtin_align_down) {
APSInt AlignedVal = APSInt(Src & ~(Align - 1), Src.isUnsigned());
pushInteger(S, AlignedVal, Call->getType());
} else {
assert(*S.Ctx.classify(Call->getType()) == PT_Bool);
S.Stk.push<Boolean>((Src & (Align - 1)) == 0);
}
return true;
}
assert(FirstArgT == PT_Ptr);
const Pointer &Ptr = S.Stk.peek<Pointer>(CallSize);
unsigned PtrOffset = Ptr.getByteOffset();
PtrOffset = Ptr.getIndex();
CharUnits BaseAlignment =
S.getASTContext().getDeclAlign(Ptr.getDeclDesc()->asValueDecl());
CharUnits PtrAlign =
BaseAlignment.alignmentAtOffset(CharUnits::fromQuantity(PtrOffset));
if (BuiltinOp == Builtin::BI__builtin_is_aligned) {
if (PtrAlign.getQuantity() >= Alignment) {
S.Stk.push<Boolean>(true);
return true;
}
// If the alignment is not known to be sufficient, some cases could still
// be aligned at run time. However, if the requested alignment is less or
// equal to the base alignment and the offset is not aligned, we know that
// the run-time value can never be aligned.
if (BaseAlignment.getQuantity() >= Alignment &&
PtrAlign.getQuantity() < Alignment) {
S.Stk.push<Boolean>(false);
return true;
}
S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_compute)
<< Alignment;
return false;
}
assert(BuiltinOp == Builtin::BI__builtin_align_down ||
BuiltinOp == Builtin::BI__builtin_align_up);
// For align_up/align_down, we can return the same value if the alignment
// is known to be greater or equal to the requested value.
if (PtrAlign.getQuantity() >= Alignment) {
S.Stk.push<Pointer>(Ptr);
return true;
}
// The alignment could be greater than the minimum at run-time, so we cannot
// infer much about the resulting pointer value. One case is possible:
// For `_Alignas(32) char buf[N]; __builtin_align_down(&buf[idx], 32)` we
// can infer the correct index if the requested alignment is smaller than
// the base alignment so we can perform the computation on the offset.
if (BaseAlignment.getQuantity() >= Alignment) {
assert(Alignment.getBitWidth() <= 64 &&
"Cannot handle > 64-bit address-space");
uint64_t Alignment64 = Alignment.getZExtValue();
CharUnits NewOffset =
CharUnits::fromQuantity(BuiltinOp == Builtin::BI__builtin_align_down
? llvm::alignDown(PtrOffset, Alignment64)
: llvm::alignTo(PtrOffset, Alignment64));
S.Stk.push<Pointer>(Ptr.atIndex(NewOffset.getQuantity()));
return true;
}
// Otherwise, we cannot constant-evaluate the result.
S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_adjust) << Alignment;
return false;
}
/// __builtin_assume_aligned(Ptr, Alignment[, ExtraOffset])
static bool interp__builtin_assume_aligned(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
assert(Call->getNumArgs() == 2 || Call->getNumArgs() == 3);
// Might be called with function pointers in C.
std::optional<PrimType> PtrT = S.Ctx.classify(Call->getArg(0));
if (PtrT != PT_Ptr)
return false;
unsigned ArgSize = callArgSize(S, Call);
const Pointer &Ptr = S.Stk.peek<Pointer>(ArgSize);
std::optional<APSInt> ExtraOffset;
APSInt Alignment;
if (Call->getNumArgs() == 2) {
Alignment = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1)));
} else {
PrimType AlignmentT = *S.Ctx.classify(Call->getArg(1));
PrimType ExtraOffsetT = *S.Ctx.classify(Call->getArg(2));
Alignment = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(1)),
align(primSize(AlignmentT)) +
align(primSize(ExtraOffsetT)));
ExtraOffset = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(2)));
}
CharUnits Align = CharUnits::fromQuantity(Alignment.getZExtValue());
// If there is a base object, then it must have the correct alignment.
if (Ptr.isBlockPointer()) {
CharUnits BaseAlignment;
if (const auto *VD = Ptr.getDeclDesc()->asValueDecl())
BaseAlignment = S.getASTContext().getDeclAlign(VD);
else if (const auto *E = Ptr.getDeclDesc()->asExpr())
BaseAlignment = GetAlignOfExpr(S.getASTContext(), E, UETT_AlignOf);
if (BaseAlignment < Align) {
S.CCEDiag(Call->getArg(0),
diag::note_constexpr_baa_insufficient_alignment)
<< 0 << BaseAlignment.getQuantity() << Align.getQuantity();
return false;
}
}
APValue AV = Ptr.toAPValue(S.getASTContext());
CharUnits AVOffset = AV.getLValueOffset();
if (ExtraOffset)
AVOffset -= CharUnits::fromQuantity(ExtraOffset->getZExtValue());
if (AVOffset.alignTo(Align) != AVOffset) {
if (Ptr.isBlockPointer())
S.CCEDiag(Call->getArg(0),
diag::note_constexpr_baa_insufficient_alignment)
<< 1 << AVOffset.getQuantity() << Align.getQuantity();
else
S.CCEDiag(Call->getArg(0),
diag::note_constexpr_baa_value_insufficient_alignment)
<< AVOffset.getQuantity() << Align.getQuantity();
return false;
}
S.Stk.push<Pointer>(Ptr);
return true;
}
static bool interp__builtin_ia32_bextr(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
if (Call->getNumArgs() != 2 || !Call->getArg(0)->getType()->isIntegerType() ||
!Call->getArg(1)->getType()->isIntegerType())
return false;
PrimType ValT = *S.Ctx.classify(Call->getArg(0));
PrimType IndexT = *S.Ctx.classify(Call->getArg(1));
APSInt Val = peekToAPSInt(S.Stk, ValT,
align(primSize(ValT)) + align(primSize(IndexT)));
APSInt Index = peekToAPSInt(S.Stk, IndexT);
unsigned BitWidth = Val.getBitWidth();
uint64_t Shift = Index.extractBitsAsZExtValue(8, 0);
uint64_t Length = Index.extractBitsAsZExtValue(8, 8);
Length = Length > BitWidth ? BitWidth : Length;
// Handle out of bounds cases.
if (Length == 0 || Shift >= BitWidth) {
pushInteger(S, 0, Call->getType());
return true;
}
uint64_t Result = Val.getZExtValue() >> Shift;
Result &= llvm::maskTrailingOnes<uint64_t>(Length);
pushInteger(S, Result, Call->getType());
return true;
}
static bool interp__builtin_ia32_bzhi(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
QualType CallType = Call->getType();
if (Call->getNumArgs() != 2 || !Call->getArg(0)->getType()->isIntegerType() ||
!Call->getArg(1)->getType()->isIntegerType() ||
!CallType->isIntegerType())
return false;
PrimType ValT = *S.Ctx.classify(Call->getArg(0));
PrimType IndexT = *S.Ctx.classify(Call->getArg(1));
APSInt Val = peekToAPSInt(S.Stk, ValT,
align(primSize(ValT)) + align(primSize(IndexT)));
APSInt Idx = peekToAPSInt(S.Stk, IndexT);
unsigned BitWidth = Val.getBitWidth();
uint64_t Index = Idx.extractBitsAsZExtValue(8, 0);
if (Index < BitWidth)
Val.clearHighBits(BitWidth - Index);
pushInteger(S, Val, CallType);
return true;
}
static bool interp__builtin_ia32_lzcnt(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
QualType CallType = Call->getType();
if (!CallType->isIntegerType() ||
!Call->getArg(0)->getType()->isIntegerType())
return false;
APSInt Val = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(0)));
pushInteger(S, Val.countLeadingZeros(), CallType);
return true;
}
static bool interp__builtin_ia32_tzcnt(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
QualType CallType = Call->getType();
if (!CallType->isIntegerType() ||
!Call->getArg(0)->getType()->isIntegerType())
return false;
APSInt Val = peekToAPSInt(S.Stk, *S.Ctx.classify(Call->getArg(0)));
pushInteger(S, Val.countTrailingZeros(), CallType);
return true;
}
static bool interp__builtin_ia32_pdep(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
if (Call->getNumArgs() != 2 || !Call->getArg(0)->getType()->isIntegerType() ||
!Call->getArg(1)->getType()->isIntegerType())
return false;
PrimType ValT = *S.Ctx.classify(Call->getArg(0));
PrimType MaskT = *S.Ctx.classify(Call->getArg(1));
APSInt Val =
peekToAPSInt(S.Stk, ValT, align(primSize(ValT)) + align(primSize(MaskT)));
APSInt Mask = peekToAPSInt(S.Stk, MaskT);
unsigned BitWidth = Val.getBitWidth();
APInt Result = APInt::getZero(BitWidth);
for (unsigned I = 0, P = 0; I != BitWidth; ++I) {
if (Mask[I])
Result.setBitVal(I, Val[P++]);
}
pushInteger(S, Result, Call->getType());
return true;
}
static bool interp__builtin_ia32_pext(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
if (Call->getNumArgs() != 2 || !Call->getArg(0)->getType()->isIntegerType() ||
!Call->getArg(1)->getType()->isIntegerType())
return false;
PrimType ValT = *S.Ctx.classify(Call->getArg(0));
PrimType MaskT = *S.Ctx.classify(Call->getArg(1));
APSInt Val =
peekToAPSInt(S.Stk, ValT, align(primSize(ValT)) + align(primSize(MaskT)));
APSInt Mask = peekToAPSInt(S.Stk, MaskT);
unsigned BitWidth = Val.getBitWidth();
APInt Result = APInt::getZero(BitWidth);
for (unsigned I = 0, P = 0; I != BitWidth; ++I) {
if (Mask[I])
Result.setBitVal(P++, Val[I]);
}
pushInteger(S, Result, Call->getType());
return true;
}
static bool interp__builtin_ia32_addcarry_subborrow(InterpState &S,
CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
if (Call->getNumArgs() != 4 || !Call->getArg(0)->getType()->isIntegerType() ||
!Call->getArg(1)->getType()->isIntegerType() ||
!Call->getArg(2)->getType()->isIntegerType())
return false;
unsigned BuiltinOp = Func->getBuiltinID();
APSInt CarryIn = getAPSIntParam(Frame, 0);
APSInt LHS = getAPSIntParam(Frame, 1);
APSInt RHS = getAPSIntParam(Frame, 2);
bool IsAdd = BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u32 ||
BuiltinOp == clang::X86::BI__builtin_ia32_addcarryx_u64;
unsigned BitWidth = LHS.getBitWidth();
unsigned CarryInBit = CarryIn.ugt(0) ? 1 : 0;
APInt ExResult =
IsAdd ? (LHS.zext(BitWidth + 1) + (RHS.zext(BitWidth + 1) + CarryInBit))
: (LHS.zext(BitWidth + 1) - (RHS.zext(BitWidth + 1) + CarryInBit));
APInt Result = ExResult.extractBits(BitWidth, 0);
APSInt CarryOut =
APSInt(ExResult.extractBits(1, BitWidth), /*IsUnsigned=*/true);
Pointer &CarryOutPtr = S.Stk.peek<Pointer>();
QualType CarryOutType = Call->getArg(3)->getType()->getPointeeType();
PrimType CarryOutT = *S.getContext().classify(CarryOutType);
assignInteger(CarryOutPtr, CarryOutT, APSInt(Result, true));
pushInteger(S, CarryOut, Call->getType());
return true;
}
static bool interp__builtin_os_log_format_buffer_size(InterpState &S,
CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
analyze_os_log::OSLogBufferLayout Layout;
analyze_os_log::computeOSLogBufferLayout(S.getASTContext(), Call, Layout);
pushInteger(S, Layout.size().getQuantity(), Call->getType());
return true;
}
static bool interp__builtin_ptrauth_string_discriminator(
InterpState &S, CodePtr OpPC, const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
const auto &Ptr = S.Stk.peek<Pointer>();
assert(Ptr.getFieldDesc()->isPrimitiveArray());
// This should be created for a StringLiteral, so should alway shold at least
// one array element.
assert(Ptr.getFieldDesc()->getNumElems() >= 1);
StringRef R(&Ptr.deref<char>(), Ptr.getFieldDesc()->getNumElems() - 1);
uint64_t Result = getPointerAuthStableSipHash(R);
pushInteger(S, Result, Call->getType());
return true;
}
static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
// A call to __operator_new is only valid within std::allocate<>::allocate.
// Walk up the call stack to find the appropriate caller and get the
// element type from it.
QualType ElemType;
const CallExpr *NewCall = nullptr;
for (const InterpFrame *F = Frame; F; F = F->Caller) {
const Function *Func = F->getFunction();
if (!Func)
continue;
const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Func->getDecl());
if (!MD)
continue;
const IdentifierInfo *FnII = MD->getIdentifier();
if (!FnII || !FnII->isStr("allocate"))
continue;
const auto *CTSD =
dyn_cast<ClassTemplateSpecializationDecl>(MD->getParent());
if (!CTSD)
continue;
const IdentifierInfo *ClassII = CTSD->getIdentifier();
const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr("allocator") &&
TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
ElemType = TAL[0].getAsType();
NewCall = cast<CallExpr>(F->Caller->getExpr(F->getRetPC()));
break;
}
}
if (ElemType.isNull()) {
S.FFDiag(Call, S.getLangOpts().CPlusPlus20
? diag::note_constexpr_new_untyped
: diag::note_constexpr_new);
return false;
}
assert(NewCall);
if (ElemType->isIncompleteType() || ElemType->isFunctionType()) {
S.FFDiag(Call, diag::note_constexpr_new_not_complete_object_type)
<< (ElemType->isIncompleteType() ? 0 : 1) << ElemType;
return false;
}
APSInt Bytes = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(0)));
CharUnits ElemSize = S.getASTContext().getTypeSizeInChars(ElemType);
assert(!ElemSize.isZero());
// Divide the number of bytes by sizeof(ElemType), so we get the number of
// elements we should allocate.
APInt NumElems, Remainder;
APInt ElemSizeAP(Bytes.getBitWidth(), ElemSize.getQuantity());
APInt::udivrem(Bytes, ElemSizeAP, NumElems, Remainder);
if (Remainder != 0) {
// This likely indicates a bug in the implementation of 'std::allocator'.
S.FFDiag(Call, diag::note_constexpr_operator_new_bad_size)
<< Bytes << APSInt(ElemSizeAP, true) << ElemType;
return false;
}
// NB: The same check we're using in CheckArraySize()
if (NumElems.getActiveBits() >
ConstantArrayType::getMaxSizeBits(S.getASTContext()) ||
NumElems.ugt(Descriptor::MaxArrayElemBytes / ElemSize.getQuantity())) {
// FIXME: NoThrow check?
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_new_too_large)
<< NumElems.getZExtValue();
return false;
}
bool IsArray = NumElems.ugt(1);
std::optional<PrimType> ElemT = S.getContext().classify(ElemType);
DynamicAllocator &Allocator = S.getAllocator();
if (ElemT) {
if (IsArray) {
Block *B = Allocator.allocate(NewCall, *ElemT, NumElems.getZExtValue(),
S.Ctx.getEvalID(),
DynamicAllocator::Form::Operator);
assert(B);
S.Stk.push<Pointer>(Pointer(B).atIndex(0));
return true;
}
const Descriptor *Desc = S.P.createDescriptor(
NewCall, *ElemT, ElemType.getTypePtr(), Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false,
/*IsMutable=*/false);
Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(),
DynamicAllocator::Form::Operator);
assert(B);
S.Stk.push<Pointer>(B);
return true;
}
assert(!ElemT);
// Structs etc.
const Descriptor *Desc = S.P.createDescriptor(
NewCall, ElemType.getTypePtr(),
IsArray ? std::nullopt : Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false,
/*Init=*/nullptr);
if (IsArray) {
Block *B =
Allocator.allocate(Desc, NumElems.getZExtValue(), S.Ctx.getEvalID(),
DynamicAllocator::Form::Operator);
assert(B);
S.Stk.push<Pointer>(Pointer(B).atIndex(0));
return true;
}
Block *B = Allocator.allocate(Desc, S.getContext().getEvalID(),
DynamicAllocator::Form::Operator);
assert(B);
S.Stk.push<Pointer>(B);
return true;
}
static bool interp__builtin_operator_delete(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
const Expr *Source = nullptr;
const Block *BlockToDelete = nullptr;
{
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (Ptr.isZero()) {
S.CCEDiag(Call, diag::note_constexpr_deallocate_null);
return true;
}
Source = Ptr.getDeclDesc()->asExpr();
BlockToDelete = Ptr.block();
}
assert(BlockToDelete);
DynamicAllocator &Allocator = S.getAllocator();
const Descriptor *BlockDesc = BlockToDelete->getDescriptor();
std::optional<DynamicAllocator::Form> AllocForm =
Allocator.getAllocationForm(Source);
if (!Allocator.deallocate(Source, BlockToDelete, S)) {
// Nothing has been deallocated, this must be a double-delete.
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_double_delete);
return false;
}
assert(AllocForm);
return CheckNewDeleteForms(
S, OpPC, *AllocForm, DynamicAllocator::Form::Operator, BlockDesc, Source);
}
static bool interp__builtin_arithmetic_fence(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
const Floating &Arg0 = S.Stk.peek<Floating>();
S.Stk.push<Floating>(Arg0);
return true;
}
static bool interp__builtin_vector_reduce(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
const Pointer &Arg = S.Stk.peek<Pointer>();
assert(Arg.getFieldDesc()->isPrimitiveArray());
unsigned ID = Func->getBuiltinID();
QualType ElemType = Arg.getFieldDesc()->getElemQualType();
assert(Call->getType() == ElemType);
PrimType ElemT = *S.getContext().classify(ElemType);
unsigned NumElems = Arg.getNumElems();
INT_TYPE_SWITCH_NO_BOOL(ElemT, {
T Result = Arg.atIndex(0).deref<T>();
unsigned BitWidth = Result.bitWidth();
for (unsigned I = 1; I != NumElems; ++I) {
T Elem = Arg.atIndex(I).deref<T>();
T PrevResult = Result;
if (ID == Builtin::BI__builtin_reduce_add) {
if (T::add(Result, Elem, BitWidth, &Result)) {
unsigned OverflowBits = BitWidth + 1;
(void)handleOverflow(S, OpPC,
(PrevResult.toAPSInt(OverflowBits) +
Elem.toAPSInt(OverflowBits)));
return false;
}
} else if (ID == Builtin::BI__builtin_reduce_mul) {
if (T::mul(Result, Elem, BitWidth, &Result)) {
unsigned OverflowBits = BitWidth * 2;
(void)handleOverflow(S, OpPC,
(PrevResult.toAPSInt(OverflowBits) *
Elem.toAPSInt(OverflowBits)));
return false;
}
} else if (ID == Builtin::BI__builtin_reduce_and) {
(void)T::bitAnd(Result, Elem, BitWidth, &Result);
} else if (ID == Builtin::BI__builtin_reduce_or) {
(void)T::bitOr(Result, Elem, BitWidth, &Result);
} else if (ID == Builtin::BI__builtin_reduce_xor) {
(void)T::bitXor(Result, Elem, BitWidth, &Result);
} else {
llvm_unreachable("Unhandled vector reduce builtin");
}
}
pushInteger(S, Result.toAPSInt(), Call->getType());
});
return true;
}
/// Can be called with an integer or vector as the first and only parameter.
static bool interp__builtin_elementwise_popcount(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
assert(Call->getNumArgs() == 1);
if (Call->getArg(0)->getType()->isIntegerType()) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = peekToAPSInt(S.Stk, ArgT);
pushInteger(S, Val.popcount(), Call->getType());
return true;
}
// Otherwise, the argument must be a vector.
assert(Call->getArg(0)->getType()->isVectorType());
const Pointer &Arg = S.Stk.peek<Pointer>();
assert(Arg.getFieldDesc()->isPrimitiveArray());
const Pointer &Dst = S.Stk.peek<Pointer>(primSize(PT_Ptr) * 2);
assert(Dst.getFieldDesc()->isPrimitiveArray());
assert(Arg.getFieldDesc()->getNumElems() ==
Dst.getFieldDesc()->getNumElems());
QualType ElemType = Arg.getFieldDesc()->getElemQualType();
PrimType ElemT = *S.getContext().classify(ElemType);
unsigned NumElems = Arg.getNumElems();
// FIXME: Reading from uninitialized vector elements?
for (unsigned I = 0; I != NumElems; ++I) {
INT_TYPE_SWITCH_NO_BOOL(ElemT, {
Dst.atIndex(I).deref<T>() =
T::from(Arg.atIndex(I).deref<T>().toAPSInt().popcount());
Dst.atIndex(I).initialize();
});
}
return true;
}
static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
assert(Call->getNumArgs() == 3);
unsigned ID = Func->getBuiltinID();
Pointer DestPtr = getParam<Pointer>(Frame, 0);
const ASTContext &ASTCtx = S.getASTContext();
const Pointer &SrcPtr = getParam<Pointer>(Frame, 1);
APSInt Size = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)));
assert(!Size.isSigned() && "memcpy and friends take an unsigned size");
if (ID == Builtin::BImemcpy || ID == Builtin::BImemmove)
diagnoseNonConstexprBuiltin(S, OpPC, ID);
bool Move =
(ID == Builtin::BI__builtin_memmove || ID == Builtin::BImemmove ||
ID == Builtin::BI__builtin_wmemmove || ID == Builtin::BIwmemmove);
bool WChar = ID == Builtin::BIwmemcpy || ID == Builtin::BIwmemmove ||
ID == Builtin::BI__builtin_wmemcpy ||
ID == Builtin::BI__builtin_wmemmove;
// If the size is zero, we treat this as always being a valid no-op.
if (Size.isZero()) {
S.Stk.push<Pointer>(DestPtr);
return true;
}
if (SrcPtr.isZero() || DestPtr.isZero()) {
Pointer DiagPtr = (SrcPtr.isZero() ? SrcPtr : DestPtr);
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_null)
<< /*IsMove=*/Move << /*IsWchar=*/WChar << !SrcPtr.isZero()
<< DiagPtr.toDiagnosticString(ASTCtx);
return false;
}
// Diagnose integral src/dest pointers specially.
if (SrcPtr.isIntegralPointer() || DestPtr.isIntegralPointer()) {
std::string DiagVal = "(void *)";
DiagVal += SrcPtr.isIntegralPointer()
? std::to_string(SrcPtr.getIntegerRepresentation())
: std::to_string(DestPtr.getIntegerRepresentation());
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_null)
<< Move << WChar << DestPtr.isIntegralPointer() << DiagVal;
return false;
}
// Can't read from dummy pointers.
if (DestPtr.isDummy() || SrcPtr.isDummy())
return false;
QualType DestElemType = getElemType(DestPtr);
size_t RemainingDestElems;
if (DestPtr.getFieldDesc()->isArray()) {
RemainingDestElems = DestPtr.isUnknownSizeArray()
? 0
: (DestPtr.getNumElems() - DestPtr.getIndex());
} else {
RemainingDestElems = 1;
}
unsigned DestElemSize = ASTCtx.getTypeSizeInChars(DestElemType).getQuantity();
if (WChar) {
uint64_t WCharSize =
ASTCtx.getTypeSizeInChars(ASTCtx.getWCharType()).getQuantity();
Size *= APSInt(APInt(Size.getBitWidth(), WCharSize, /*IsSigned=*/false),
/*IsUnsigend=*/true);
}
if (Size.urem(DestElemSize) != 0) {
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_memcpy_unsupported)
<< Move << WChar << 0 << DestElemType << Size << DestElemSize;
return false;
}
QualType SrcElemType = getElemType(SrcPtr);
size_t RemainingSrcElems;
if (SrcPtr.getFieldDesc()->isArray()) {
RemainingSrcElems = SrcPtr.isUnknownSizeArray()
? 0
: (SrcPtr.getNumElems() - SrcPtr.getIndex());
} else {
RemainingSrcElems = 1;
}
unsigned SrcElemSize = ASTCtx.getTypeSizeInChars(SrcElemType).getQuantity();
if (!ASTCtx.hasSameUnqualifiedType(DestElemType, SrcElemType)) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_type_pun)
<< Move << SrcElemType << DestElemType;
return false;
}
if (DestElemType->isIncompleteType() ||
DestPtr.getType()->isIncompleteType()) {
QualType DiagType =
DestElemType->isIncompleteType() ? DestElemType : DestPtr.getType();
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_memcpy_incomplete_type)
<< Move << DiagType;
return false;
}
if (!DestElemType.isTriviallyCopyableType(ASTCtx)) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_nontrivial)
<< Move << DestElemType;
return false;
}
// Check if we have enough elements to read from and write to.
size_t RemainingDestBytes = RemainingDestElems * DestElemSize;
size_t RemainingSrcBytes = RemainingSrcElems * SrcElemSize;
if (Size.ugt(RemainingDestBytes) || Size.ugt(RemainingSrcBytes)) {
APInt N = Size.udiv(DestElemSize);
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_memcpy_unsupported)
<< Move << WChar << (Size.ugt(RemainingSrcBytes) ? 1 : 2)
<< DestElemType << toString(N, 10, /*Signed=*/false);
return false;
}
// Check for overlapping memory regions.
if (!Move && Pointer::pointToSameBlock(SrcPtr, DestPtr)) {
unsigned SrcIndex = SrcPtr.getIndex() * SrcPtr.elemSize();
unsigned DstIndex = DestPtr.getIndex() * DestPtr.elemSize();
unsigned N = Size.getZExtValue();
if ((SrcIndex <= DstIndex && (SrcIndex + N) > DstIndex) ||
(DstIndex <= SrcIndex && (DstIndex + N) > SrcIndex)) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_memcpy_overlap)
<< /*IsWChar=*/false;
return false;
}
}
assert(Size.getZExtValue() % DestElemSize == 0);
if (!DoMemcpy(S, OpPC, SrcPtr, DestPtr, Bytes(Size.getZExtValue()).toBits()))
return false;
S.Stk.push<Pointer>(DestPtr);
return true;
}
/// Determine if T is a character type for which we guarantee that
/// sizeof(T) == 1.
static bool isOneByteCharacterType(QualType T) {
return T->isCharType() || T->isChar8Type();
}
static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
assert(Call->getNumArgs() == 3);
unsigned ID = Func->getBuiltinID();
const Pointer &PtrA = getParam<Pointer>(Frame, 0);
const Pointer &PtrB = getParam<Pointer>(Frame, 1);
const APSInt &Size =
peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)));
if (ID == Builtin::BImemcmp || ID == Builtin::BIbcmp ||
ID == Builtin::BIwmemcmp)
diagnoseNonConstexprBuiltin(S, OpPC, ID);
if (Size.isZero()) {
pushInteger(S, 0, Call->getType());
return true;
}
bool IsWide =
(ID == Builtin::BIwmemcmp || ID == Builtin::BI__builtin_wmemcmp);
const ASTContext &ASTCtx = S.getASTContext();
QualType ElemTypeA = getElemType(PtrA);
QualType ElemTypeB = getElemType(PtrB);
// FIXME: This is an arbitrary limitation the current constant interpreter
// had. We could remove this.
if (!IsWide && (!isOneByteCharacterType(ElemTypeA) ||
!isOneByteCharacterType(ElemTypeB))) {
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_memcmp_unsupported)
<< ASTCtx.BuiltinInfo.getQuotedName(ID) << PtrA.getType()
<< PtrB.getType();
return false;
}
if (PtrA.isDummy() || PtrB.isDummy())
return false;
// Now, read both pointers to a buffer and compare those.
BitcastBuffer BufferA(
Bits(ASTCtx.getTypeSize(ElemTypeA) * PtrA.getNumElems()));
readPointerToBuffer(S.getContext(), PtrA, BufferA, false);
// FIXME: The swapping here is UNDOING something we do when reading the
// data into the buffer.
if (ASTCtx.getTargetInfo().isBigEndian())
swapBytes(BufferA.Data.get(), BufferA.byteSize().getQuantity());
BitcastBuffer BufferB(
Bits(ASTCtx.getTypeSize(ElemTypeB) * PtrB.getNumElems()));
readPointerToBuffer(S.getContext(), PtrB, BufferB, false);
// FIXME: The swapping here is UNDOING something we do when reading the
// data into the buffer.
if (ASTCtx.getTargetInfo().isBigEndian())
swapBytes(BufferB.Data.get(), BufferB.byteSize().getQuantity());
size_t MinBufferSize = std::min(BufferA.byteSize().getQuantity(),
BufferB.byteSize().getQuantity());
unsigned ElemSize = 1;
if (IsWide)
ElemSize = ASTCtx.getTypeSizeInChars(ASTCtx.getWCharType()).getQuantity();
// The Size given for the wide variants is in wide-char units. Convert it
// to bytes.
size_t ByteSize = Size.getZExtValue() * ElemSize;
size_t CmpSize = std::min(MinBufferSize, ByteSize);
for (size_t I = 0; I != CmpSize; I += ElemSize) {
if (IsWide) {
INT_TYPE_SWITCH(*S.getContext().classify(ASTCtx.getWCharType()), {
T A = *reinterpret_cast<T *>(BufferA.Data.get() + I);
T B = *reinterpret_cast<T *>(BufferB.Data.get() + I);
if (A < B) {
pushInteger(S, -1, Call->getType());
return true;
} else if (A > B) {
pushInteger(S, 1, Call->getType());
return true;
}
});
} else {
std::byte A = BufferA.Data[I];
std::byte B = BufferB.Data[I];
if (A < B) {
pushInteger(S, -1, Call->getType());
return true;
} else if (A > B) {
pushInteger(S, 1, Call->getType());
return true;
}
}
}
// We compared CmpSize bytes above. If the limiting factor was the Size
// passed, we're done and the result is equality (0).
if (ByteSize <= CmpSize) {
pushInteger(S, 0, Call->getType());
return true;
}
// However, if we read all the available bytes but were instructed to read
// even more, diagnose this as a "read of dereferenced one-past-the-end
// pointer". This is what would happen if we called CheckLoad() on every array
// element.
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_past_end)
<< AK_Read << S.Current->getRange(OpPC);
return false;
}
static bool interp__builtin_memchr(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func, const CallExpr *Call) {
unsigned ID = Func->getBuiltinID();
if (ID == Builtin::BImemchr || ID == Builtin::BIwcschr ||
ID == Builtin::BIstrchr || ID == Builtin::BIwmemchr)
diagnoseNonConstexprBuiltin(S, OpPC, ID);
const Pointer &Ptr = getParam<Pointer>(Frame, 0);
APSInt Desired;
std::optional<APSInt> MaxLength;
if (Call->getNumArgs() == 3) {
MaxLength =
peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(2)), 0);
Desired = peekToAPSInt(
S.Stk, *S.getContext().classify(Call->getArg(1)),
align(primSize(*S.getContext().classify(Call->getArg(2)))) +
align(primSize(*S.getContext().classify(Call->getArg(1)))));
} else {
Desired = peekToAPSInt(S.Stk, *S.getContext().classify(Call->getArg(1)));
}
if (MaxLength && MaxLength->isZero()) {
S.Stk.push<Pointer>();
return true;
}
if (Ptr.isDummy())
return false;
// Null is only okay if the given size is 0.
if (Ptr.isZero()) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null)
<< AK_Read;
return false;
}
QualType ElemTy = Ptr.getFieldDesc()->isArray()
? Ptr.getFieldDesc()->getElemQualType()
: Ptr.getFieldDesc()->getType();
bool IsRawByte = ID == Builtin::BImemchr || ID == Builtin::BI__builtin_memchr;
// Give up on byte-oriented matching against multibyte elements.
if (IsRawByte && !isOneByteCharacterType(ElemTy)) {
S.FFDiag(S.Current->getSource(OpPC),
diag::note_constexpr_memchr_unsupported)
<< S.getASTContext().BuiltinInfo.getQuotedName(ID) << ElemTy;
return false;
}
if (ID == Builtin::BIstrchr || ID == Builtin::BI__builtin_strchr) {
// strchr compares directly to the passed integer, and therefore
// always fails if given an int that is not a char.
if (Desired !=
Desired.trunc(S.getASTContext().getCharWidth()).getSExtValue()) {
S.Stk.push<Pointer>();
return true;
}
}
uint64_t DesiredVal;
if (ID == Builtin::BIwmemchr || ID == Builtin::BI__builtin_wmemchr ||
ID == Builtin::BIwcschr || ID == Builtin::BI__builtin_wcschr) {
// wcschr and wmemchr are given a wchar_t to look for. Just use it.
DesiredVal = Desired.getZExtValue();
} else {
DesiredVal = Desired.trunc(S.getASTContext().getCharWidth()).getZExtValue();
}
bool StopAtZero =
(ID == Builtin::BIstrchr || ID == Builtin::BI__builtin_strchr ||
ID == Builtin::BIwcschr || ID == Builtin::BI__builtin_wcschr);
PrimType ElemT =
IsRawByte ? PT_Sint8 : *S.getContext().classify(getElemType(Ptr));
size_t Index = Ptr.getIndex();
size_t Step = 0;
for (;;) {
const Pointer &ElemPtr =
(Index + Step) > 0 ? Ptr.atIndex(Index + Step) : Ptr;
if (!CheckLoad(S, OpPC, ElemPtr))
return false;
uint64_t V;
INT_TYPE_SWITCH_NO_BOOL(
ElemT, { V = static_cast<uint64_t>(ElemPtr.deref<T>().toUnsigned()); });
if (V == DesiredVal) {
S.Stk.push<Pointer>(ElemPtr);
return true;
}
if (StopAtZero && V == 0)
break;
++Step;
if (MaxLength && Step == MaxLength->getZExtValue())
break;
}
S.Stk.push<Pointer>();
return true;
}
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call, uint32_t BuiltinID) {
if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID))
return Invalid(S, OpPC);
const InterpFrame *Frame = S.Current;
std::optional<PrimType> ReturnT = S.getContext().classify(Call);
switch (BuiltinID) {
case Builtin::BI__builtin_is_constant_evaluated:
if (!interp__builtin_is_constant_evaluated(S, OpPC, Frame, Call))
return false;
break;
case Builtin::BI__builtin_assume:
case Builtin::BI__assume:
break;
case Builtin::BI__builtin_strcmp:
case Builtin::BIstrcmp:
case Builtin::BI__builtin_strncmp:
case Builtin::BIstrncmp:
case Builtin::BI__builtin_wcsncmp:
case Builtin::BIwcsncmp:
case Builtin::BI__builtin_wcscmp:
case Builtin::BIwcscmp:
if (!interp__builtin_strcmp(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_strlen:
case Builtin::BIstrlen:
case Builtin::BI__builtin_wcslen:
case Builtin::BIwcslen:
if (!interp__builtin_strlen(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_nan:
case Builtin::BI__builtin_nanf:
case Builtin::BI__builtin_nanl:
case Builtin::BI__builtin_nanf16:
case Builtin::BI__builtin_nanf128:
if (!interp__builtin_nan(S, OpPC, Frame, F, /*Signaling=*/false))
return false;
break;
case Builtin::BI__builtin_nans:
case Builtin::BI__builtin_nansf:
case Builtin::BI__builtin_nansl:
case Builtin::BI__builtin_nansf16:
case Builtin::BI__builtin_nansf128:
if (!interp__builtin_nan(S, OpPC, Frame, F, /*Signaling=*/true))
return false;
break;
case Builtin::BI__builtin_huge_val:
case Builtin::BI__builtin_huge_valf:
case Builtin::BI__builtin_huge_vall:
case Builtin::BI__builtin_huge_valf16:
case Builtin::BI__builtin_huge_valf128:
case Builtin::BI__builtin_inf:
case Builtin::BI__builtin_inff:
case Builtin::BI__builtin_infl:
case Builtin::BI__builtin_inff16:
case Builtin::BI__builtin_inff128:
if (!interp__builtin_inf(S, OpPC, Frame, F))
return false;
break;
case Builtin::BI__builtin_copysign:
case Builtin::BI__builtin_copysignf:
case Builtin::BI__builtin_copysignl:
case Builtin::BI__builtin_copysignf128:
if (!interp__builtin_copysign(S, OpPC, Frame, F))
return false;
break;
case Builtin::BI__builtin_fmin:
case Builtin::BI__builtin_fminf:
case Builtin::BI__builtin_fminl:
case Builtin::BI__builtin_fminf16:
case Builtin::BI__builtin_fminf128:
if (!interp__builtin_fmin(S, OpPC, Frame, F, /*IsNumBuiltin=*/false))
return false;
break;
case Builtin::BI__builtin_fminimum_num:
case Builtin::BI__builtin_fminimum_numf:
case Builtin::BI__builtin_fminimum_numl:
case Builtin::BI__builtin_fminimum_numf16:
case Builtin::BI__builtin_fminimum_numf128:
if (!interp__builtin_fmin(S, OpPC, Frame, F, /*IsNumBuiltin=*/true))
return false;
break;
case Builtin::BI__builtin_fmax:
case Builtin::BI__builtin_fmaxf:
case Builtin::BI__builtin_fmaxl:
case Builtin::BI__builtin_fmaxf16:
case Builtin::BI__builtin_fmaxf128:
if (!interp__builtin_fmax(S, OpPC, Frame, F, /*IsNumBuiltin=*/false))
return false;
break;
case Builtin::BI__builtin_fmaximum_num:
case Builtin::BI__builtin_fmaximum_numf:
case Builtin::BI__builtin_fmaximum_numl:
case Builtin::BI__builtin_fmaximum_numf16:
case Builtin::BI__builtin_fmaximum_numf128:
if (!interp__builtin_fmax(S, OpPC, Frame, F, /*IsNumBuiltin=*/true))
return false;
break;
case Builtin::BI__builtin_isnan:
if (!interp__builtin_isnan(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_issignaling:
if (!interp__builtin_issignaling(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_isinf:
if (!interp__builtin_isinf(S, OpPC, Frame, F, /*Sign=*/false, Call))
return false;
break;
case Builtin::BI__builtin_isinf_sign:
if (!interp__builtin_isinf(S, OpPC, Frame, F, /*Sign=*/true, Call))
return false;
break;
case Builtin::BI__builtin_isfinite:
if (!interp__builtin_isfinite(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_isnormal:
if (!interp__builtin_isnormal(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_issubnormal:
if (!interp__builtin_issubnormal(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_iszero:
if (!interp__builtin_iszero(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_signbit:
case Builtin::BI__builtin_signbitf:
case Builtin::BI__builtin_signbitl:
if (!interp__builtin_signbit(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_isgreater:
case Builtin::BI__builtin_isgreaterequal:
case Builtin::BI__builtin_isless:
case Builtin::BI__builtin_islessequal:
case Builtin::BI__builtin_islessgreater:
case Builtin::BI__builtin_isunordered:
if (!interp_floating_comparison(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_isfpclass:
if (!interp__builtin_isfpclass(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_fpclassify:
if (!interp__builtin_fpclassify(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_fabs:
case Builtin::BI__builtin_fabsf:
case Builtin::BI__builtin_fabsl:
case Builtin::BI__builtin_fabsf128:
if (!interp__builtin_fabs(S, OpPC, Frame, F))
return false;
break;
case Builtin::BI__builtin_abs:
case Builtin::BI__builtin_labs:
case Builtin::BI__builtin_llabs:
if (!interp__builtin_abs(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_popcount:
case Builtin::BI__builtin_popcountl:
case Builtin::BI__builtin_popcountll:
case Builtin::BI__builtin_popcountg:
case Builtin::BI__popcnt16: // Microsoft variants of popcount
case Builtin::BI__popcnt:
case Builtin::BI__popcnt64:
if (!interp__builtin_popcount(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_parity:
case Builtin::BI__builtin_parityl:
case Builtin::BI__builtin_parityll:
if (!interp__builtin_parity(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_clrsb:
case Builtin::BI__builtin_clrsbl:
case Builtin::BI__builtin_clrsbll:
if (!interp__builtin_clrsb(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_bitreverse8:
case Builtin::BI__builtin_bitreverse16:
case Builtin::BI__builtin_bitreverse32:
case Builtin::BI__builtin_bitreverse64:
if (!interp__builtin_bitreverse(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_classify_type:
if (!interp__builtin_classify_type(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_expect:
case Builtin::BI__builtin_expect_with_probability:
if (!interp__builtin_expect(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_rotateleft8:
case Builtin::BI__builtin_rotateleft16:
case Builtin::BI__builtin_rotateleft32:
case Builtin::BI__builtin_rotateleft64:
case Builtin::BI_rotl8: // Microsoft variants of rotate left
case Builtin::BI_rotl16:
case Builtin::BI_rotl:
case Builtin::BI_lrotl:
case Builtin::BI_rotl64:
if (!interp__builtin_rotate(S, OpPC, Frame, F, Call, /*Right=*/false))
return false;
break;
case Builtin::BI__builtin_rotateright8:
case Builtin::BI__builtin_rotateright16:
case Builtin::BI__builtin_rotateright32:
case Builtin::BI__builtin_rotateright64:
case Builtin::BI_rotr8: // Microsoft variants of rotate right
case Builtin::BI_rotr16:
case Builtin::BI_rotr:
case Builtin::BI_lrotr:
case Builtin::BI_rotr64:
if (!interp__builtin_rotate(S, OpPC, Frame, F, Call, /*Right=*/true))
return false;
break;
case Builtin::BI__builtin_ffs:
case Builtin::BI__builtin_ffsl:
case Builtin::BI__builtin_ffsll:
if (!interp__builtin_ffs(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
if (!interp__builtin_addressof(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BIas_const:
case Builtin::BIforward:
case Builtin::BIforward_like:
case Builtin::BImove:
case Builtin::BImove_if_noexcept:
if (!interp__builtin_move(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_eh_return_data_regno:
if (!interp__builtin_eh_return_data_regno(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_launder:
if (!noopPointer(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_add_overflow:
case Builtin::BI__builtin_sub_overflow:
case Builtin::BI__builtin_mul_overflow:
case Builtin::BI__builtin_sadd_overflow:
case Builtin::BI__builtin_uadd_overflow:
case Builtin::BI__builtin_uaddl_overflow:
case Builtin::BI__builtin_uaddll_overflow:
case Builtin::BI__builtin_usub_overflow:
case Builtin::BI__builtin_usubl_overflow:
case Builtin::BI__builtin_usubll_overflow:
case Builtin::BI__builtin_umul_overflow:
case Builtin::BI__builtin_umull_overflow:
case Builtin::BI__builtin_umulll_overflow:
case Builtin::BI__builtin_saddl_overflow:
case Builtin::BI__builtin_saddll_overflow:
case Builtin::BI__builtin_ssub_overflow:
case Builtin::BI__builtin_ssubl_overflow:
case Builtin::BI__builtin_ssubll_overflow:
case Builtin::BI__builtin_smul_overflow:
case Builtin::BI__builtin_smull_overflow:
case Builtin::BI__builtin_smulll_overflow:
if (!interp__builtin_overflowop(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_addcb:
case Builtin::BI__builtin_addcs:
case Builtin::BI__builtin_addc:
case Builtin::BI__builtin_addcl:
case Builtin::BI__builtin_addcll:
case Builtin::BI__builtin_subcb:
case Builtin::BI__builtin_subcs:
case Builtin::BI__builtin_subc:
case Builtin::BI__builtin_subcl:
case Builtin::BI__builtin_subcll:
if (!interp__builtin_carryop(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_clz:
case Builtin::BI__builtin_clzl:
case Builtin::BI__builtin_clzll:
case Builtin::BI__builtin_clzs:
case Builtin::BI__builtin_clzg:
case Builtin::BI__lzcnt16: // Microsoft variants of count leading-zeroes
case Builtin::BI__lzcnt:
case Builtin::BI__lzcnt64:
if (!interp__builtin_clz(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_ctz:
case Builtin::BI__builtin_ctzl:
case Builtin::BI__builtin_ctzll:
case Builtin::BI__builtin_ctzs:
case Builtin::BI__builtin_ctzg:
if (!interp__builtin_ctz(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_bswap16:
case Builtin::BI__builtin_bswap32:
case Builtin::BI__builtin_bswap64:
if (!interp__builtin_bswap(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__atomic_always_lock_free:
case Builtin::BI__atomic_is_lock_free:
case Builtin::BI__c11_atomic_is_lock_free:
if (!interp__builtin_atomic_lock_free(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_complex:
if (!interp__builtin_complex(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_is_aligned:
case Builtin::BI__builtin_align_up:
case Builtin::BI__builtin_align_down:
if (!interp__builtin_is_aligned_up_down(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_assume_aligned:
if (!interp__builtin_assume_aligned(S, OpPC, Frame, F, Call))
return false;
break;
case clang::X86::BI__builtin_ia32_bextr_u32:
case clang::X86::BI__builtin_ia32_bextr_u64:
case clang::X86::BI__builtin_ia32_bextri_u32:
case clang::X86::BI__builtin_ia32_bextri_u64:
if (!interp__builtin_ia32_bextr(S, OpPC, Frame, F, Call))
return false;
break;
case clang::X86::BI__builtin_ia32_bzhi_si:
case clang::X86::BI__builtin_ia32_bzhi_di:
if (!interp__builtin_ia32_bzhi(S, OpPC, Frame, F, Call))
return false;
break;
case clang::X86::BI__builtin_ia32_lzcnt_u16:
case clang::X86::BI__builtin_ia32_lzcnt_u32:
case clang::X86::BI__builtin_ia32_lzcnt_u64:
if (!interp__builtin_ia32_lzcnt(S, OpPC, Frame, F, Call))
return false;
break;
case clang::X86::BI__builtin_ia32_tzcnt_u16:
case clang::X86::BI__builtin_ia32_tzcnt_u32:
case clang::X86::BI__builtin_ia32_tzcnt_u64:
if (!interp__builtin_ia32_tzcnt(S, OpPC, Frame, F, Call))
return false;
break;
case clang::X86::BI__builtin_ia32_pdep_si:
case clang::X86::BI__builtin_ia32_pdep_di:
if (!interp__builtin_ia32_pdep(S, OpPC, Frame, F, Call))
return false;
break;
case clang::X86::BI__builtin_ia32_pext_si:
case clang::X86::BI__builtin_ia32_pext_di:
if (!interp__builtin_ia32_pext(S, OpPC, Frame, F, Call))
return false;
break;
case clang::X86::BI__builtin_ia32_addcarryx_u32:
case clang::X86::BI__builtin_ia32_addcarryx_u64:
case clang::X86::BI__builtin_ia32_subborrow_u32:
case clang::X86::BI__builtin_ia32_subborrow_u64:
if (!interp__builtin_ia32_addcarry_subborrow(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_os_log_format_buffer_size:
if (!interp__builtin_os_log_format_buffer_size(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_ptrauth_string_discriminator:
if (!interp__builtin_ptrauth_string_discriminator(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__noop:
pushInteger(S, 0, Call->getType());
break;
case Builtin::BI__builtin_operator_new:
if (!interp__builtin_operator_new(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_operator_delete:
if (!interp__builtin_operator_delete(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__arithmetic_fence:
if (!interp__builtin_arithmetic_fence(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_reduce_add:
case Builtin::BI__builtin_reduce_mul:
case Builtin::BI__builtin_reduce_and:
case Builtin::BI__builtin_reduce_or:
case Builtin::BI__builtin_reduce_xor:
if (!interp__builtin_vector_reduce(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_elementwise_popcount:
if (!interp__builtin_elementwise_popcount(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_memcpy:
case Builtin::BImemcpy:
case Builtin::BI__builtin_wmemcpy:
case Builtin::BIwmemcpy:
case Builtin::BI__builtin_memmove:
case Builtin::BImemmove:
case Builtin::BI__builtin_wmemmove:
case Builtin::BIwmemmove:
if (!interp__builtin_memcpy(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_memcmp:
case Builtin::BImemcmp:
case Builtin::BI__builtin_bcmp:
case Builtin::BIbcmp:
case Builtin::BI__builtin_wmemcmp:
case Builtin::BIwmemcmp:
if (!interp__builtin_memcmp(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BImemchr:
case Builtin::BI__builtin_memchr:
case Builtin::BIstrchr:
case Builtin::BI__builtin_strchr:
case Builtin::BIwmemchr:
case Builtin::BI__builtin_wmemchr:
case Builtin::BIwcschr:
case Builtin::BI__builtin_wcschr:
case Builtin::BI__builtin_char_memchr:
if (!interp__builtin_memchr(S, OpPC, Frame, F, Call))
return false;
break;
default:
S.FFDiag(S.Current->getLocation(OpPC),
diag::note_invalid_subexpr_in_const_expr)
<< S.Current->getRange(OpPC);
return false;
}
return retPrimValue(S, OpPC, ReturnT);
}
bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
llvm::ArrayRef<int64_t> ArrayIndices,
int64_t &IntResult) {
CharUnits Result;
unsigned N = E->getNumComponents();
assert(N > 0);
unsigned ArrayIndex = 0;
QualType CurrentType = E->getTypeSourceInfo()->getType();
for (unsigned I = 0; I != N; ++I) {
const OffsetOfNode &Node = E->getComponent(I);
switch (Node.getKind()) {
case OffsetOfNode::Field: {
const FieldDecl *MemberDecl = Node.getField();
const RecordType *RT = CurrentType->getAs<RecordType>();
if (!RT)
return false;
const RecordDecl *RD = RT->getDecl();
if (RD->isInvalidDecl())
return false;
const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD);
unsigned FieldIndex = MemberDecl->getFieldIndex();
assert(FieldIndex < RL.getFieldCount() && "offsetof field in wrong type");
Result +=
S.getASTContext().toCharUnitsFromBits(RL.getFieldOffset(FieldIndex));
CurrentType = MemberDecl->getType().getNonReferenceType();
break;
}
case OffsetOfNode::Array: {
// When generating bytecode, we put all the index expressions as Sint64 on
// the stack.
int64_t Index = ArrayIndices[ArrayIndex];
const ArrayType *AT = S.getASTContext().getAsArrayType(CurrentType);
if (!AT)
return false;
CurrentType = AT->getElementType();
CharUnits ElementSize = S.getASTContext().getTypeSizeInChars(CurrentType);
Result += Index * ElementSize;
++ArrayIndex;
break;
}
case OffsetOfNode::Base: {
const CXXBaseSpecifier *BaseSpec = Node.getBase();
if (BaseSpec->isVirtual())
return false;
// Find the layout of the class whose base we are looking into.
const RecordType *RT = CurrentType->getAs<RecordType>();
if (!RT)
return false;
const RecordDecl *RD = RT->getDecl();
if (RD->isInvalidDecl())
return false;
const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD);
// Find the base class itself.
CurrentType = BaseSpec->getType();
const RecordType *BaseRT = CurrentType->getAs<RecordType>();
if (!BaseRT)
return false;
// Add the offset to the base.
Result += RL.getBaseClassOffset(cast<CXXRecordDecl>(BaseRT->getDecl()));
break;
}
case OffsetOfNode::Identifier:
llvm_unreachable("Dependent OffsetOfExpr?");
}
}
IntResult = Result.getQuantity();
return true;
}
bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
const Pointer &Ptr, const APSInt &IntValue) {
const Record *R = Ptr.getRecord();
assert(R);
assert(R->getNumFields() == 1);
unsigned FieldOffset = R->getField(0u)->Offset;
const Pointer &FieldPtr = Ptr.atField(FieldOffset);
PrimType FieldT = *S.getContext().classify(FieldPtr.getType());
INT_TYPE_SWITCH(FieldT,
FieldPtr.deref<T>() = T::from(IntValue.getSExtValue()));
FieldPtr.initialize();
return true;
}
static void zeroAll(Pointer &Dest) {
const Descriptor *Desc = Dest.getFieldDesc();
if (Desc->isPrimitive()) {
TYPE_SWITCH(Desc->getPrimType(), {
Dest.deref<T>().~T();
new (&Dest.deref<T>()) T();
});
return;
}
if (Desc->isRecord()) {
const Record *R = Desc->ElemRecord;
for (const Record::Field &F : R->fields()) {
Pointer FieldPtr = Dest.atField(F.Offset);
zeroAll(FieldPtr);
}
return;
}
if (Desc->isPrimitiveArray()) {
for (unsigned I = 0, N = Desc->getNumElems(); I != N; ++I) {
TYPE_SWITCH(Desc->getPrimType(), {
Dest.deref<T>().~T();
new (&Dest.deref<T>()) T();
});
}
return;
}
if (Desc->isCompositeArray()) {
for (unsigned I = 0, N = Desc->getNumElems(); I != N; ++I) {
Pointer ElemPtr = Dest.atIndex(I).narrow();
zeroAll(ElemPtr);
}
return;
}
}
static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
Pointer &Dest, bool Activate);
static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
Pointer &Dest, bool Activate = false) {
[[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
const Descriptor *DestDesc = Dest.getFieldDesc();
auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
Pointer DestField = Dest.atField(F.Offset);
if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
TYPE_SWITCH(*FT, {
DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
if (Src.atField(F.Offset).isInitialized())
DestField.initialize();
if (Activate)
DestField.activate();
});
return true;
}
// Composite field.
return copyComposite(S, OpPC, Src.atField(F.Offset), DestField, Activate);
};
assert(SrcDesc->isRecord());
assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
const Record *R = DestDesc->ElemRecord;
for (const Record::Field &F : R->fields()) {
if (R->isUnion()) {
// For unions, only copy the active field. Zero all others.
const Pointer &SrcField = Src.atField(F.Offset);
if (SrcField.isActive()) {
if (!copyField(F, /*Activate=*/true))
return false;
} else {
Pointer DestField = Dest.atField(F.Offset);
zeroAll(DestField);
}
} else {
if (!copyField(F, Activate))
return false;
}
}
for (const Record::Base &B : R->bases()) {
Pointer DestBase = Dest.atField(B.Offset);
if (!copyRecord(S, OpPC, Src.atField(B.Offset), DestBase, Activate))
return false;
}
Dest.initialize();
return true;
}
static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
Pointer &Dest, bool Activate = false) {
assert(Src.isLive() && Dest.isLive());
[[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
const Descriptor *DestDesc = Dest.getFieldDesc();
assert(!DestDesc->isPrimitive() && !SrcDesc->isPrimitive());
if (DestDesc->isPrimitiveArray()) {
assert(SrcDesc->isPrimitiveArray());
assert(SrcDesc->getNumElems() == DestDesc->getNumElems());
PrimType ET = DestDesc->getPrimType();
for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) {
Pointer DestElem = Dest.atIndex(I);
TYPE_SWITCH(ET, {
DestElem.deref<T>() = Src.atIndex(I).deref<T>();
DestElem.initialize();
});
}
return true;
}
if (DestDesc->isCompositeArray()) {
assert(SrcDesc->isCompositeArray());
assert(SrcDesc->getNumElems() == DestDesc->getNumElems());
for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) {
const Pointer &SrcElem = Src.atIndex(I).narrow();
Pointer DestElem = Dest.atIndex(I).narrow();
if (!copyComposite(S, OpPC, SrcElem, DestElem, Activate))
return false;
}
return true;
}
if (DestDesc->isRecord())
return copyRecord(S, OpPC, Src, Dest, Activate);
return Invalid(S, OpPC);
}
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
return copyComposite(S, OpPC, Src, Dest);
}
} // namespace interp
} // namespace clang