Skip to content

[clang][bytecode] Implement __builtin_assume_aligned #111968

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ static T getParam(const InterpFrame *Frame, unsigned Index) {
return Frame->getParam<T>(Offset);
}

// static APSInt getAPSIntParam(InterpStack &Stk, size_t Offset = 0) {
static APSInt getAPSIntParam(const InterpFrame *Frame, unsigned Index) {
APSInt R;
unsigned Offset = Frame->getFunction()->getParamOffset(Index);
Expand Down Expand Up @@ -1162,6 +1161,71 @@ static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC,
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,
Expand Down Expand Up @@ -1905,6 +1969,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
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:
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/ExprConstShared.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
#ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
#define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H

#include "clang/Basic/TypeTraits.h"

namespace llvm {
class APFloat;
}
namespace clang {
class QualType;
class LangOptions;
class ASTContext;
class CharUnits;
class Expr;
} // namespace clang
using namespace clang;
/// Values returned by __builtin_classify_type, chosen to match the values
Expand Down Expand Up @@ -66,4 +71,7 @@ void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C,
llvm::APFloat D, llvm::APFloat &ResR,
llvm::APFloat &ResI);

CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
UnaryExprOrTypeTrait ExprKind);

#endif
35 changes: 17 additions & 18 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9620,7 +9620,7 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
return ExprEvaluatorBaseTy::VisitCastExpr(E);
}

static CharUnits GetAlignOfType(EvalInfo &Info, QualType T,
static CharUnits GetAlignOfType(const ASTContext &Ctx, QualType T,
UnaryExprOrTypeTrait ExprKind) {
// C++ [expr.alignof]p3:
// When alignof is applied to a reference type, the result is the
Expand All @@ -9631,23 +9631,22 @@ static CharUnits GetAlignOfType(EvalInfo &Info, QualType T,
return CharUnits::One();

const bool AlignOfReturnsPreferred =
Info.Ctx.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver7;
Ctx.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver7;

// __alignof is defined to return the preferred alignment.
// Before 8, clang returned the preferred alignment for alignof and _Alignof
// as well.
if (ExprKind == UETT_PreferredAlignOf || AlignOfReturnsPreferred)
return Info.Ctx.toCharUnitsFromBits(
Info.Ctx.getPreferredTypeAlign(T.getTypePtr()));
return Ctx.toCharUnitsFromBits(Ctx.getPreferredTypeAlign(T.getTypePtr()));
// alignof and _Alignof are defined to return the ABI alignment.
else if (ExprKind == UETT_AlignOf)
return Info.Ctx.getTypeAlignInChars(T.getTypePtr());
return Ctx.getTypeAlignInChars(T.getTypePtr());
else
llvm_unreachable("GetAlignOfType on a non-alignment ExprKind");
}

static CharUnits GetAlignOfExpr(EvalInfo &Info, const Expr *E,
UnaryExprOrTypeTrait ExprKind) {
CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
UnaryExprOrTypeTrait ExprKind) {
E = E->IgnoreParens();

// The kinds of expressions that we have special-case logic here for
Expand All @@ -9657,22 +9656,22 @@ static CharUnits GetAlignOfExpr(EvalInfo &Info, const Expr *E,
// alignof decl is always accepted, even if it doesn't make sense: we default
// to 1 in those cases.
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return Info.Ctx.getDeclAlign(DRE->getDecl(),
/*RefAsPointee*/true);
return Ctx.getDeclAlign(DRE->getDecl(),
/*RefAsPointee*/ true);

if (const MemberExpr *ME = dyn_cast<MemberExpr>(E))
return Info.Ctx.getDeclAlign(ME->getMemberDecl(),
/*RefAsPointee*/true);
return Ctx.getDeclAlign(ME->getMemberDecl(),
/*RefAsPointee*/ true);

return GetAlignOfType(Info, E->getType(), ExprKind);
return GetAlignOfType(Ctx, E->getType(), ExprKind);
}

static CharUnits getBaseAlignment(EvalInfo &Info, const LValue &Value) {
if (const auto *VD = Value.Base.dyn_cast<const ValueDecl *>())
return Info.Ctx.getDeclAlign(VD);
if (const auto *E = Value.Base.dyn_cast<const Expr *>())
return GetAlignOfExpr(Info, E, UETT_AlignOf);
return GetAlignOfType(Info, Value.Base.getTypeInfoType(), UETT_AlignOf);
return GetAlignOfExpr(Info.Ctx, E, UETT_AlignOf);
return GetAlignOfType(Info.Ctx, Value.Base.getTypeInfoType(), UETT_AlignOf);
}

/// Evaluate the value of the alignment argument to __builtin_align_{up,down},
Expand Down Expand Up @@ -14478,11 +14477,11 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
case UETT_PreferredAlignOf:
case UETT_AlignOf: {
if (E->isArgumentType())
return Success(GetAlignOfType(Info, E->getArgumentType(), E->getKind()),
E);
return Success(
GetAlignOfType(Info.Ctx, E->getArgumentType(), E->getKind()), E);
else
return Success(GetAlignOfExpr(Info, E->getArgumentExpr(), E->getKind()),
E);
return Success(
GetAlignOfExpr(Info.Ctx, E->getArgumentExpr(), E->getKind()), E);
}

case UETT_PtrAuthTypeDiscriminator: {
Expand Down
2 changes: 2 additions & 0 deletions clang/test/Sema/builtin-assume-aligned.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -DSIZE_T_64 -fsyntax-only -Wno-strict-prototypes -triple x86_64-linux -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -triple i386-freebsd -verify %s
// RUN: %clang_cc1 -DSIZE_T_64 -fsyntax-only -Wno-strict-prototypes -triple x86_64-linux -verify %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -triple i386-freebsd -verify %s -fexperimental-new-constant-interpreter

// __builtin_assume_aligned's second parameter is size_t, which may be 32 bits,
// so test differently when size_t is 32 bits and when it is 64 bits.
Expand Down
1 change: 1 addition & 0 deletions clang/test/SemaCXX/builtin-assume-aligned.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -triple x86_64-linux-gnu %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -triple x86_64-linux-gnu %s -fexperimental-new-constant-interpreter

int n;
constexpr int *p = 0;
Expand Down
Loading