diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 4eb86f8a1b82..fda44edf4f20 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3882,6 +3882,86 @@ def FMinOp : BinaryFPToFPBuiltinOp<"fmin", "MinNumOp">; def FModOp : BinaryFPToFPBuiltinOp<"fmod", "FRemOp">; def PowOp : BinaryFPToFPBuiltinOp<"pow", "PowOp">; +//===----------------------------------------------------------------------===// +// Assume Operations +//===----------------------------------------------------------------------===// + +def AssumeOp : CIR_Op<"assume"> { + let summary = "Tell the optimizer that a boolean value is true"; + let description = [{ + The `cir.assume` operation takes a single boolean prediate as its only + argument and does not have any results. The operation tells the optimizer + that the predicate's value is true. + + This operation corresponds to the `__assume` and the `__builtin_assume` + builtin function. + }]; + + let arguments = (ins CIR_BoolType:$predicate); + let results = (outs); + + let assemblyFormat = [{ + $predicate `:` type($predicate) attr-dict + }]; +} + +def 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. + + When the 3rd argument `offset` is absent, this operation tells the optimizer + that the pointer given by the `pointer` argument is aligned to the alignment + given by the `align` argument. + + When the `offset` argument is given, it represents an offset from the + alignment. This operation then tells the optimizer that the pointer given by + the `pointer` argument is always misaligned by the alignment given by the + `align` argument by `offset` bytes, a.k.a. the pointer yielded by + `(char *)pointer - offset` is aligned to the specified alignment. + + The `align` argument is a constant integer represented as an integer + attribute instead of an SSA value. It must be a positive integer. + + The result of this operation has the same value as the `pointer` argument, + but the optimizer has additional knowledge about its alignment. + + This operation corresponds to the `__builtin_assume_aligned` builtin + function. + }]; + + let arguments = (ins CIR_PointerType:$pointer, + I64Attr:$alignment, + Optional:$offset); + let results = (outs CIR_PointerType:$result); + + let assemblyFormat = [{ + $pointer `:` qualified(type($pointer)) + `[` `alignment` $alignment (`,` `offset` $offset^ `:` type($offset))? `]` + attr-dict + }]; +} + +def AssumeSepStorageOp : CIR_Op<"assume.separate_storage", [SameTypeOperands]> { + let summary = + "Tell the optimizer that two pointers point to different allocations"; + let description = [{ + The `cir.assume.separate_storage` operation takes two pointers as arguments, + and the operation tells the optimizer that these two pointers point to + different allocations. + + This operation corresponds to the `__builtin_assume_separate_storage` + builtin function. + }]; + + let arguments = (ins VoidPtr:$ptr1, VoidPtr:$ptr2); + + let assemblyFormat = [{ + $ptr1 `,` $ptr2 `:` qualified(type($ptr1)) attr-dict + }]; +} + //===----------------------------------------------------------------------===// // Branch Probability Operations //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index c24888362865..ab4525d38b55 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -13,6 +13,7 @@ #include "CIRGenCXXABI.h" #include "CIRGenCall.h" +#include "CIRGenCstEmitter.h" #include "CIRGenFunction.h" #include "CIRGenModule.h" #include "TargetInfo.h" @@ -823,6 +824,44 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(buildScalarExpr(E->getArg(0))); } + case Builtin::BI__builtin_assume_aligned: { + const Expr *ptr = E->getArg(0); + mlir::Value ptrValue = buildScalarExpr(ptr); + mlir::Value offsetValue = + (E->getNumArgs() > 2) ? buildScalarExpr(E->getArg(2)) : nullptr; + + mlir::Attribute alignmentAttr = ConstantEmitter(*this).emitAbstract( + E->getArg(1), E->getArg(1)->getType()); + std::int64_t alignment = cast(alignmentAttr).getSInt(); + + ptrValue = buildAlignmentAssumption(ptrValue, ptr, ptr->getExprLoc(), + builder.getI64IntegerAttr(alignment), + offsetValue); + return RValue::get(ptrValue); + } + + case Builtin::BI__assume: + case Builtin::BI__builtin_assume: { + if (E->getArg(0)->HasSideEffects(getContext())) + return RValue::get(nullptr); + + mlir::Value argValue = buildScalarExpr(E->getArg(0)); + builder.create(getLoc(E->getExprLoc()), argValue); + return RValue::get(nullptr); + } + + case Builtin::BI__builtin_assume_separate_storage: { + const Expr *arg0 = E->getArg(0); + const Expr *arg1 = E->getArg(1); + + mlir::Value value0 = buildScalarExpr(arg0); + mlir::Value value1 = buildScalarExpr(arg1); + + builder.create(getLoc(E->getExprLoc()), + value0, value1); + return RValue::get(nullptr); + } + case Builtin::BI__builtin_prefetch: { auto evaluateOperandAsInt = [&](const Expr *Arg) { Expr::EvalResult Res; diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 0c7ac712284d..410fcc2a316d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -1854,3 +1854,22 @@ CIRGenFunction::buildArrayLength(const clang::ArrayType *origArrayType, return numElements; } + +mlir::Value CIRGenFunction::buildAlignmentAssumption( + mlir::Value ptrValue, QualType ty, SourceLocation loc, + SourceLocation assumptionLoc, mlir::IntegerAttr alignment, + mlir::Value offsetValue) { + if (SanOpts.has(SanitizerKind::Alignment)) + llvm_unreachable("NYI"); + return builder.create( + getLoc(assumptionLoc), ptrValue, alignment, offsetValue); +} + +mlir::Value CIRGenFunction::buildAlignmentAssumption( + mlir::Value ptrValue, const Expr *expr, SourceLocation assumptionLoc, + mlir::IntegerAttr alignment, mlir::Value offsetValue) { + QualType ty = expr->getType(); + SourceLocation loc = expr->getExprLoc(); + return buildAlignmentAssumption(ptrValue, ty, loc, assumptionLoc, alignment, + offsetValue); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 7703b6cd6ee7..c09860cb85fc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -969,6 +969,17 @@ class CIRGenFunction : public CIRGenTypeCache { ReturnValueSlot ReturnValue, llvm::Triple::ArchType Arch); + mlir::Value buildAlignmentAssumption(mlir::Value ptrValue, QualType ty, + SourceLocation loc, + SourceLocation assumptionLoc, + mlir::IntegerAttr alignment, + mlir::Value offsetValue = nullptr); + + mlir::Value buildAlignmentAssumption(mlir::Value ptrValue, const Expr *expr, + SourceLocation assumptionLoc, + mlir::IntegerAttr alignment, + mlir::Value offsetValue = nullptr); + /// Build a debug stoppoint if we are emitting debug info. void buildStopPoint(const Stmt *S); diff --git a/clang/test/CIR/CodeGen/builtin-assume.cpp b/clang/test/CIR/CodeGen/builtin-assume.cpp new file mode 100644 index 000000000000..da807994f4b1 --- /dev/null +++ b/clang/test/CIR/CodeGen/builtin-assume.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %s -o %t.cir +// RUN: FileCheck %s --check-prefix=CIR --input-file=%t.cir + +int test_assume(int x) { + __builtin_assume(x > 0); + return x; +} + +// CIR: cir.func @_Z11test_assumei +// CIR: %[[#x:]] = cir.load %{{.+}} : !cir.ptr, !s32i +// CIR-NEXT: %[[#zero:]] = cir.const #cir.int<0> : !s32i +// CIR-NEXT: %[[#cond:]] = cir.cmp(gt, %[[#x]], %[[#zero]]) : !s32i, !cir.bool +// CIR-NEXT: cir.assume %[[#cond]] : !cir.bool +// CIR: } + +int test_assume_aligned(int *ptr) { + int *aligned = (int *)__builtin_assume_aligned(ptr, 8); + return *aligned; +} + +// CIR: cir.func @_Z19test_assume_alignedPi +// CIR: %[[#ptr:]] = cir.load %{{.+}} : !cir.ptr>, !cir.ptr +// CIR-NEXT: %[[#aligned:]] = cir.assume.aligned %[[#ptr]] : !cir.ptr[alignment 8] +// CIR-NEXT: cir.store %[[#aligned]], %[[#aligned_slot:]] : !cir.ptr, !cir.ptr> +// CIR-NEXT: %[[#aligned2:]] = cir.load deref %[[#aligned_slot]] : !cir.ptr>, !cir.ptr +// CIR-NEXT: %{{.+}} = cir.load %[[#aligned2]] : !cir.ptr, !s32i +// CIR: } + +int test_assume_aligned_offset(int *ptr) { + int *aligned = (int *)__builtin_assume_aligned(ptr, 8, 4); + return *aligned; +} + +// CIR: cir.func @_Z26test_assume_aligned_offsetPi +// CIR: %[[#ptr:]] = cir.load %{{.+}} : !cir.ptr>, !cir.ptr +// CIR-NEXT: %[[#offset:]] = cir.const #cir.int<4> : !s32i +// CIR-NEXT: %[[#offset2:]] = cir.cast(integral, %[[#offset]] : !s32i), !u64i +// CIR-NEXT: %[[#aligned:]] = cir.assume.aligned %[[#ptr]] : !cir.ptr[alignment 8, offset %[[#offset2]] : !u64i] +// CIR-NEXT: cir.store %[[#aligned]], %[[#aligned_slot:]] : !cir.ptr, !cir.ptr> +// CIR-NEXT: %[[#aligned2:]] = cir.load deref %[[#aligned_slot]] : !cir.ptr>, !cir.ptr +// CIR-NEXT: %{{.+}} = cir.load %[[#aligned2]] : !cir.ptr, !s32i +// CIR: } + +int test_separate_storage(int *p1, int *p2) { + __builtin_assume_separate_storage(p1, p2); + return *p1 + *p2; +} + +// CIR: cir.func @_Z21test_separate_storagePiS_ +// CIR: %[[#p1:]] = cir.load %{{.+}} : !cir.ptr>, !cir.ptr +// CIR-NEXT: %[[#p1_voidptr:]] = cir.cast(bitcast, %[[#p1]] : !cir.ptr), !cir.ptr +// CIR-NEXT: %[[#p2:]] = cir.load %{{.+}} : !cir.ptr>, !cir.ptr +// CIR-NEXT: %[[#p2_voidptr:]] = cir.cast(bitcast, %[[#p2]] : !cir.ptr), !cir.ptr +// CIR-NEXT: cir.assume.separate_storage %[[#p1_voidptr]], %[[#p2_voidptr]] : !cir.ptr +// CIR: } diff --git a/clang/test/CIR/Transforms/builtin-assume.cir b/clang/test/CIR/Transforms/builtin-assume.cir new file mode 100644 index 000000000000..72afb3812e53 --- /dev/null +++ b/clang/test/CIR/Transforms/builtin-assume.cir @@ -0,0 +1,40 @@ +// RUN: cir-opt --canonicalize -o %t.cir %s +// RUN: FileCheck --input-file %t.cir %s +// RUN: cir-opt -cir-simplify -o %t.cir %s +// RUN: FileCheck --input-file %t.cir %s + +!s32i = !cir.int +module { + // Make sure canonicalizers don't erase assume builtins. + + cir.func @assume(%arg0: !s32i) { + %0 = cir.const #cir.int<0> : !s32i + %1 = cir.cmp(gt, %arg0, %0) : !s32i, !cir.bool + cir.assume %1 : !cir.bool + cir.return + } + // CHECK: cir.func @assume(%arg0: !s32i) { + // CHECK-NEXT: %0 = cir.const #cir.int<0> : !s32i + // CHECK-NEXT: %1 = cir.cmp(gt, %arg0, %0) : !s32i, !cir.bool + // CHECK-NEXT: cir.assume %1 : !cir.bool + // CHECK-NEXT: cir.return + // CHECK-NEXT: } + + cir.func @assume_aligned(%arg0: !cir.ptr) -> !cir.ptr { + %0 = cir.assume.aligned %arg0 : !cir.ptr[alignment 8] + cir.return %0 : !cir.ptr + } + // CHECK: cir.func @assume_aligned(%arg0: !cir.ptr) -> !cir.ptr { + // CHECK-NEXT: %0 = cir.assume.aligned %arg0 : !cir.ptr[alignment 8] + // CHECK-NEXT: cir.return %0 : !cir.ptr + // CHECK-NEXT: } + + cir.func @assume_separate_storage(%arg0: !cir.ptr, %arg1: !cir.ptr) { + cir.assume.separate_storage %arg0, %arg1 : !cir.ptr + cir.return + } + // CHECK: cir.func @assume_separate_storage(%arg0: !cir.ptr, %arg1: !cir.ptr) { + // CHECK-NEXT: cir.assume.separate_storage %arg0, %arg1 : !cir.ptr + // CHECK-NEXT: cir.return + // CHECK-NEXT: } +} \ No newline at end of file