Skip to content

Commit c6f4616

Browse files
committed
[CIR][CIRGen] add CIRGen support for assume builtins
This patch adds CIRGen support for the following 3 builtins related to compile- time assumptions: - `__builtin_assume` - `__builtin_assume_aligned` - `__builtin_assume_separate_storage` 3 new operations are invented to represent the three builtins. LLVMIR lowering for these builtins cannot be implemented at this moment due to the lack of operand bundle support in LLVMIR dialect.
1 parent bde154c commit c6f4616

File tree

5 files changed

+204
-0
lines changed

5 files changed

+204
-0
lines changed

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3882,6 +3882,86 @@ def FMinOp : BinaryFPToFPBuiltinOp<"fmin", "MinNumOp">;
38823882
def FModOp : BinaryFPToFPBuiltinOp<"fmod", "FRemOp">;
38833883
def PowOp : BinaryFPToFPBuiltinOp<"pow", "PowOp">;
38843884

3885+
//===----------------------------------------------------------------------===//
3886+
// Assume Operations
3887+
//===----------------------------------------------------------------------===//
3888+
3889+
def AssumeOp : CIR_Op<"assume"> {
3890+
let summary = "Tell the optimizer that a boolean value is true";
3891+
let description = [{
3892+
The `cir.assume` operation takes a single boolean prediate as its only
3893+
argument and does not have any results. The operation tells the optimizer
3894+
that the predicate's value is true.
3895+
3896+
This operation corresponds to the `__assume` and the `__builtin_assume`
3897+
builtin function.
3898+
}];
3899+
3900+
let arguments = (ins CIR_BoolType:$predicate);
3901+
let results = (outs);
3902+
3903+
let assemblyFormat = [{
3904+
$predicate `:` type($predicate) attr-dict
3905+
}];
3906+
}
3907+
3908+
def AssumeAlignedOp
3909+
: CIR_Op<"assume.aligned", [Pure, AllTypesMatch<["pointer", "result"]>]> {
3910+
let summary = "Tell the optimizer that a pointer is aligned";
3911+
let description = [{
3912+
The `cir.assume.aligned` operation takes two or three arguments.
3913+
3914+
When the 3rd argument `offset` is absent, this operation tells the optimizer
3915+
that the pointer given by the `pointer` argument is aligned to the alignment
3916+
given by the `align` argument.
3917+
3918+
When the `offset` argument is given, it represents an offset from the
3919+
alignment. This operation then tells the optimizer that the pointer given by
3920+
the `pointer` argument is always misaligned by the alignment given by the
3921+
`align` argument by `offset` bytes, a.k.a. the pointer yielded by
3922+
`(char *)pointer - offset` is aligned to the specified alignment.
3923+
3924+
The `align` argument is a constant integer represented as an integer
3925+
attribute instead of an SSA value. It must be a positive integer.
3926+
3927+
The result of this operation has the same value as the `pointer` argument,
3928+
but the optimizer has additional knowledge about its alignment.
3929+
3930+
This operation corresponds to the `__builtin_assume_aligned` builtin
3931+
function.
3932+
}];
3933+
3934+
let arguments = (ins CIR_PointerType:$pointer,
3935+
I64Attr:$alignment,
3936+
Optional<CIR_IntType>:$offset);
3937+
let results = (outs CIR_PointerType:$result);
3938+
3939+
let assemblyFormat = [{
3940+
$pointer `:` qualified(type($pointer))
3941+
`[` `alignment` $alignment (`,` `offset` $offset^ `:` type($offset))? `]`
3942+
attr-dict
3943+
}];
3944+
}
3945+
3946+
def AssumeSepStorageOp : CIR_Op<"assume.separate_storage", [SameTypeOperands]> {
3947+
let summary =
3948+
"Tell the optimizer that two pointers point to different allocations";
3949+
let description = [{
3950+
The `cir.assume.separate_storage` operation takes two pointers as arguments,
3951+
and the operation tells the optimizer that these two pointers point to
3952+
different allocations.
3953+
3954+
This operation corresponds to the `__builtin_assume_separate_storage`
3955+
builtin function.
3956+
}];
3957+
3958+
let arguments = (ins VoidPtr:$ptr1, VoidPtr:$ptr2);
3959+
3960+
let assemblyFormat = [{
3961+
$ptr1 `,` $ptr2 `:` qualified(type($ptr1)) attr-dict
3962+
}];
3963+
}
3964+
38853965
//===----------------------------------------------------------------------===//
38863966
// Branch Probability Operations
38873967
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "CIRGenCXXABI.h"
1515
#include "CIRGenCall.h"
16+
#include "CIRGenCstEmitter.h"
1617
#include "CIRGenFunction.h"
1718
#include "CIRGenModule.h"
1819
#include "TargetInfo.h"
@@ -823,6 +824,44 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
823824
return RValue::get(buildScalarExpr(E->getArg(0)));
824825
}
825826

827+
case Builtin::BI__builtin_assume_aligned: {
828+
const Expr *ptr = E->getArg(0);
829+
mlir::Value ptrValue = buildScalarExpr(ptr);
830+
mlir::Value offsetValue =
831+
(E->getNumArgs() > 2) ? buildScalarExpr(E->getArg(2)) : nullptr;
832+
833+
mlir::Attribute alignmentAttr = ConstantEmitter(*this).emitAbstract(
834+
E->getArg(1), E->getArg(1)->getType());
835+
std::int64_t alignment = cast<mlir::cir::IntAttr>(alignmentAttr).getSInt();
836+
837+
ptrValue = buildAlignmentAssumption(ptrValue, ptr, ptr->getExprLoc(),
838+
builder.getI64IntegerAttr(alignment),
839+
offsetValue);
840+
return RValue::get(ptrValue);
841+
}
842+
843+
case Builtin::BI__assume:
844+
case Builtin::BI__builtin_assume: {
845+
if (E->getArg(0)->HasSideEffects(getContext()))
846+
return RValue::get(nullptr);
847+
848+
mlir::Value argValue = buildScalarExpr(E->getArg(0));
849+
builder.create<mlir::cir::AssumeOp>(getLoc(E->getExprLoc()), argValue);
850+
return RValue::get(nullptr);
851+
}
852+
853+
case Builtin::BI__builtin_assume_separate_storage: {
854+
const Expr *arg0 = E->getArg(0);
855+
const Expr *arg1 = E->getArg(1);
856+
857+
mlir::Value value0 = buildScalarExpr(arg0);
858+
mlir::Value value1 = buildScalarExpr(arg1);
859+
860+
builder.create<mlir::cir::AssumeSepStorageOp>(getLoc(E->getExprLoc()),
861+
value0, value1);
862+
return RValue::get(nullptr);
863+
}
864+
826865
case Builtin::BI__builtin_prefetch: {
827866
auto evaluateOperandAsInt = [&](const Expr *Arg) {
828867
Expr::EvalResult Res;

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,3 +1854,22 @@ CIRGenFunction::buildArrayLength(const clang::ArrayType *origArrayType,
18541854

18551855
return numElements;
18561856
}
1857+
1858+
mlir::Value CIRGenFunction::buildAlignmentAssumption(
1859+
mlir::Value ptrValue, QualType ty, SourceLocation loc,
1860+
SourceLocation assumptionLoc, mlir::IntegerAttr alignment,
1861+
mlir::Value offsetValue) {
1862+
if (SanOpts.has(SanitizerKind::Alignment))
1863+
llvm_unreachable("NYI");
1864+
return builder.create<mlir::cir::AssumeAlignedOp>(
1865+
getLoc(assumptionLoc), ptrValue, alignment, offsetValue);
1866+
}
1867+
1868+
mlir::Value CIRGenFunction::buildAlignmentAssumption(
1869+
mlir::Value ptrValue, const Expr *expr, SourceLocation assumptionLoc,
1870+
mlir::IntegerAttr alignment, mlir::Value offsetValue) {
1871+
QualType ty = expr->getType();
1872+
SourceLocation loc = expr->getExprLoc();
1873+
return buildAlignmentAssumption(ptrValue, ty, loc, assumptionLoc, alignment,
1874+
offsetValue);
1875+
}

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,17 @@ class CIRGenFunction : public CIRGenTypeCache {
969969
ReturnValueSlot ReturnValue,
970970
llvm::Triple::ArchType Arch);
971971

972+
mlir::Value buildAlignmentAssumption(mlir::Value ptrValue, QualType ty,
973+
SourceLocation loc,
974+
SourceLocation assumptionLoc,
975+
mlir::IntegerAttr alignment,
976+
mlir::Value offsetValue = nullptr);
977+
978+
mlir::Value buildAlignmentAssumption(mlir::Value ptrValue, const Expr *expr,
979+
SourceLocation assumptionLoc,
980+
mlir::IntegerAttr alignment,
981+
mlir::Value offsetValue = nullptr);
982+
972983
/// Build a debug stoppoint if we are emitting debug info.
973984
void buildStopPoint(const Stmt *S);
974985

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %s -o %t.cir
2+
// RUN: FileCheck %s --check-prefix=CIR --input-file=%t.cir
3+
4+
int test_assume(int x) {
5+
__builtin_assume(x > 0);
6+
return x;
7+
}
8+
9+
// CIR: cir.func @_Z11test_assumei
10+
// CIR: %[[#x:]] = cir.load %{{.+}} : !cir.ptr<!s32i>, !s32i
11+
// CIR-NEXT: %[[#zero:]] = cir.const #cir.int<0> : !s32i
12+
// CIR-NEXT: %[[#cond:]] = cir.cmp(gt, %[[#x]], %[[#zero]]) : !s32i, !cir.bool
13+
// CIR-NEXT: cir.assume %[[#cond]] : !cir.bool
14+
// CIR: }
15+
16+
int test_assume_aligned(int *ptr) {
17+
int *aligned = (int *)__builtin_assume_aligned(ptr, 8);
18+
return *aligned;
19+
}
20+
21+
// CIR: cir.func @_Z19test_assume_alignedPi
22+
// CIR: %[[#ptr:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
23+
// CIR-NEXT: %[[#aligned:]] = cir.assume.aligned %[[#ptr]] : !cir.ptr<!s32i>[alignment 8]
24+
// CIR-NEXT: cir.store %[[#aligned]], %[[#aligned_slot:]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
25+
// CIR-NEXT: %[[#aligned2:]] = cir.load deref %[[#aligned_slot]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
26+
// CIR-NEXT: %{{.+}} = cir.load %[[#aligned2]] : !cir.ptr<!s32i>, !s32i
27+
// CIR: }
28+
29+
int test_assume_aligned_offset(int *ptr) {
30+
int *aligned = (int *)__builtin_assume_aligned(ptr, 8, 4);
31+
return *aligned;
32+
}
33+
34+
// CIR: cir.func @_Z26test_assume_aligned_offsetPi
35+
// CIR: %[[#ptr:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
36+
// CIR-NEXT: %[[#offset:]] = cir.const #cir.int<4> : !s32i
37+
// CIR-NEXT: %[[#offset2:]] = cir.cast(integral, %[[#offset]] : !s32i), !u64i
38+
// CIR-NEXT: %[[#aligned:]] = cir.assume.aligned %[[#ptr]] : !cir.ptr<!s32i>[alignment 8, offset %[[#offset2]] : !u64i]
39+
// CIR-NEXT: cir.store %[[#aligned]], %[[#aligned_slot:]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
40+
// CIR-NEXT: %[[#aligned2:]] = cir.load deref %[[#aligned_slot]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
41+
// CIR-NEXT: %{{.+}} = cir.load %[[#aligned2]] : !cir.ptr<!s32i>, !s32i
42+
// CIR: }
43+
44+
int test_separate_storage(int *p1, int *p2) {
45+
__builtin_assume_separate_storage(p1, p2);
46+
return *p1 + *p2;
47+
}
48+
49+
// CIR: cir.func @_Z21test_separate_storagePiS_
50+
// CIR: %[[#p1:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
51+
// CIR-NEXT: %[[#p1_voidptr:]] = cir.cast(bitcast, %[[#p1]] : !cir.ptr<!s32i>), !cir.ptr<!void>
52+
// CIR-NEXT: %[[#p2:]] = cir.load %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
53+
// CIR-NEXT: %[[#p2_voidptr:]] = cir.cast(bitcast, %[[#p2]] : !cir.ptr<!s32i>), !cir.ptr<!void>
54+
// CIR-NEXT: cir.assume.separate_storage %[[#p1_voidptr]], %[[#p2_voidptr]] : !cir.ptr<!void>
55+
// CIR: }

0 commit comments

Comments
 (0)