Skip to content

Inliner: Fix missing test coverage for incompatible gc rejection #133708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

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.

3 participants