Skip to content

Commit 75244a1

Browse files
committed
[clang][Interp] Implement align builtins
__builtin_is_aligned __builtin_is_align_up __builtin_is_align_down
1 parent 711df7b commit 75244a1

File tree

2 files changed

+376
-0
lines changed

2 files changed

+376
-0
lines changed

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,117 @@ static bool interp__builtin_complex(InterpState &S, CodePtr OpPC,
977977
return true;
978978
}
979979

980+
/// __builtin_is_aligned()
981+
/// __builtin_align_up()
982+
/// __builtin_align_down()
983+
/// The first parameter is either an integer or a pointer.
984+
/// The second parameter is the requested alignment as an integer.
985+
static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC,
986+
const InterpFrame *Frame,
987+
const Function *Func,
988+
const CallExpr *Call) {
989+
unsigned BuiltinOp = Func->getBuiltinID();
990+
unsigned CallSize = callArgSize(S, Call);
991+
992+
PrimType AlignmentT = *S.Ctx.classify(Call->getArg(1));
993+
const APSInt &Alignment = peekToAPSInt(S.Stk, AlignmentT);
994+
995+
if (Alignment < 0 || !Alignment.isPowerOf2()) {
996+
S.FFDiag(Call, diag::note_constexpr_invalid_alignment) << Alignment;
997+
return false;
998+
}
999+
unsigned SrcWidth = S.getCtx().getIntWidth(Call->getArg(0)->getType());
1000+
APSInt MaxValue(APInt::getOneBitSet(SrcWidth, SrcWidth - 1));
1001+
if (APSInt::compareValues(Alignment, MaxValue) > 0) {
1002+
S.FFDiag(Call, diag::note_constexpr_alignment_too_big)
1003+
<< MaxValue << Call->getArg(0)->getType() << Alignment;
1004+
return false;
1005+
}
1006+
1007+
// The first parameter is either an integer or a pointer (but not a function
1008+
// pointer).
1009+
PrimType FirstArgT = *S.Ctx.classify(Call->getArg(0));
1010+
1011+
if (isIntegralType(FirstArgT)) {
1012+
const APSInt &Src = peekToAPSInt(S.Stk, FirstArgT, CallSize);
1013+
APSInt Align = Alignment.extOrTrunc(Src.getBitWidth());
1014+
if (BuiltinOp == Builtin::BI__builtin_align_up) {
1015+
APSInt AlignedVal =
1016+
APSInt((Src + (Align - 1)) & ~(Align - 1), Src.isUnsigned());
1017+
pushInteger(S, AlignedVal, Call->getType());
1018+
} else if (BuiltinOp == Builtin::BI__builtin_align_down) {
1019+
APSInt AlignedVal = APSInt(Src & ~(Align - 1), Src.isUnsigned());
1020+
pushInteger(S, AlignedVal, Call->getType());
1021+
} else {
1022+
assert(*S.Ctx.classify(Call->getType()) == PT_Bool);
1023+
S.Stk.push<Boolean>((Src & (Align - 1)) == 0);
1024+
}
1025+
return true;
1026+
}
1027+
1028+
assert(FirstArgT == PT_Ptr);
1029+
const Pointer &Ptr = S.Stk.peek<Pointer>(CallSize);
1030+
1031+
unsigned PtrOffset = Ptr.getByteOffset();
1032+
PtrOffset = Ptr.getIndex();
1033+
CharUnits BaseAlignment =
1034+
S.getCtx().getDeclAlign(Ptr.getDeclDesc()->asValueDecl());
1035+
CharUnits PtrAlign =
1036+
BaseAlignment.alignmentAtOffset(CharUnits::fromQuantity(PtrOffset));
1037+
1038+
if (BuiltinOp == Builtin::BI__builtin_is_aligned) {
1039+
if (PtrAlign.getQuantity() >= Alignment) {
1040+
S.Stk.push<Boolean>(true);
1041+
return true;
1042+
}
1043+
// If the alignment is not known to be sufficient, some cases could still
1044+
// be aligned at run time. However, if the requested alignment is less or
1045+
// equal to the base alignment and the offset is not aligned, we know that
1046+
// the run-time value can never be aligned.
1047+
if (BaseAlignment.getQuantity() >= Alignment &&
1048+
PtrAlign.getQuantity() < Alignment) {
1049+
S.Stk.push<Boolean>(false);
1050+
return true;
1051+
}
1052+
1053+
S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_compute)
1054+
<< Alignment;
1055+
return false;
1056+
}
1057+
1058+
assert(BuiltinOp == Builtin::BI__builtin_align_down ||
1059+
BuiltinOp == Builtin::BI__builtin_align_up);
1060+
1061+
// For align_up/align_down, we can return the same value if the alignment
1062+
// is known to be greater or equal to the requested value.
1063+
if (PtrAlign.getQuantity() >= Alignment) {
1064+
S.Stk.push<Pointer>(Ptr);
1065+
return true;
1066+
}
1067+
1068+
// The alignment could be greater than the minimum at run-time, so we cannot
1069+
// infer much about the resulting pointer value. One case is possible:
1070+
// For `_Alignas(32) char buf[N]; __builtin_align_down(&buf[idx], 32)` we
1071+
// can infer the correct index if the requested alignment is smaller than
1072+
// the base alignment so we can perform the computation on the offset.
1073+
if (BaseAlignment.getQuantity() >= Alignment) {
1074+
assert(Alignment.getBitWidth() <= 64 &&
1075+
"Cannot handle > 64-bit address-space");
1076+
uint64_t Alignment64 = Alignment.getZExtValue();
1077+
CharUnits NewOffset =
1078+
CharUnits::fromQuantity(BuiltinOp == Builtin::BI__builtin_align_down
1079+
? llvm::alignDown(PtrOffset, Alignment64)
1080+
: llvm::alignTo(PtrOffset, Alignment64));
1081+
1082+
S.Stk.push<Pointer>(Ptr.atIndex(NewOffset.getQuantity()));
1083+
return true;
1084+
}
1085+
1086+
// Otherwise, we cannot constant-evaluate the result.
1087+
S.FFDiag(Call->getArg(0), diag::note_constexpr_alignment_adjust) << Alignment;
1088+
return false;
1089+
}
1090+
9801091
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
9811092
const CallExpr *Call) {
9821093
const InterpFrame *Frame = S.Current;
@@ -1291,6 +1402,13 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
12911402
return false;
12921403
break;
12931404

1405+
case Builtin::BI__builtin_is_aligned:
1406+
case Builtin::BI__builtin_align_up:
1407+
case Builtin::BI__builtin_align_down:
1408+
if (!interp__builtin_is_aligned_up_down(S, OpPC, Frame, F, Call))
1409+
return false;
1410+
break;
1411+
12941412
default:
12951413
S.FFDiag(S.Current->getLocation(OpPC),
12961414
diag::note_invalid_subexpr_in_const_expr)

0 commit comments

Comments
 (0)