Skip to content
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
37 changes: 37 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -4044,6 +4044,43 @@ def CIR_ExpectOp : CIR_Op<"expect", [
}];
}

//===----------------------------------------------------------------------===//
// PtrDiffOp
//===----------------------------------------------------------------------===//

def CIR_PtrDiffOp : CIR_Op<"ptr_diff", [Pure, SameTypeOperands]> {
let summary = "Pointer subtraction arithmetic";
let description = [{
The cir.ptr_diff operation computes the difference between two pointers that
have the same element type.

The result reflects the ABI-defined size of the pointed-to type. For example,
subtracting two !cir.ptr<!u64i> values may yield 1, representing an 8-byte
difference. In contrast, for pointers to void or function types, a result of
8 corresponds to an 8-byte difference.

For pointers to types whose size are not aligned with the target data
layout, the size is generally rounded to the next power of 2 bits. For
example, subtracting two !cir.ptr<!s24i> values for the _BitInt(24) type may
yield 1, representing a 4-byte difference (as opposed to a 3-byte
difference).

Example:

```mlir
%7 = cir.ptr_diff %0, %1 : !cir.ptr<!u64i> -> !u64i
```
}];

let arguments = (ins CIR_PointerType:$lhs, CIR_PointerType:$rhs);
let results = (outs CIR_AnyFundamentalIntType:$result);

let assemblyFormat = [{
$lhs `,` $rhs `:` qualified(type($lhs)) `->` qualified(type($result))
attr-dict
}];
}

//===----------------------------------------------------------------------===//
// Floating Point Ops
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ struct MissingFeatures {
static bool invokeOp() { return false; }
static bool labelOp() { return false; }
static bool ptrDiffOp() { return false; }
static bool llvmLoweringPtrDiffConsidersPointee() { return false; }
static bool ptrStrideOp() { return false; }
static bool switchOp() { return false; }
static bool throwOp() { return false; }
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1734,9 +1734,9 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo &ops) {
// LLVM we shall take VLA's, division by element size, etc.
//
// See more in `EmitSub` in CGExprScalar.cpp.
assert(!cir::MissingFeatures::ptrDiffOp());
cgf.cgm.errorNYI("ptrdiff");
return {};
assert(!cir::MissingFeatures::llvmLoweringPtrDiffConsidersPointee());
return cir::PtrDiffOp::create(builder, cgf.getLoc(ops.loc), cgf.PtrDiffTy,
ops.lhs, ops.rhs);
}

mlir::Value ScalarExprEmitter::emitShl(const BinOpInfo &ops) {
Expand Down
48 changes: 48 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,54 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
return mlir::success();
}

static uint64_t getTypeSize(mlir::Type type, mlir::Operation &op) {
mlir::DataLayout layout(op.getParentOfType<mlir::ModuleOp>());
// For LLVM purposes we treat void as u8.
if (isa<cir::VoidType>(type))
type = cir::IntType::get(type.getContext(), 8, /*isSigned=*/false);
return llvm::divideCeil(layout.getTypeSizeInBits(type), 8);
}

mlir::LogicalResult CIRToLLVMPtrDiffOpLowering::matchAndRewrite(
cir::PtrDiffOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
auto dstTy = mlir::cast<cir::IntType>(op.getType());
mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);

auto lhs = rewriter.create<mlir::LLVM::PtrToIntOp>(op.getLoc(), llvmDstTy,
adaptor.getLhs());
auto rhs = rewriter.create<mlir::LLVM::PtrToIntOp>(op.getLoc(), llvmDstTy,
adaptor.getRhs());

auto diff =
rewriter.create<mlir::LLVM::SubOp>(op.getLoc(), llvmDstTy, lhs, rhs);

cir::PointerType ptrTy = op.getLhs().getType();
assert(!cir::MissingFeatures::llvmLoweringPtrDiffConsidersPointee());
uint64_t typeSize = getTypeSize(ptrTy.getPointee(), *op);

// Avoid silly division by 1.
mlir::Value resultVal = diff.getResult();
if (typeSize != 1) {
auto typeSizeVal = rewriter.create<mlir::LLVM::ConstantOp>(
op.getLoc(), llvmDstTy, typeSize);

if (dstTy.isUnsigned()) {
auto uDiv =
rewriter.create<mlir::LLVM::UDivOp>(op.getLoc(), diff, typeSizeVal);
uDiv.setIsExact(true);
resultVal = uDiv.getResult();
} else {
auto sDiv =
rewriter.create<mlir::LLVM::SDivOp>(op.getLoc(), diff, typeSizeVal);
sDiv.setIsExact(true);
resultVal = sDiv.getResult();
}
}
rewriter.replaceOp(op, resultVal);
return mlir::success();
}

mlir::LogicalResult CIRToLLVMExpectOpLowering::matchAndRewrite(
cir::ExpectOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
Expand Down
51 changes: 51 additions & 0 deletions clang/test/CIR/CodeGen/ptrdiff.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s

int addrcmp(const void* a, const void* b) {
// CIR-LABEL: addrcmp
// CIR: %[[R:.*]] = cir.ptr_diff
// CIR: cir.cast integral %[[R]] : !s64i -> !s32i

// LLVM-LABEL: define dso_local i32 @addrcmp(
// LLVM: %[[PTR_A:.*]] = ptrtoint ptr {{.*}} to i64
// LLVM: %[[PTR_B:.*]] = ptrtoint ptr {{.*}} to i64
// LLVM: %[[SUB:.*]] = sub i64 %[[PTR_A]], %[[PTR_B]]
// LLVM-NOT: sdiv
// LLVM: trunc i64 %[[SUB]] to i32

// OGCG-LABEL: define dso_local i32 @addrcmp(
// OGCG: %[[PTR_A:.*]] = ptrtoint ptr {{.*}} to i64
// OGCG: %[[PTR_B:.*]] = ptrtoint ptr {{.*}} to i64
// OGCG: %[[SUB:.*]] = sub i64 %[[PTR_A]], %[[PTR_B]]
// OGCG-NOT: sdiv
// OGCG: trunc i64 %[[SUB]] to i32
return *(const void**)a - *(const void**)b;
}

unsigned long long test_ptr_diff(int *a, int* b) {
// CIR-LABEL: test_ptr_diff
// CIR: %[[D:.*]] = cir.ptr_diff {{.*}} : !cir.ptr<!s32i> -> !s64i
// CIR: %[[U:.*]] = cir.cast integral %[[D]] : !s64i -> !u64i
// CIR: cir.return {{.*}} : !u64i

// LLVM-LABEL: define dso_local i64 @test_ptr_diff(
// LLVM: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
// LLVM: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
// LLVM: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
// LLVM: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 4
// LLVM: store i64 %[[Q]], ptr %[[RETADDR:.*]], align
// LLVM: %[[RETLOAD:.*]] = load i64, ptr %[[RETADDR]], align
// LLVM: ret i64 %[[RETLOAD]]

// OGCG-LABEL: define dso_local i64 @test_ptr_diff(
// OGCG: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
// OGCG: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
// OGCG: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
// OGCG: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 4
// OGCG: ret i64 %[[Q]]
return a - b;
}
33 changes: 33 additions & 0 deletions clang/test/CIR/CodeGen/ptrdiff.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG

typedef unsigned long size_type;

size_type size(unsigned long *_start, unsigned long *_finish) {
// CIR-LABEL: cir.func dso_local @_Z4sizePmS_
// CIR: %[[D:.*]] = cir.ptr_diff {{.*}} : !cir.ptr<!u64i> -> !s64i
// CIR: %[[U:.*]] = cir.cast integral %[[D]] : !s64i -> !u64i
// CIR: cir.return {{.*}} : !u64i

// LLVM-LABEL: define dso_local {{.*}}i64 @_Z4sizePmS_(
// LLVM: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
// LLVM: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
// LLVM: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
// LLVM: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 8
// LLVM: store i64 %[[Q]], ptr %[[RETADDR:.*]], align
// LLVM: %[[RET:.*]] = load i64, ptr %[[RETADDR]], align
// LLVM: ret i64 %[[RET]]

// OGCG-LABEL: define dso_local {{.*}}i64 @_Z4sizePmS_(
// OGCG: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
// OGCG: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
// OGCG: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
// OGCG: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 8
// OGCG: ret i64 %[[Q]]

return static_cast<size_type>(_finish - _start);
}