Skip to content

[CIR] Add support for __builtin_assume_aligned #152152

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
Aug 10, 2025
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
50 changes: 50 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,56 @@ def CIR_AssumeOp : CIR_Op<"assume"> {
}];
}

def CIR_AssumeAlignedOp : CIR_Op<"assume_aligned", [
Pure, AllTypesMatch<["pointer", "result"]>
]> {
let summary = "Tell the optimizer that a pointer is aligned";
let description = [{
The `cir.assume_aligned` operation takes two or three arguments. The first
argument `pointer` gives the pointer value whose alignment is to be assumed,
and the second argument `align` is an integer attribute that gives the
assumed alignment.

The `offset` argument is optional. If given, it represents misalignment
offset. When it's present, this operation tells the optimizer that the
pointer is always misaligned to the alignment by `offset` bytes, a.k.a. the
pointer yielded by `(char *)pointer - offset` is aligned to the specified
alignment. Note that the `offset` argument is an SSA value rather than an
attribute, which means that you could pass a dynamically determined value
as the mialignment offset.

The result of this operation has the same value as the `pointer` argument,
but it additionally carries any alignment information indicated by this
operation.

This operation corresponds to the `__builtin_assume_aligned` builtin
function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a small example?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.


Example:

```mlir
// Assume that %0 is a CIR pointer value of type !cir.ptr<!s32i>
%1 = cir.assume_aligned %0 alignment 16 : !cir.ptr<!s32i>

// With a misalignment offset of 4 bytes:
%2 = cir.const #cir.int<4> : !u64i
%3 = cir.assume_aligned %0 alignment 16 [offset %2 : !u64i] : !cir.ptr<!s32i>
```
}];

let arguments = (ins CIR_PointerType:$pointer,
I64Attr:$alignment,
Optional<CIR_IntType>:$offset);
let results = (outs CIR_PointerType:$result);

let assemblyFormat = [{
$pointer
`alignment` $alignment
(`[` `offset` $offset^ `:` type($offset) `]`)?
`:` qualified(type($pointer)) attr-dict
}];
}

def CIR_AssumeSepStorageOp : CIR_Op<"assume_separate_storage", [
SameTypeOperands
]> {
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return RValue::get(nullptr);
}

case Builtin::BI__builtin_assume_aligned: {
const Expr *ptrExpr = e->getArg(0);
mlir::Value ptrValue = emitScalarExpr(ptrExpr);
mlir::Value offsetValue =
(e->getNumArgs() > 2) ? emitScalarExpr(e->getArg(2)) : nullptr;

std::optional<llvm::APSInt> alignment =
e->getArg(1)->getIntegerConstantExpr(getContext());
assert(alignment.has_value() &&
"the second argument to __builtin_assume_aligned must be an "
"integral constant expression");

mlir::Value result =
emitAlignmentAssumption(ptrValue, ptrExpr, ptrExpr->getExprLoc(),
alignment->getSExtValue(), offsetValue);
return RValue::get(result);
}

case Builtin::BI__builtin_complex: {
mlir::Value real = emitScalarExpr(e->getArg(0));
mlir::Value imag = emitScalarExpr(e->getArg(1));
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,23 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType,
return builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs);
}

mlir::Value CIRGenFunction::emitAlignmentAssumption(
mlir::Value ptrValue, QualType ty, SourceLocation loc,
SourceLocation assumptionLoc, int64_t alignment, mlir::Value offsetValue) {
assert(!cir::MissingFeatures::sanitizers());
return cir::AssumeAlignedOp::create(builder, getLoc(assumptionLoc), ptrValue,
alignment, offsetValue);
}

mlir::Value CIRGenFunction::emitAlignmentAssumption(
mlir::Value ptrValue, const Expr *expr, SourceLocation assumptionLoc,
int64_t alignment, mlir::Value offsetValue) {
QualType ty = expr->getType();
SourceLocation loc = expr->getExprLoc();
return emitAlignmentAssumption(ptrValue, ty, loc, assumptionLoc, alignment,
offsetValue);
}

// TODO(cir): Most of this function can be shared between CIRGen
// and traditional LLVM codegen
void CIRGenFunction::emitVariablyModifiedType(QualType type) {
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,18 @@ class CIRGenFunction : public CIRGenTypeCache {
/// ----------------------
/// CIR emit functions
/// ----------------------
public:
mlir::Value emitAlignmentAssumption(mlir::Value ptrValue, QualType ty,
SourceLocation loc,
SourceLocation assumptionLoc,
int64_t alignment,
mlir::Value offsetValue = nullptr);

mlir::Value emitAlignmentAssumption(mlir::Value ptrValue, const Expr *expr,
SourceLocation assumptionLoc,
int64_t alignment,
mlir::Value offsetValue = nullptr);

private:
void emitAndUpdateRetAlloca(clang::QualType type, mlir::Location loc,
clang::CharUnits alignment);
Expand Down
24 changes: 24 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,29 @@ mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite(
return mlir::success();
}

mlir::LogicalResult CIRToLLVMAssumeAlignedOpLowering::matchAndRewrite(
cir::AssumeAlignedOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
SmallVector<mlir::Value, 3> opBundleArgs{adaptor.getPointer()};

auto alignment = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
adaptor.getAlignmentAttr());
opBundleArgs.push_back(alignment);

if (mlir::Value offset = adaptor.getOffset())
opBundleArgs.push_back(offset);

auto cond = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
rewriter.getI1Type(), 1);
mlir::LLVM::AssumeOp::create(rewriter, op.getLoc(), cond, "align",
opBundleArgs);

// The llvm.assume operation does not have a result, so we need to replace
// all uses of this cir.assume_aligned operation with the input ptr itself.
rewriter.replaceOp(op, adaptor.getPointer());
return mlir::success();
}

mlir::LogicalResult CIRToLLVMAssumeSepStorageOpLowering::matchAndRewrite(
cir::AssumeSepStorageOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
Expand Down Expand Up @@ -2168,6 +2191,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
patterns.add<
// clang-format off
CIRToLLVMAssumeOpLowering,
CIRToLLVMAssumeAlignedOpLowering,
CIRToLLVMAssumeSepStorageOpLowering,
CIRToLLVMBaseClassAddrOpLowering,
CIRToLLVMBinOpLowering,
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ class CIRToLLVMAssumeOpLowering
mlir::ConversionPatternRewriter &) const override;
};

class CIRToLLVMAssumeAlignedOpLowering
: public mlir::OpConversionPattern<cir::AssumeAlignedOp> {
public:
using mlir::OpConversionPattern<cir::AssumeAlignedOp>::OpConversionPattern;

mlir::LogicalResult
matchAndRewrite(cir::AssumeAlignedOp op, OpAdaptor,
mlir::ConversionPatternRewriter &) const override;
};

class CIRToLLVMAssumeSepStorageOpLowering
: public mlir::OpConversionPattern<cir::AssumeSepStorageOp> {
public:
Expand Down
32 changes: 32 additions & 0 deletions clang/test/CIR/CodeGen/builtin_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,38 @@ void assume(bool arg) {
// OGCG: call void @llvm.assume(i1 %{{.+}})
// OGCG: }

void *assume_aligned(void *ptr) {
return __builtin_assume_aligned(ptr, 16);
}

// CIR: @_Z14assume_alignedPv
// CIR: %{{.+}} = cir.assume_aligned %{{.+}} alignment 16 : !cir.ptr<!void>
// CIR: }

// LLVM: @_Z14assume_alignedPv
// LLVM: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16) ]
// LLVM: }

// OGCG: @_Z14assume_alignedPv
// OGCG: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16) ]
// OGCG: }

void *assume_aligned_misalignment(void *ptr, unsigned misalignment) {
return __builtin_assume_aligned(ptr, 16, misalignment);
}

// CIR: @_Z27assume_aligned_misalignmentPvj
// CIR: %{{.+}} = cir.assume_aligned %{{.+}} alignment 16[offset %{{.+}} : !u64i] : !cir.ptr<!void>
// CIR: }

// LLVM: @_Z27assume_aligned_misalignmentPvj
// LLVM: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16, i64 %{{.+}}) ]
// LLVM: }

// OGCG: @_Z27assume_aligned_misalignmentPvj
// OGCG: call void @llvm.assume(i1 true) [ "align"(ptr %{{.+}}, i64 16, i64 %{{.+}}) ]
// OGCG: }

void assume_separate_storage(void *p1, void *p2) {
__builtin_assume_separate_storage(p1, p2);
}
Expand Down