Skip to content

Commit f9740bc

Browse files
kimsh02github-actions[bot]
authored andcommitted
Automerge: [CIR] Upstream pointer subtraction handling (#163306)
This upstreams the CIR handling for pointer subtraction, including introducing the cir.ptr_diff operation. Fixes #162360
2 parents 8298587 + 01c0cb9 commit f9740bc

File tree

6 files changed

+173
-3
lines changed

6 files changed

+173
-3
lines changed

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4044,6 +4044,43 @@ def CIR_ExpectOp : CIR_Op<"expect", [
40444044
}];
40454045
}
40464046

4047+
//===----------------------------------------------------------------------===//
4048+
// PtrDiffOp
4049+
//===----------------------------------------------------------------------===//
4050+
4051+
def CIR_PtrDiffOp : CIR_Op<"ptr_diff", [Pure, SameTypeOperands]> {
4052+
let summary = "Pointer subtraction arithmetic";
4053+
let description = [{
4054+
The cir.ptr_diff operation computes the difference between two pointers that
4055+
have the same element type.
4056+
4057+
The result reflects the ABI-defined size of the pointed-to type. For example,
4058+
subtracting two !cir.ptr<!u64i> values may yield 1, representing an 8-byte
4059+
difference. In contrast, for pointers to void or function types, a result of
4060+
8 corresponds to an 8-byte difference.
4061+
4062+
For pointers to types whose size are not aligned with the target data
4063+
layout, the size is generally rounded to the next power of 2 bits. For
4064+
example, subtracting two !cir.ptr<!s24i> values for the _BitInt(24) type may
4065+
yield 1, representing a 4-byte difference (as opposed to a 3-byte
4066+
difference).
4067+
4068+
Example:
4069+
4070+
```mlir
4071+
%7 = cir.ptr_diff %0, %1 : !cir.ptr<!u64i> -> !u64i
4072+
```
4073+
}];
4074+
4075+
let arguments = (ins CIR_PointerType:$lhs, CIR_PointerType:$rhs);
4076+
let results = (outs CIR_AnyFundamentalIntType:$result);
4077+
4078+
let assemblyFormat = [{
4079+
$lhs `,` $rhs `:` qualified(type($lhs)) `->` qualified(type($result))
4080+
attr-dict
4081+
}];
4082+
}
4083+
40474084
//===----------------------------------------------------------------------===//
40484085
// Floating Point Ops
40494086
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ struct MissingFeatures {
322322
static bool invokeOp() { return false; }
323323
static bool labelOp() { return false; }
324324
static bool ptrDiffOp() { return false; }
325+
static bool llvmLoweringPtrDiffConsidersPointee() { return false; }
325326
static bool ptrStrideOp() { return false; }
326327
static bool switchOp() { return false; }
327328
static bool throwOp() { return false; }

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,9 +1734,9 @@ mlir::Value ScalarExprEmitter::emitSub(const BinOpInfo &ops) {
17341734
// LLVM we shall take VLA's, division by element size, etc.
17351735
//
17361736
// See more in `EmitSub` in CGExprScalar.cpp.
1737-
assert(!cir::MissingFeatures::ptrDiffOp());
1738-
cgf.cgm.errorNYI("ptrdiff");
1739-
return {};
1737+
assert(!cir::MissingFeatures::llvmLoweringPtrDiffConsidersPointee());
1738+
return cir::PtrDiffOp::create(builder, cgf.getLoc(ops.loc), cgf.PtrDiffTy,
1739+
ops.lhs, ops.rhs);
17401740
}
17411741

17421742
mlir::Value ScalarExprEmitter::emitShl(const BinOpInfo &ops) {

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,54 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
14991499
return mlir::success();
15001500
}
15011501

1502+
static uint64_t getTypeSize(mlir::Type type, mlir::Operation &op) {
1503+
mlir::DataLayout layout(op.getParentOfType<mlir::ModuleOp>());
1504+
// For LLVM purposes we treat void as u8.
1505+
if (isa<cir::VoidType>(type))
1506+
type = cir::IntType::get(type.getContext(), 8, /*isSigned=*/false);
1507+
return llvm::divideCeil(layout.getTypeSizeInBits(type), 8);
1508+
}
1509+
1510+
mlir::LogicalResult CIRToLLVMPtrDiffOpLowering::matchAndRewrite(
1511+
cir::PtrDiffOp op, OpAdaptor adaptor,
1512+
mlir::ConversionPatternRewriter &rewriter) const {
1513+
auto dstTy = mlir::cast<cir::IntType>(op.getType());
1514+
mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1515+
1516+
auto lhs = rewriter.create<mlir::LLVM::PtrToIntOp>(op.getLoc(), llvmDstTy,
1517+
adaptor.getLhs());
1518+
auto rhs = rewriter.create<mlir::LLVM::PtrToIntOp>(op.getLoc(), llvmDstTy,
1519+
adaptor.getRhs());
1520+
1521+
auto diff =
1522+
rewriter.create<mlir::LLVM::SubOp>(op.getLoc(), llvmDstTy, lhs, rhs);
1523+
1524+
cir::PointerType ptrTy = op.getLhs().getType();
1525+
assert(!cir::MissingFeatures::llvmLoweringPtrDiffConsidersPointee());
1526+
uint64_t typeSize = getTypeSize(ptrTy.getPointee(), *op);
1527+
1528+
// Avoid silly division by 1.
1529+
mlir::Value resultVal = diff.getResult();
1530+
if (typeSize != 1) {
1531+
auto typeSizeVal = rewriter.create<mlir::LLVM::ConstantOp>(
1532+
op.getLoc(), llvmDstTy, typeSize);
1533+
1534+
if (dstTy.isUnsigned()) {
1535+
auto uDiv =
1536+
rewriter.create<mlir::LLVM::UDivOp>(op.getLoc(), diff, typeSizeVal);
1537+
uDiv.setIsExact(true);
1538+
resultVal = uDiv.getResult();
1539+
} else {
1540+
auto sDiv =
1541+
rewriter.create<mlir::LLVM::SDivOp>(op.getLoc(), diff, typeSizeVal);
1542+
sDiv.setIsExact(true);
1543+
resultVal = sDiv.getResult();
1544+
}
1545+
}
1546+
rewriter.replaceOp(op, resultVal);
1547+
return mlir::success();
1548+
}
1549+
15021550
mlir::LogicalResult CIRToLLVMExpectOpLowering::matchAndRewrite(
15031551
cir::ExpectOp op, OpAdaptor adaptor,
15041552
mlir::ConversionPatternRewriter &rewriter) const {

clang/test/CIR/CodeGen/ptrdiff.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
7+
8+
int addrcmp(const void* a, const void* b) {
9+
// CIR-LABEL: addrcmp
10+
// CIR: %[[R:.*]] = cir.ptr_diff
11+
// CIR: cir.cast integral %[[R]] : !s64i -> !s32i
12+
13+
// LLVM-LABEL: define dso_local i32 @addrcmp(
14+
// LLVM: %[[PTR_A:.*]] = ptrtoint ptr {{.*}} to i64
15+
// LLVM: %[[PTR_B:.*]] = ptrtoint ptr {{.*}} to i64
16+
// LLVM: %[[SUB:.*]] = sub i64 %[[PTR_A]], %[[PTR_B]]
17+
// LLVM-NOT: sdiv
18+
// LLVM: trunc i64 %[[SUB]] to i32
19+
20+
// OGCG-LABEL: define dso_local i32 @addrcmp(
21+
// OGCG: %[[PTR_A:.*]] = ptrtoint ptr {{.*}} to i64
22+
// OGCG: %[[PTR_B:.*]] = ptrtoint ptr {{.*}} to i64
23+
// OGCG: %[[SUB:.*]] = sub i64 %[[PTR_A]], %[[PTR_B]]
24+
// OGCG-NOT: sdiv
25+
// OGCG: trunc i64 %[[SUB]] to i32
26+
return *(const void**)a - *(const void**)b;
27+
}
28+
29+
unsigned long long test_ptr_diff(int *a, int* b) {
30+
// CIR-LABEL: test_ptr_diff
31+
// CIR: %[[D:.*]] = cir.ptr_diff {{.*}} : !cir.ptr<!s32i> -> !s64i
32+
// CIR: %[[U:.*]] = cir.cast integral %[[D]] : !s64i -> !u64i
33+
// CIR: cir.return {{.*}} : !u64i
34+
35+
// LLVM-LABEL: define dso_local i64 @test_ptr_diff(
36+
// LLVM: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
37+
// LLVM: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
38+
// LLVM: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
39+
// LLVM: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 4
40+
// LLVM: store i64 %[[Q]], ptr %[[RETADDR:.*]], align
41+
// LLVM: %[[RETLOAD:.*]] = load i64, ptr %[[RETADDR]], align
42+
// LLVM: ret i64 %[[RETLOAD]]
43+
44+
// OGCG-LABEL: define dso_local i64 @test_ptr_diff(
45+
// OGCG: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
46+
// OGCG: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
47+
// OGCG: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
48+
// OGCG: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 4
49+
// OGCG: ret i64 %[[Q]]
50+
return a - b;
51+
}

clang/test/CIR/CodeGen/ptrdiff.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
3+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
5+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
7+
8+
typedef unsigned long size_type;
9+
10+
size_type size(unsigned long *_start, unsigned long *_finish) {
11+
// CIR-LABEL: cir.func dso_local @_Z4sizePmS_
12+
// CIR: %[[D:.*]] = cir.ptr_diff {{.*}} : !cir.ptr<!u64i> -> !s64i
13+
// CIR: %[[U:.*]] = cir.cast integral %[[D]] : !s64i -> !u64i
14+
// CIR: cir.return {{.*}} : !u64i
15+
16+
// LLVM-LABEL: define dso_local {{.*}}i64 @_Z4sizePmS_(
17+
// LLVM: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
18+
// LLVM: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
19+
// LLVM: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
20+
// LLVM: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 8
21+
// LLVM: store i64 %[[Q]], ptr %[[RETADDR:.*]], align
22+
// LLVM: %[[RET:.*]] = load i64, ptr %[[RETADDR]], align
23+
// LLVM: ret i64 %[[RET]]
24+
25+
// OGCG-LABEL: define dso_local {{.*}}i64 @_Z4sizePmS_(
26+
// OGCG: %[[IA:.*]] = ptrtoint ptr %{{.*}} to i64
27+
// OGCG: %[[IB:.*]] = ptrtoint ptr %{{.*}} to i64
28+
// OGCG: %[[SUB:.*]] = sub i64 %[[IA]], %[[IB]]
29+
// OGCG: %[[Q:.*]] = sdiv exact i64 %[[SUB]], 8
30+
// OGCG: ret i64 %[[Q]]
31+
32+
return static_cast<size_type>(_finish - _start);
33+
}

0 commit comments

Comments
 (0)