[llvm] [CodeGen] Use byte offsets and ptradd in ShadowStackGCLowering (PR #178436)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 14:00:56 PDT 2026


isuckatcs wrote:

> that PR was created came a couple weeks after this PR was merged, so I don't see how you're suggesting this PR broke that PR

I didn't suggest this. It didn't brake THAT PR, it is a behavioral change unrelated to that.

> I'm not sure I follow this argument, as your own comment earlier says that would have segfaulted LLVM.

This snippet has been accepted even in LLVM 18 (https://godbolt.org/z/9ceEfeG9v).
```llvm
@metadata = constant i32 0

define void @foo() gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  store { i64, ptr } zeroinitializer, ptr %0, align 8
  call void @llvm.gcroot(ptr %0, ptr @metadata)
  ret void
}
```

The crash comes with this variation, where the root is not initialized (https://godbolt.org/z/8z14s76YM).
```llvm
@metadata = constant i32 0

define void @foo() gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  call void @llvm.gcroot(ptr %0, ptr @metadata)
  ret void
}
```
Instruction selection crashes because the `gc-lowering` pass creates invalid IR.
```llvm
*** IR Dump Before Lower Garbage Collection Instructions (gc-lowering) ***
define void @foo() gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  call void @llvm.gcroot(ptr %0, ptr @metadata)
  ret void
}
*** IR Dump After Lower Garbage Collection Instructions (gc-lowering) ***
define void @foo() gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  store { i64, ptr } null, ptr %0, align 8
  call void @llvm.gcroot(ptr %0, ptr @metadata)
  ret void
}
```

Another way to trigger the crash is to keep the original snippet, and pass it to clang with `-O1` or higher optimization (https://godbolt.org/z/4Krdj9f7W).
```llvm
@metadata = constant i32 0

define void @foo() gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  store { i64, ptr } zeroinitializer, ptr %0, align 8
  call void @llvm.gcroot(ptr %0, ptr @metadata)
  ret void
}
```
Here SROA first replaces the zero initialization.
```llvm
; *** IR Dump Before SROAPass on foo ***
define void @foo() gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  store { i64, ptr } zeroinitializer, ptr %0, align 8
  call void @llvm.gcroot(ptr %0, ptr @metadata)
  ret void
}
; *** IR Dump After SROAPass on foo ***
define void @foo() gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  %.fca.0.gep = getelementptr inbounds { i64, ptr }, ptr %0, i32 0, i32 0
  store i64 0, ptr %.fca.0.gep, align 8
  %.fca.1.gep = getelementptr inbounds { i64, ptr }, ptr %0, i32 0, i32 1
  store ptr null, ptr %.fca.1.gep, align 8
  call void @llvm.gcroot(ptr %0, ptr @metadata)
  ret void
}
```
Then `gc-lower` thinks the root is not initialized and inserts the wrong initializer, leading to invalid IR once again.
```llvm
*** IR Dump Before Lower Garbage Collection Instructions (gc-lowering) *** (function: foo)
define void @foo() local_unnamed_addr #0 gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %0, i8 0, i64 16, i1 false)
  call void @llvm.gcroot(ptr nonnull %0, ptr nonnull @metadata)
  ret void
}
*** IR Dump After Lower Garbage Collection Instructions (gc-lowering) *** (function: foo)
define void @foo() local_unnamed_addr #0 gc "shadow-stack" {
entry:
  %0 = alloca { i64, ptr }, align 8
  store { i64, ptr } null, ptr %0, align 8
  call void @llvm.memset.p0.i64(ptr noundef nonnull align 8 dereferenceable(16) %0, i8 0, i64 16, i1 false)
  call void @llvm.gcroot(ptr nonnull %0, ptr nonnull @metadata)
  ret void
}
```

https://github.com/llvm/llvm-project/pull/178436


More information about the llvm-commits mailing list