[clang] [CIR] Fix spurious MemRead on pure pointer-arithmetic ops (PR #185154)

Henrich Lauko via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 6 22:55:45 PST 2026


https://github.com/xlauko created https://github.com/llvm/llvm-project/pull/185154

Remove incorrect [MemRead] annotations from ops that only perform
pointer arithmetic without loading memory. Add Pure trait to ops
that were missing it.

Affected ops:
- VTableGetVPtrOp, VTableGetVirtualFnAddrOp, VTableGetTypeInfoOp:
  remove [MemRead] (already had Pure)
- GetMemberOp, GetRuntimeMemberOp, BaseClassAddrOp, DerivedClassAddrOp:
  remove [MemRead] and add Pure

>From 08f3edad0554c236bbdc977eaa574ca4dca344ee Mon Sep 17 00:00:00 2001
From: xlauko <xlauko at mail.muni.cz>
Date: Sat, 7 Mar 2026 07:53:25 +0100
Subject: [PATCH] [CIR] Fix spurious MemRead on pure pointer-arithmetic ops

Remove incorrect [MemRead] annotations from ops that only perform
pointer arithmetic without loading memory. Add Pure trait to ops
that were missing it.

Affected ops:
- VTableGetVPtrOp, VTableGetVirtualFnAddrOp, VTableGetTypeInfoOp:
  remove [MemRead] (already had Pure)
- GetMemberOp, GetRuntimeMemberOp, BaseClassAddrOp, DerivedClassAddrOp:
  remove [MemRead] and add Pure
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 25 ++++---
 .../CIR/Transforms/pure-ptr-arithmetic.cir    | 65 +++++++++++++++++++
 2 files changed, 77 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/CIR/Transforms/pure-ptr-arithmetic.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 79eef71229192..e5268a69b15e0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -204,7 +204,6 @@ def CIR_CastKind : CIR_I32EnumAttr<"CastKind", "cast kind", [
 def CIR_CastOp : CIR_Op<"cast", [
   Pure, DeclareOpInterfaceMethods<PromotableOpInterface>
 ]> {
-  // FIXME: not all conversions are free of side effects.
   let summary = "Conversion between values of different types";
   let description = [{
     Apply the usual C/C++ conversion rules between values. This operation models
@@ -2804,7 +2803,7 @@ def CIR_VTableGetVPtrOp : CIR_Op<"vtable.get_vptr", [Pure]> {
   }];
 
   let arguments = (ins
-    Arg<CIR_PointerType, "the vptr address", [MemRead]>:$src
+    CIR_PointerType:$src
   );
 
   let results = (outs CIR_PtrToVPtr:$result);
@@ -2851,7 +2850,7 @@ def CIR_VTableGetVirtualFnAddrOp : CIR_Op<"vtable.get_virtual_fn_addr", [
   }];
 
   let arguments = (ins
-    Arg<CIR_VPtrType, "vptr", [MemRead]>:$vptr,
+    CIR_VPtrType:$vptr,
     I64Attr:$index);
 
   let results = (outs CIR_PointerType:$result);
@@ -2890,7 +2889,7 @@ def CIR_VTableGetTypeInfoOp : CIR_Op<"vtable.get_type_info", [
     ```
   }];
 
-  let arguments = (ins Arg<CIR_VPtrType, "vptr", [MemRead]>:$vptr);
+  let arguments = (ins CIR_VPtrType:$vptr);
   let results = (outs CIR_PointerType:$result);
 
   let assemblyFormat = [{
@@ -3149,7 +3148,7 @@ def CIR_GetBitfieldOp : CIR_Op<"get_bitfield"> {
 // GetMemberOp
 //===----------------------------------------------------------------------===//
 
-def CIR_GetMemberOp : CIR_Op<"get_member"> {
+def CIR_GetMemberOp : CIR_Op<"get_member", [Pure]> {
   let summary = "Get the address of a member of a record";
   let description = [{
     The `cir.get_member` operation gets the address of a particular named
@@ -3171,7 +3170,7 @@ def CIR_GetMemberOp : CIR_Op<"get_member"> {
   }];
 
   let arguments = (ins
-    Arg<CIR_PointerType, "the address to load from", [MemRead]>:$addr,
+    CIR_PointerType:$addr,
     StrAttr:$name,
     IndexAttr:$index_attr);
 
@@ -4317,7 +4316,7 @@ def CIR_ArrayDtor : CIR_ArrayInitDestroy<"array.dtor"> {
 // GetRuntimeMemberOp
 //===----------------------------------------------------------------------===//
 
-def CIR_GetRuntimeMemberOp : CIR_Op<"get_runtime_member"> {
+def CIR_GetRuntimeMemberOp : CIR_Op<"get_runtime_member", [Pure]> {
   let summary = "Get the address of a member of a record";
   let description = [{
     The `cir.get_runtime_member` operation gets the address of a member from
@@ -4357,8 +4356,8 @@ def CIR_GetRuntimeMemberOp : CIR_Op<"get_runtime_member"> {
   }];
 
   let arguments = (ins
-    Arg<CIR_PtrToRecordType, "address of the record object", [MemRead]>:$addr,
-    Arg<CIR_DataMemberType, "pointer to the target member">:$member);
+    CIR_PtrToRecordType:$addr,
+    CIR_DataMemberType:$member);
 
   let results = (outs Res<CIR_PointerType, "">:$result);
 
@@ -4714,7 +4713,7 @@ def CIR_VecSplatOp : CIR_Op<"vec.splat", [
 // BaseClassAddrOp
 //===----------------------------------------------------------------------===//
 
-def CIR_BaseClassAddrOp : CIR_Op<"base_class_addr"> {
+def CIR_BaseClassAddrOp : CIR_Op<"base_class_addr", [Pure]> {
   let summary = "Get the base class address for a class/struct";
   let description = [{
     The `cir.base_class_addr` operaration gets the address of a particular
@@ -4743,7 +4742,7 @@ def CIR_BaseClassAddrOp : CIR_Op<"base_class_addr"> {
   }];
 
   let arguments = (ins
-    Arg<CIR_PointerType, "derived class pointer", [MemRead]>:$derived_addr,
+    CIR_PointerType:$derived_addr,
     IndexAttr:$offset, UnitAttr:$assume_not_null);
 
   let results = (outs Res<CIR_PointerType, "">:$base_addr);
@@ -4759,7 +4758,7 @@ def CIR_BaseClassAddrOp : CIR_Op<"base_class_addr"> {
 // DerivedClassAddrOp
 //===----------------------------------------------------------------------===//
 
-def CIR_DerivedClassAddrOp : CIR_Op<"derived_class_addr"> {
+def CIR_DerivedClassAddrOp : CIR_Op<"derived_class_addr", [Pure]> {
   let summary = "Get the derived class address for a class/struct";
   let description = [{
     The `cir.derived_class_addr` operaration gets the address of a particular
@@ -4793,7 +4792,7 @@ def CIR_DerivedClassAddrOp : CIR_Op<"derived_class_addr"> {
   }];
 
   let arguments = (ins
-    Arg<CIR_PointerType, "base class pointer", [MemRead]>:$base_addr,
+    CIR_PointerType:$base_addr,
     IndexAttr:$offset, UnitAttr:$assume_not_null);
 
   let results = (outs Res<CIR_PointerType, "">:$derived_addr);
diff --git a/clang/test/CIR/Transforms/pure-ptr-arithmetic.cir b/clang/test/CIR/Transforms/pure-ptr-arithmetic.cir
new file mode 100644
index 0000000000000..4c7e79923d0db
--- /dev/null
+++ b/clang/test/CIR/Transforms/pure-ptr-arithmetic.cir
@@ -0,0 +1,65 @@
+// RUN: cir-opt %s -canonicalize -o - | FileCheck %s
+
+// Verify that pointer-arithmetic ops marked Pure are eliminated when their
+// results are unused (trivially dead).
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!void = !cir.void
+
+!rec_S = !cir.record<struct "S" {!s32i, !s32i}>
+!rec_Base = !cir.record<struct "Base" {!u8i}>
+!rec_Derived = !cir.record<struct "Derived" {!rec_Base, !s32i}>
+
+module {
+
+// CHECK-LABEL: @dead_get_member
+// CHECK-NOT:     cir.get_member
+cir.func @dead_get_member(%arg0: !cir.ptr<!rec_S>) {
+  %0 = cir.get_member %arg0[1] {name = "y"} : !cir.ptr<!rec_S> -> !cir.ptr<!s32i>
+  cir.return
+}
+
+// CHECK-LABEL: @dead_base_class_addr
+// CHECK-NOT:     cir.base_class_addr
+cir.func @dead_base_class_addr(%arg0: !cir.ptr<!rec_Derived>) {
+  %0 = cir.base_class_addr %arg0 : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base>
+  cir.return
+}
+
+// CHECK-LABEL: @dead_derived_class_addr
+// CHECK-NOT:     cir.derived_class_addr
+cir.func @dead_derived_class_addr(%arg0: !cir.ptr<!rec_Base>) {
+  %0 = cir.derived_class_addr %arg0 : !cir.ptr<!rec_Base> nonnull [0] -> !cir.ptr<!rec_Derived>
+  cir.return
+}
+
+// CHECK-LABEL: @dead_vtable_get_vptr
+// CHECK-NOT:     cir.vtable.get_vptr
+cir.func @dead_vtable_get_vptr(%arg0: !cir.ptr<!rec_Derived>) {
+  %0 = cir.vtable.get_vptr %arg0 : !cir.ptr<!rec_Derived> -> !cir.ptr<!cir.vptr>
+  cir.return
+}
+
+// CHECK-LABEL: @dead_vtable_get_virtual_fn_addr
+// CHECK-NOT:     cir.vtable.get_virtual_fn_addr
+cir.func @dead_vtable_get_virtual_fn_addr(%arg0: !cir.vptr) {
+  %0 = cir.vtable.get_virtual_fn_addr %arg0[0] : !cir.vptr -> !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!rec_Base>)>>>
+  cir.return
+}
+
+// CHECK-LABEL: @dead_vtable_get_type_info
+// CHECK-NOT:     cir.vtable.get_type_info
+cir.func @dead_vtable_get_type_info(%arg0: !cir.vptr) {
+  %0 = cir.vtable.get_type_info %arg0 : !cir.vptr -> !cir.ptr<!cir.ptr<!rec_S>>
+  cir.return
+}
+
+// CHECK-LABEL: @dead_get_runtime_member
+// CHECK-NOT:     cir.get_runtime_member
+cir.func @dead_get_runtime_member(%arg0: !cir.ptr<!rec_S>, %arg1: !cir.data_member<!s32i in !rec_S>) {
+  %0 = cir.get_runtime_member %arg0[%arg1 : !cir.data_member<!s32i in !rec_S>] : !cir.ptr<!rec_S> -> !cir.ptr<!s32i>
+  cir.return
+}
+
+}



More information about the cfe-commits mailing list