Author: Adam Smith Date: 2026-09-18T06:56:28-05:00 New Revision: 6b7f72c11059e52dad5cf33895af3f2284ab8fc0
URL: https://github.com/llvm/llvm-project/commit/6b7f72c11059e52dad5cf33895af3f2284ab8fc0 DIFF: https://github.com/llvm/llvm-project/commit/6b7f72c11059e52dad5cf33895af3f2284ab8fc0.diff LOG: [CIR] Add cir.ptr_mask (#224143) `cir.ptr_mask` ANDs a pointer with an integer mask and gives back a pointer, lowering to llvm.intr.ptrmask. Paired with `cir.ptr_stride` it rounds a pointer up to an alignment the way classic's emitRoundPointerUpToAlignment does, without the round trip through an integer that would lose provenance. llvm.ptrmask needs the mask at exactly the target's pointer index width, so the lowering extends or truncates it first. GEP takes an index of any width, so `cir.ptr_stride` never needed this. Assisted-by: Cursor / claude-opus-5 Added: clang/test/CIR/IR/invalid-ptr-mask.cir clang/test/CIR/IR/ptr-mask.cir clang/test/CIR/Lowering/ptr-mask.cir Modified: clang/include/clang/CIR/Dialect/IR/CIROps.td clang/lib/CIR/Dialect/IR/CIRDialect.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index f23475842ed3a..8770f4acbcc82 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -544,6 +544,42 @@ def CIR_PtrStrideOp : CIR_Op<"ptr_stride", [ }]; } +//===----------------------------------------------------------------------===// +// PtrMaskOp +//===----------------------------------------------------------------------===// + +def CIR_PtrMaskOp : CIR_Op<"ptr_mask", [ + Pure, AllTypesMatch<["ptr", "result"]> +]> { + let summary = "Clear bits of a pointer"; + let description = [{ + The `cir.ptr_mask` operation clears the bits of `ptr` that are not set in + `mask`. The result stays a pointer, whereas masking it as an integer + would lose provenance. + + `mask` must be exactly the target's pointer index width. + + ``` + %m = cir.const #cir.int<-32> : !s64i + %0 = cir.ptr_mask %p, %m : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> + ``` + }]; + + let arguments = (ins + CIR_PointerType:$ptr, + CIR_AnyFundamentalIntType:$mask + ); + + let results = (outs CIR_PointerType:$result); + + let assemblyFormat = [{ + $ptr`,` $mask `:` functional-type(operands, results) attr-dict + }]; + + let llvmOp = "PtrMaskOp"; + let hasVerifier = 1; +} + //===----------------------------------------------------------------------===// // ConstantOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 31ffb023fee74..1da8ad66c85b5 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -3551,6 +3551,26 @@ LogicalResult cir::CopyOp::verify() { return mlir::success(); } +//===----------------------------------------------------------------------===// +// PtrMaskOp Definitions +//===----------------------------------------------------------------------===// + +LogicalResult cir::PtrMaskOp::verify() { + mlir::DataLayout layout = mlir::DataLayout::closest(*this); + std::optional<uint64_t> indexWidth = + layout.getTypeIndexBitwidth(getPtr().getType()); + if (!indexWidth) + return emitOpError() << "pointer has no index width"; + + uint64_t maskWidth = getMask().getType().getWidth(); + if (maskWidth != *indexWidth) + return emitOpError() << "mask width " << maskWidth + << " must equal the pointer index width " + << *indexWidth; + + return mlir::success(); +} + //===----------------------------------------------------------------------===// // GetRuntimeMemberOp Definitions //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/IR/invalid-ptr-mask.cir b/clang/test/CIR/IR/invalid-ptr-mask.cir new file mode 100644 index 0000000000000..6546c259cf0f0 --- /dev/null +++ b/clang/test/CIR/IR/invalid-ptr-mask.cir @@ -0,0 +1,51 @@ +// RUN: cir-opt %s -verify-diagnostics -split-input-file + +!u8i = !cir.int<u, 8> +!s31i = !cir.int<s, 31> +!s32i = !cir.int<s, 32> +!s64i = !cir.int<s, 64> +!bitint64 = !cir.int<s, 64, bitint> + +module { + cir.func @result_pointee_ diff ers(%arg0: !cir.ptr<!u8i>) -> !cir.ptr<!s32i> { + %0 = cir.const #cir.int<-32> : !s64i + // expected-error@+1 {{'cir.ptr_mask' op failed to verify that all of {ptr, result} have same type}} + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!s32i> + cir.return %1 : !cir.ptr<!s32i> + } + + cir.func @bitint_mask(%arg0: !cir.ptr<!u8i>, %arg1: !bitint64) -> !cir.ptr<!u8i> { + // expected-error@+1 {{'cir.ptr_mask' op operand #1 must be fundamental integer type, but got '!cir.int<s, 64, bitint>'}} + %0 = cir.ptr_mask %arg0, %arg1 : (!cir.ptr<!u8i>, !bitint64) -> !cir.ptr<!u8i> + cir.return %0 : !cir.ptr<!u8i> + } + + cir.func @non_fundamental_width(%arg0: !cir.ptr<!u8i>, %arg1: !s31i) -> !cir.ptr<!u8i> { + // expected-error@+1 {{'cir.ptr_mask' op operand #1 must be fundamental integer type, but got '!cir.int<s, 31>'}} + %0 = cir.ptr_mask %arg0, %arg1 : (!cir.ptr<!u8i>, !s31i) -> !cir.ptr<!u8i> + cir.return %0 : !cir.ptr<!u8i> + } + + cir.func @mask_narrower_than_index(%arg0: !cir.ptr<!u8i>, %arg1: !s32i) -> !cir.ptr<!u8i> { + // expected-error@+1 {{'cir.ptr_mask' op mask width 32 must equal the pointer index width 64}} + %0 = cir.ptr_mask %arg0, %arg1 : (!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i> + cir.return %0 : !cir.ptr<!u8i> + } +} + +// ----- + +!u8i = !cir.int<u, 8> +!s64i = !cir.int<s, 64> + +// The index width, not the pointer size, is what the mask must match. +module attributes {dlti.dl_spec = #dlti.dl_spec< + #dlti.dl_entry<!cir.ptr<!cir.void>, + #cir.ptr_spec<size = 64, abi = 64, + preferred = 64, index = 32>>>} { + cir.func @mask_matches_size_not_index(%arg0: !cir.ptr<!u8i>, %arg1: !s64i) -> !cir.ptr<!u8i> { + // expected-error@+1 {{'cir.ptr_mask' op mask width 64 must equal the pointer index width 32}} + %0 = cir.ptr_mask %arg0, %arg1 : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> + cir.return %0 : !cir.ptr<!u8i> + } +} diff --git a/clang/test/CIR/IR/ptr-mask.cir b/clang/test/CIR/IR/ptr-mask.cir new file mode 100644 index 0000000000000..983087e3885d6 --- /dev/null +++ b/clang/test/CIR/IR/ptr-mask.cir @@ -0,0 +1,35 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +!u8i = !cir.int<u, 8> +!u64i = !cir.int<u, 64> +!s32i = !cir.int<s, 32> +!s64i = !cir.int<s, 64> + +module { + // CHECK-LABEL: cir.func @mask_byte_ptr( + cir.func @mask_byte_ptr(%arg0: !cir.ptr<!u8i>) -> !cir.ptr<!u8i> { + %0 = cir.const #cir.int<-32> : !s64i + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> + cir.return %1 : !cir.ptr<!u8i> + } + // CHECK: %[[MASK:.+]] = cir.const #cir.int<-32> : !s64i + // CHECK: cir.ptr_mask %arg0, %[[MASK]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> + + // CHECK-LABEL: cir.func @mask_unsigned( + cir.func @mask_unsigned(%arg0: !cir.ptr<!u8i>) -> !cir.ptr<!u8i> { + %0 = cir.const #cir.int<18446744073709551584> : !u64i + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!u8i>, !u64i) -> !cir.ptr<!u8i> + cir.return %1 : !cir.ptr<!u8i> + } + // CHECK: %[[MASK:.+]] = cir.const #cir.int<18446744073709551584> : !u64i + // CHECK: cir.ptr_mask %arg0, %[[MASK]] : (!cir.ptr<!u8i>, !u64i) -> !cir.ptr<!u8i> + + // CHECK-LABEL: cir.func @mask_typed_ptr( + cir.func @mask_typed_ptr(%arg0: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> { + %0 = cir.const #cir.int<-8> : !s64i + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!s32i>, !s64i) -> !cir.ptr<!s32i> + cir.return %1 : !cir.ptr<!s32i> + } + // CHECK: %[[MASK:.+]] = cir.const #cir.int<-8> : !s64i + // CHECK: cir.ptr_mask %arg0, %[[MASK]] : (!cir.ptr<!s32i>, !s64i) -> !cir.ptr<!s32i> +} diff --git a/clang/test/CIR/Lowering/ptr-mask.cir b/clang/test/CIR/Lowering/ptr-mask.cir new file mode 100644 index 0000000000000..f37bc252c9740 --- /dev/null +++ b/clang/test/CIR/Lowering/ptr-mask.cir @@ -0,0 +1,75 @@ +// RUN: cir-opt %s --cir-to-llvm --split-input-file -o %t.mlir +// RUN: FileCheck --input-file=%t.mlir %s + +!u8i = !cir.int<u, 8> +!u64i = !cir.int<u, 64> +!s32i = !cir.int<s, 32> +!s64i = !cir.int<s, 64> + +module attributes {cir.triple = "x86_64-unknown-linux-gnu", + dlti.dl_spec = #dlti.dl_spec<>} { + // CHECK-LABEL: llvm.func @mask_byte_ptr( + // CHECK-NEXT: %[[MASK:.*]] = llvm.mlir.constant(-32 : i64) : i64 + // CHECK-NEXT: %[[RES:.*]] = llvm.intr.ptrmask %arg0, %[[MASK]] : (!llvm.ptr, i64) -> !llvm.ptr + // CHECK-NEXT: llvm.return %[[RES]] : !llvm.ptr + cir.func @mask_byte_ptr(%arg0: !cir.ptr<!u8i>) -> !cir.ptr<!u8i> { + %0 = cir.const #cir.int<-32> : !s64i + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> + cir.return %1 : !cir.ptr<!u8i> + } + + // The mask reaches the intrinsic as it was written, with no conversion. + // CHECK-LABEL: llvm.func @mask_unsigned( + // CHECK-NEXT: %[[MASK:.*]] = llvm.mlir.constant(-32 : i64) : i64 + // CHECK-NEXT: %[[RES:.*]] = llvm.intr.ptrmask %arg0, %[[MASK]] : (!llvm.ptr, i64) -> !llvm.ptr + // CHECK-NEXT: llvm.return %[[RES]] : !llvm.ptr + cir.func @mask_unsigned(%arg0: !cir.ptr<!u8i>) -> !cir.ptr<!u8i> { + %0 = cir.const #cir.int<18446744073709551584> : !u64i + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!u8i>, !u64i) -> !cir.ptr<!u8i> + cir.return %1 : !cir.ptr<!u8i> + } + + // CHECK-LABEL: llvm.func @mask_argument( + // CHECK-SAME: %[[PTR:.*]]: !llvm.ptr, %[[MASK:.*]]: i64) + // CHECK-NEXT: %[[RES:.*]] = llvm.intr.ptrmask %[[PTR]], %[[MASK]] : (!llvm.ptr, i64) -> !llvm.ptr + // CHECK-NEXT: llvm.return %[[RES]] : !llvm.ptr + cir.func @mask_argument(%arg0: !cir.ptr<!u8i>, %arg1: !s64i) -> !cir.ptr<!u8i> { + %0 = cir.ptr_mask %arg0, %arg1 : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> + cir.return %0 : !cir.ptr<!u8i> + } + + // CHECK-LABEL: llvm.func @mask_typed_ptr( + // CHECK-NEXT: %[[MASK:.*]] = llvm.mlir.constant(-8 : i64) : i64 + // CHECK-NEXT: %[[RES:.*]] = llvm.intr.ptrmask %arg0, %[[MASK]] : (!llvm.ptr, i64) -> !llvm.ptr + // CHECK-NEXT: llvm.return %[[RES]] : !llvm.ptr + cir.func @mask_typed_ptr(%arg0: !cir.ptr<!s32i>) -> !cir.ptr<!s32i> { + %0 = cir.const #cir.int<-8> : !s64i + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!s32i>, !s64i) -> !cir.ptr<!s32i> + cir.return %1 : !cir.ptr<!s32i> + } +} + +// ----- + +!u8i = !cir.int<u, 8> +!s32i = !cir.int<s, 32> + +// This layout gives the pointer a 64-bit size and a 32-bit index width, so the +// mask is 32 bits, matching the index rather than the size. +module attributes {cir.triple = "x86_64-unknown-linux-gnu", + dlti.dl_spec = #dlti.dl_spec< + #dlti.dl_entry<!cir.ptr<!cir.void>, + #cir.ptr_spec<size = 64, abi = 64, + preferred = 64, index = 32>>, + #dlti.dl_entry<!llvm.ptr, + dense<[64, 64, 64, 32]> : vector<4xi64>>>} { + // CHECK-LABEL: llvm.func @mask_narrow_index( + // CHECK-NEXT: %[[MASK:.*]] = llvm.mlir.constant(-32 : i32) : i32 + // CHECK-NEXT: %[[RES:.*]] = llvm.intr.ptrmask %arg0, %[[MASK]] : (!llvm.ptr, i32) -> !llvm.ptr + // CHECK-NEXT: llvm.return %[[RES]] : !llvm.ptr + cir.func @mask_narrow_index(%arg0: !cir.ptr<!u8i>) -> !cir.ptr<!u8i> { + %0 = cir.const #cir.int<-32> : !s32i + %1 = cir.ptr_mask %arg0, %0 : (!cir.ptr<!u8i>, !s32i) -> !cir.ptr<!u8i> + cir.return %1 : !cir.ptr<!u8i> + } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
