Skip to content

[CIR] Handle negative offsets in pointer constants#193624

Open
andykaylor wants to merge 1 commit intollvm:mainfrom
andykaylor:cir-negative-offset
Open

[CIR] Handle negative offsets in pointer constants#193624
andykaylor wants to merge 1 commit intollvm:mainfrom
andykaylor:cir-negative-offset

Conversation

@andykaylor
Copy link
Copy Markdown
Contributor

When a negative offset was used as an index into a global array as part of a global pointer initialization, CIR was hitting an unreachable marker while trying to construct a GlobalView attribute to describe the pointer initialization. Although this sort of negative offset is UB, Clang allows it, so we want to produce the same results via CIR.

Assisted-by: Cursor / claude-4.7-opus-high

When a negative offset was used as an index into a global array
as part of a global pointer initialization, CIR was hitting an unreachable
marker while trying to construct a GlobalView attribute to describe the
pointer initialization. Although this sort of negative offset is UB, Clang
allows it, so we want to produce the same results via CIR.

Assisted-by: Cursor / claude-4.7-opus-high
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Apr 22, 2026
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Apr 22, 2026

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

Changes

When a negative offset was used as an index into a global array as part of a global pointer initialization, CIR was hitting an unreachable marker while trying to construct a GlobalView attribute to describe the pointer initialization. Although this sort of negative offset is UB, Clang allows it, so we want to produce the same results via CIR.

Assisted-by: Cursor / claude-4.7-opus-high


Full diff: https://github.com/llvm/llvm-project/pull/193624.diff

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.cpp (+10-3)
  • (added) clang/test/CIR/CodeGen/global-ptr-init-negative-offset.c (+26)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index 0cf35812babe7..c58b5041cd9cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -91,12 +91,19 @@ void CIRGenBuilderTy::computeGlobalViewIndicesFromFlatOffset(
   if (!offset)
     return;
 
+  // Compute floor-division and a non-negative remainder. A negative flat
+  // offset (e.g. from a pointer one element before the start of an array)
+  // must translate to a negative array index with a non-negative remainder
+  // so that the recursive call can descend into the element type without
+  // a negative offset flowing into the record case below.
   auto getIndexAndNewOffset =
       [](int64_t offset, int64_t eltSize) -> std::pair<int64_t, int64_t> {
     int64_t divRet = offset / eltSize;
-    if (divRet < 0)
-      divRet -= 1; // make sure offset is positive
-    int64_t modRet = offset - (divRet * eltSize);
+    int64_t modRet = offset % eltSize;
+    if (modRet < 0) {
+      divRet -= 1;
+      modRet += eltSize;
+    }
     return {divRet, modRet};
   };
 
diff --git a/clang/test/CIR/CodeGen/global-ptr-init-negative-offset.c b/clang/test/CIR/CodeGen/global-ptr-init-negative-offset.c
new file mode 100644
index 0000000000000..751aed20fbea4
--- /dev/null
+++ b/clang/test/CIR/CodeGen/global-ptr-init-negative-offset.c
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -fclangir -emit-cir -o %t.cir %s
+// RUN: FileCheck -check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -fclangir -emit-llvm -o %t-cir.ll %s
+// RUN: FileCheck -check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o %t.ll %s
+// RUN: FileCheck -check-prefix=LLVM --input-file=%t.ll %s
+
+// Constant initializers that point to an element before the start of an
+// array produce a negative flat offset (e.g. flex-generated scanner tables
+// that do `yytop = &yywork[-1]`). Although forming such a pointer is UB per
+// the C standard, Clang accepts it as a constant expression.
+
+struct Entry {
+  int verify, advance;
+};
+
+extern struct Entry arr[];
+
+struct Entry *before = &arr[-1];
+int *before_field = &arr[-1].advance;
+
+// CIR: cir.global external @before = #cir.global_view<@arr, [-1 : i32]> : !cir.ptr<!rec_Entry>
+// CIR: cir.global external @before_field = #cir.global_view<@arr, [-1 : i32, 1 : i32]> : !cir.ptr<!s32i>
+
+// LLVM: @before = global ptr getelementptr (i8, ptr @arr, i64 -8)
+// LLVM: @before_field = global ptr getelementptr (i8, ptr @arr, i64 -4)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants