Skip to content

Commit 2b66f83

Browse files
Lancernsmeenai
authored andcommitted
[CIR][CIRGen] add CIRGen support for assume builtins (llvm#841)
This PR 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 0bcfd37 commit 2b66f83

File tree

6 files changed

+244
-0
lines changed

6 files changed

+244
-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
@@ -3896,6 +3896,86 @@ def FMinOp : BinaryFPToFPBuiltinOp<"fmin", "MinNumOp">;
38963896
def FModOp : BinaryFPToFPBuiltinOp<"fmod", "FRemOp">;
38973897
def PowOp : BinaryFPToFPBuiltinOp<"pow", "PowOp">;
38983898

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

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: }
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: cir-opt --canonicalize -o %t.cir %s
2+
// RUN: FileCheck --input-file %t.cir %s
3+
// RUN: cir-opt -cir-simplify -o %t.cir %s
4+
// RUN: FileCheck --input-file %t.cir %s
5+
6+
!s32i = !cir.int<s, 32>
7+
module {
8+
// Make sure canonicalizers don't erase assume builtins.
9+
10+
cir.func @assume(%arg0: !s32i) {
11+
%0 = cir.const #cir.int<0> : !s32i
12+
%1 = cir.cmp(gt, %arg0, %0) : !s32i, !cir.bool
13+
cir.assume %1 : !cir.bool
14+
cir.return
15+
}
16+
// CHECK: cir.func @assume(%arg0: !s32i) {
17+
// CHECK-NEXT: %0 = cir.const #cir.int<0> : !s32i
18+
// CHECK-NEXT: %1 = cir.cmp(gt, %arg0, %0) : !s32i, !cir.bool
19+
// CHECK-NEXT: cir.assume %1 : !cir.bool
20+
// CHECK-NEXT: cir.return
21+
// CHECK-NEXT: }
22+
23+
cir.func @assume_aligned(%arg0: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
24+
%0 = cir.assume.aligned %arg0 : !cir.ptr<!s32i>[alignment 8]
25+
cir.return %0 : !cir.ptr<!s32i>
26+
}
27+
// CHECK: cir.func @assume_aligned(%arg0: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> {
28+
// CHECK-NEXT: %0 = cir.assume.aligned %arg0 : !cir.ptr<!s32i>[alignment 8]
29+
// CHECK-NEXT: cir.return %0 : !cir.ptr<!s32i>
30+
// CHECK-NEXT: }
31+
32+
cir.func @assume_separate_storage(%arg0: !cir.ptr<!cir.void>, %arg1: !cir.ptr<!cir.void>) {
33+
cir.assume.separate_storage %arg0, %arg1 : !cir.ptr<!cir.void>
34+
cir.return
35+
}
36+
// CHECK: cir.func @assume_separate_storage(%arg0: !cir.ptr<!void>, %arg1: !cir.ptr<!void>) {
37+
// CHECK-NEXT: cir.assume.separate_storage %arg0, %arg1 : !cir.ptr<!void>
38+
// CHECK-NEXT: cir.return
39+
// CHECK-NEXT: }
40+
}

0 commit comments

Comments
 (0)