Skip to content

Commit b2cdd80

Browse files
authored
[CIR] Add support for __builtin_assume_aligned (#152152)
This patch upstreams CIRGen and LLVM lowering support for the `__builtin_assume_aligned` builtin function.
1 parent 733fddb commit b2cdd80

File tree

7 files changed

+163
-0
lines changed

7 files changed

+163
-0
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3186,6 +3186,56 @@ def CIR_AssumeOp : CIR_Op<"assume"> {
31863186
}];
31873187
}
31883188

3189+
def CIR_AssumeAlignedOp : CIR_Op<"assume_aligned", [
3190+
Pure, AllTypesMatch<["pointer", "result"]>
3191+
]> {
3192+
let summary = "Tell the optimizer that a pointer is aligned";
3193+
let description = [{
3194+
The `cir.assume_aligned` operation takes two or three arguments. The first
3195+
argument `pointer` gives the pointer value whose alignment is to be assumed,
3196+
and the second argument `align` is an integer attribute that gives the
3197+
assumed alignment.
3198+
3199+
The `offset` argument is optional. If given, it represents misalignment
3200+
offset. When it's present, this operation tells the optimizer that the
3201+
pointer is always misaligned to the alignment by `offset` bytes, a.k.a. the
3202+
pointer yielded by `(char *)pointer - offset` is aligned to the specified
3203+
alignment. Note that the `offset` argument is an SSA value rather than an
3204+
attribute, which means that you could pass a dynamically determined value
3205+
as the mialignment offset.
3206+
3207+
The result of this operation has the same value as the `pointer` argument,
3208+
but it additionally carries any alignment information indicated by this
3209+
operation.
3210+
3211+
This operation corresponds to the `__builtin_assume_aligned` builtin
3212+
function.
3213+
3214+
Example:
3215+
3216+
```mlir
3217+
// Assume that %0 is a CIR pointer value of type !cir.ptr<!s32i>
3218+
%1 = cir.assume_aligned %0 alignment 16 : !cir.ptr<!s32i>
3219+
3220+
// With a misalignment offset of 4 bytes:
3221+
%2 = cir.const #cir.int<4> : !u64i
3222+
%3 = cir.assume_aligned %0 alignment 16 [offset %2 : !u64i] : !cir.ptr<!s32i>
3223+
```
3224+
}];
3225+
3226+
let arguments = (ins CIR_PointerType:$pointer,
3227+
I64Attr:$alignment,
3228+
Optional<CIR_IntType>:$offset);
3229+
let results = (outs CIR_PointerType:$result);
3230+
3231+
let assemblyFormat = [{
3232+
$pointer
3233+
`alignment` $alignment
3234+
(`[` `offset` $offset^ `:` type($offset) `]`)?
3235+
`:` qualified(type($pointer)) attr-dict
3236+
}];
3237+
}
3238+
31893239
def CIR_AssumeSepStorageOp : CIR_Op<"assume_separate_storage", [
31903240
SameTypeOperands
31913241
]> {

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,24 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
129129
return RValue::get(nullptr);
130130
}
131131

132+
case Builtin::BI__builtin_assume_aligned: {
133+
const Expr *ptrExpr = e->getArg(0);
134+
mlir::Value ptrValue = emitScalarExpr(ptrExpr);
135+
mlir::Value offsetValue =
136+
(e->getNumArgs() > 2) ? emitScalarExpr(e->getArg(2)) : nullptr;
137+
138+
std::optional<llvm::APSInt> alignment =
139+
e->getArg(1)->getIntegerConstantExpr(getContext());
140+
assert(alignment.has_value() &&
141+
"the second argument to __builtin_assume_aligned must be an "
142+
"integral constant expression");
143+
144+
mlir::Value result =
145+
emitAlignmentAssumption(ptrValue, ptrExpr, ptrExpr->getExprLoc(),
146+
alignment->getSExtValue(), offsetValue);
147+
return RValue::get(result);
148+
}
149+
132150
case Builtin::BI__builtin_complex: {
133151
mlir::Value real = emitScalarExpr(e->getArg(0));
134152
mlir::Value imag = emitScalarExpr(e->getArg(1));

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,23 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
933933
return builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
934934
}
935935

936+
mlir::Value CIRGenFunction::emitAlignmentAssumption(
937+
mlir::Value ptrValue, QualType ty, SourceLocation loc,
938+
SourceLocation assumptionLoc, int64_t alignment, mlir::Value offsetValue) {
939+
assert(!cir::MissingFeatures::sanitizers());
940+
return cir::AssumeAlignedOp::create(builder, getLoc(assumptionLoc), ptrValue,
941+
alignment, offsetValue);
942+
}
943+
944+
mlir::Value CIRGenFunction::emitAlignmentAssumption(
945+
mlir::Value ptrValue, const Expr *expr, SourceLocation assumptionLoc,
946+
int64_t alignment, mlir::Value offsetValue) {
947+
QualType ty = expr->getType();
948+
SourceLocation loc = expr->getExprLoc();
949+
return emitAlignmentAssumption(ptrValue, ty, loc, assumptionLoc, alignment,
950+
offsetValue);
951+
}
952+
936953
// TODO(cir): Most of this function can be shared between CIRGen
937954
// and traditional LLVM codegen
938955
void CIRGenFunction::emitVariablyModifiedType(QualType type) {

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,18 @@ class CIRGenFunction : public CIRGenTypeCache {
829829
/// ----------------------
830830
/// CIR emit functions
831831
/// ----------------------
832+
public:
833+
mlir::Value emitAlignmentAssumption(mlir::Value ptrValue, QualType ty,
834+
SourceLocation loc,
835+
SourceLocation assumptionLoc,
836+
int64_t alignment,
837+
mlir::Value offsetValue = nullptr);
838+
839+
mlir::Value emitAlignmentAssumption(mlir::Value ptrValue, const Expr *expr,
840+
SourceLocation assumptionLoc,
841+
int64_t alignment,
842+
mlir::Value offsetValue = nullptr);
843+
832844
private:
833845
void emitAndUpdateRetAlloca(clang::QualType type, mlir::Location loc,
834846
clang::CharUnits alignment);

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,29 @@ mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite(
460460
return mlir::success();
461461
}
462462

463+
mlir::LogicalResult CIRToLLVMAssumeAlignedOpLowering::matchAndRewrite(
464+
cir::AssumeAlignedOp op, OpAdaptor adaptor,
465+
mlir::ConversionPatternRewriter &rewriter) const {
466+
SmallVector<mlir::Value, 3> opBundleArgs{adaptor.getPointer()};
467+
468+
auto alignment = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
469+
adaptor.getAlignmentAttr());
470+
opBundleArgs.push_back(alignment);
471+
472+
if (mlir::Value offset = adaptor.getOffset())
473+
opBundleArgs.push_back(offset);
474+
475+
auto cond = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
476+
rewriter.getI1Type(), 1);
477+
mlir::LLVM::AssumeOp::create(rewriter, op.getLoc(), cond, "align",
478+
opBundleArgs);
479+
480+
// The llvm.assume operation does not have a result, so we need to replace
481+
// all uses of this cir.assume_aligned operation with the input ptr itself.
482+
rewriter.replaceOp(op, adaptor.getPointer());
483+
return mlir::success();
484+
}
485+
463486
mlir::LogicalResult CIRToLLVMAssumeSepStorageOpLowering::matchAndRewrite(
464487
cir::AssumeSepStorageOp op, OpAdaptor adaptor,
465488
mlir::ConversionPatternRewriter &rewriter) const {
@@ -2173,6 +2196,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
21732196
patterns.add<
21742197
// clang-format off
21752198
CIRToLLVMAssumeOpLowering,
2199+
CIRToLLVMAssumeAlignedOpLowering,
21762200
CIRToLLVMAssumeSepStorageOpLowering,
21772201
CIRToLLVMBaseClassAddrOpLowering,
21782202
CIRToLLVMBinOpLowering,

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ class CIRToLLVMAssumeOpLowering
4444
mlir::ConversionPatternRewriter &) const override;
4545
};
4646

47+
class CIRToLLVMAssumeAlignedOpLowering
48+
: public mlir::OpConversionPattern<cir::AssumeAlignedOp> {
49+
public:
50+
using mlir::OpConversionPattern<cir::AssumeAlignedOp>::OpConversionPattern;
51+
52+
mlir::LogicalResult
53+
matchAndRewrite(cir::AssumeAlignedOp op, OpAdaptor,
54+
mlir::ConversionPatternRewriter &) const override;
55+
};
56+
4757
class CIRToLLVMAssumeSepStorageOpLowering
4858
: public mlir::OpConversionPattern<cir::AssumeSepStorageOp> {
4959
public:

clang/test/CIR/CodeGen/builtin_call.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,38 @@ void assume(bool arg) {
111111
// OGCG: call void @llvm.assume(i1 %{{.+}})
112112
// OGCG: }
113113

114+
void *assume_aligned(void *ptr) {
115+
return __builtin_assume_aligned(ptr, 16);
116+
}
117+
118+
// CIR: @_Z14assume_alignedPv
119+
// CIR: %{{.+}} = cir.assume_aligned %{{.+}} alignment 16 : !cir.ptr<!void>
120+
// CIR: }
121+
122+
// LLVM: @_Z14assume_alignedPv
123+
// LLVM: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16) ]
124+
// LLVM: }
125+
126+
// OGCG: @_Z14assume_alignedPv
127+
// OGCG: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16) ]
128+
// OGCG: }
129+
130+
void *assume_aligned_misalignment(void *ptr, unsigned misalignment) {
131+
return __builtin_assume_aligned(ptr, 16, misalignment);
132+
}
133+
134+
// CIR: @_Z27assume_aligned_misalignmentPvj
135+
// CIR: %{{.+}} = cir.assume_aligned %{{.+}} alignment 16[offset %{{.+}} : !u64i] : !cir.ptr<!void>
136+
// CIR: }
137+
138+
// LLVM: @_Z27assume_aligned_misalignmentPvj
139+
// LLVM: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16, i64 %{{.+}}) ]
140+
// LLVM: }
141+
142+
// OGCG: @_Z27assume_aligned_misalignmentPvj
143+
// OGCG: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16, i64 %{{.+}}) ]
144+
// OGCG: }
145+
114146
void assume_separate_storage(void *p1, void *p2) {
115147
__builtin_assume_separate_storage(p1, p2);
116148
}

0 commit comments

Comments
 (0)