diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 4c15d9ed0f834..c3b320da3a585 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -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 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 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 + ``` + }]; + + 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 //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 4fbae150b587e..de3bc9487bc8c 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -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; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 637f9ef65c88f..138082b59fecd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -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) { diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index f0d73ac872386..f5c29bbe8d14a 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -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()); + // For LLVM purposes we treat void as u8. + if (isa(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(op.getType()); + mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy); + + auto lhs = rewriter.create(op.getLoc(), llvmDstTy, + adaptor.getLhs()); + auto rhs = rewriter.create(op.getLoc(), llvmDstTy, + adaptor.getRhs()); + + auto diff = + rewriter.create(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( + op.getLoc(), llvmDstTy, typeSize); + + if (dstTy.isUnsigned()) { + auto uDiv = + rewriter.create(op.getLoc(), diff, typeSizeVal); + uDiv.setIsExact(true); + resultVal = uDiv.getResult(); + } else { + auto sDiv = + rewriter.create(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 { diff --git a/clang/test/CIR/CodeGen/ptrdiff.c b/clang/test/CIR/CodeGen/ptrdiff.c new file mode 100644 index 0000000000000..e6a754ebc8d4b --- /dev/null +++ b/clang/test/CIR/CodeGen/ptrdiff.c @@ -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 -> !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; +} \ No newline at end of file diff --git a/clang/test/CIR/CodeGen/ptrdiff.cpp b/clang/test/CIR/CodeGen/ptrdiff.cpp new file mode 100644 index 0000000000000..34ba0ff725581 --- /dev/null +++ b/clang/test/CIR/CodeGen/ptrdiff.cpp @@ -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 -> !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(_finish - _start); +} \ No newline at end of file