Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Mar 31, 2025

No description provided.

Copy link
Contributor Author

arsenm commented Mar 31, 2025

This stack of pull requests is managed by Graphite. Learn more about stacking.

@arsenm arsenm added the ipo Interprocedural optimizations label Mar 31, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review March 31, 2025 12:21
@llvmbot
Copy link
Member

llvmbot commented Mar 31, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Matt Arsenault (arsenm)

Changes

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

1 Files Affected:

  • (added) llvm/test/Transforms/Inline/no-inline-incompatible-gc.ll (+140)
diff --git a/llvm/test/Transforms/Inline/no-inline-incompatible-gc.ll b/llvm/test/Transforms/Inline/no-inline-incompatible-gc.ll
new file mode 100644
index 0000000000000..531801df7cc46
--- /dev/null
+++ b/llvm/test/Transforms/Inline/no-inline-incompatible-gc.ll
@@ -0,0 +1,140 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes='cgscc(inline)' -pass-remarks=inline -pass-remarks-missed=inline < %s 2> %t.err | FileCheck %s
+; RUN: FileCheck -implicit-check-not=remark -check-prefix=REMARK %s < %t.err
+
+; REMARK: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_no_gc'
+; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_same_gc'
+; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' is not inlined into 'caller_incompatible_gc': incompatible GC
+; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_inline_first_caller'
+; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_other_gc' is not inlined into 'caller_inline_first_caller': incompatible GC
+; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_gc' inlined into 'caller_inline_second_caller'
+; REMARK-NEXT: remark: <unknown>:0:0: 'callee_with_other_gc' is not inlined into 'caller_inline_second_caller': incompatible GC
+
+%IntArray = type { i32, [0 x ptr] }
+
+; Callee gc propagates to the caller
+define i32 @caller_no_gc() {
+; CHECK-LABEL: define i32 @caller_no_gc() gc "example" {
+; CHECK-NEXT:    [[ROOT_I:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
+; CHECK-NEXT:    [[OBJ_I:%.*]] = call ptr @h()
+; CHECK-NEXT:    store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
+; CHECK-NEXT:    [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    ret i32 [[LENGTH_I]]
+;
+  %x = call i32 @callee_with_gc()
+  ret i32 %x
+}
+
+; Inline of matching gc allowed.
+define i32 @caller_same_gc() gc "example" {
+; CHECK-LABEL: define i32 @caller_same_gc() gc "example" {
+; CHECK-NEXT:    [[ROOT_I:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
+; CHECK-NEXT:    [[OBJ_I:%.*]] = call ptr @h()
+; CHECK-NEXT:    store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
+; CHECK-NEXT:    [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    ret i32 [[LENGTH_I]]
+;
+  %x = call i32 @callee_with_gc()
+  ret i32 %x
+}
+
+; Reject inline with mismatched gc
+define i32 @caller_incompatible_gc() gc "incompatible" {
+; CHECK-LABEL: define i32 @caller_incompatible_gc() gc "incompatible" {
+; CHECK-NEXT:    [[X:%.*]] = call i32 @callee_with_gc()
+; CHECK-NEXT:    ret i32 [[X]]
+;
+  %x = call i32 @callee_with_gc()
+  ret i32 %x
+}
+
+define i32 @callee_with_gc() gc "example" {
+; CHECK-LABEL: define i32 @callee_with_gc() gc "example" {
+; CHECK-NEXT:    [[ROOT:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @llvm.gcroot(ptr [[ROOT]], ptr null)
+; CHECK-NEXT:    [[OBJ:%.*]] = call ptr @h()
+; CHECK-NEXT:    store ptr [[OBJ]], ptr [[ROOT]], align 8
+; CHECK-NEXT:    [[LENGTH_PTR:%.*]] = getelementptr [[INTARRAY:%.*]], ptr [[OBJ]], i32 0, i32 0
+; CHECK-NEXT:    [[LENGTH:%.*]] = load i32, ptr [[LENGTH_PTR]], align 4
+; CHECK-NEXT:    ret i32 [[LENGTH]]
+;
+  %root = alloca ptr, align 8
+  call void @llvm.gcroot(ptr %root, ptr null)
+  %obj = call ptr @h()
+  store ptr %obj, ptr %root, align 8
+  %Length.ptr = getelementptr %IntArray, ptr %obj, i32 0, i32 0
+  %Length = load i32, ptr %Length.ptr, align 4
+  ret i32 %Length
+}
+
+define i32 @callee_with_other_gc() gc "other-example" {
+; CHECK-LABEL: define i32 @callee_with_other_gc() gc "other-example" {
+; CHECK-NEXT:    [[ROOT:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @llvm.gcroot(ptr [[ROOT]], ptr null)
+; CHECK-NEXT:    [[OBJ:%.*]] = call ptr @h()
+; CHECK-NEXT:    store ptr [[OBJ]], ptr [[ROOT]], align 8
+; CHECK-NEXT:    [[LENGTH_PTR:%.*]] = getelementptr [[INTARRAY:%.*]], ptr [[OBJ]], i32 0, i32 0
+; CHECK-NEXT:    [[LENGTH:%.*]] = load i32, ptr [[LENGTH_PTR]], align 4
+; CHECK-NEXT:    ret i32 [[LENGTH]]
+;
+  %root = alloca ptr, align 8
+  call void @llvm.gcroot(ptr %root, ptr null)
+  %obj = call ptr @h()
+  store ptr %obj, ptr %root, align 8
+  %Length.ptr = getelementptr %IntArray, ptr %obj, i32 0, i32 0
+  %Length = load i32, ptr %Length.ptr, align 4
+  ret i32 %Length
+}
+
+; After inlining the first call, inline is blocked of the second call
+; since the gc type propagates to the caller.
+define i32 @caller_inline_first_caller() {
+; CHECK-LABEL: define i32 @caller_inline_first_caller() gc "example" {
+; CHECK-NEXT:    [[ROOT_I:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
+; CHECK-NEXT:    [[OBJ_I:%.*]] = call ptr @h()
+; CHECK-NEXT:    store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
+; CHECK-NEXT:    [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    [[Y:%.*]] = call i32 @callee_with_other_gc()
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[LENGTH_I]], [[Y]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+  %x = call i32 @callee_with_gc()
+  %y = call i32 @callee_with_other_gc()
+  %add = add i32 %x, %y
+  ret i32 %add
+}
+
+; We can't inline the first call due to the incompatible gc, but can
+; inline the second
+define i32 @caller_inline_second_caller() gc "example" {
+; CHECK-LABEL: define i32 @caller_inline_second_caller() gc "example" {
+; CHECK-NEXT:    [[ROOT_I:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    call void @llvm.gcroot(ptr [[ROOT_I]], ptr null)
+; CHECK-NEXT:    [[OBJ_I:%.*]] = call ptr @h()
+; CHECK-NEXT:    store ptr [[OBJ_I]], ptr [[ROOT_I]], align 8
+; CHECK-NEXT:    [[LENGTH_I:%.*]] = load i32, ptr [[OBJ_I]], align 4
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[ROOT_I]])
+; CHECK-NEXT:    [[Y:%.*]] = call i32 @callee_with_other_gc()
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[LENGTH_I]], [[Y]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+  %x = call i32 @callee_with_gc()
+  %y = call i32 @callee_with_other_gc()
+  %add = add i32 %x, %y
+  ret i32 %add
+}
+
+declare ptr @h()
+
+declare void @llvm.gcroot(ptr, ptr) #0
+attributes #0 = { nounwind }

@dantrushin
Copy link
Contributor

LGTM

@arsenm arsenm merged commit 5b8d8bb into main Mar 31, 2025
16 checks passed
@arsenm arsenm deleted the users/arsenm/inliner/fix-missing-test-coverage-incompatible-gc branch March 31, 2025 23:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ipo Interprocedural optimizations llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants