Skip to content

[llvm][gvn-sink] Don't try to sink inline asm #138414

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
merged 1 commit into from
May 6, 2025
Merged

Conversation

ilovepi
Copy link
Contributor

@ilovepi ilovepi commented May 3, 2025

Fixes #138345. Before this patch, gvn-sink would try to sink inline
assembly statements. Other GVN passes avoid them (see

if (CallB->isInlineAsm())

Similarly, gvn-sink should skip these instructions, since they are not
safe to move. To do this, we update the early exit in
canReplaceOperandWithVariable, since it should have caught this case.
It's more efficient to also skip numbering in GVNSink if the instruction
is InlineAsm, but that should be infrequent.

The test added is reduced from a failure when compiling Fuchsia with
gvn-sink.

Copy link
Contributor Author

ilovepi commented May 3, 2025

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

@ilovepi ilovepi requested review from hiraditya and nikic May 3, 2025 16:59
@ilovepi ilovepi added the llvm:GVN GVN and NewGVN stages (Global value numbering) label May 3, 2025 — with Graphite App
@ilovepi ilovepi requested a review from fhahn May 3, 2025 17:02
@ilovepi ilovepi marked this pull request as ready for review May 3, 2025 17:02
@llvmbot
Copy link
Member

llvmbot commented May 3, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Paul Kirth (ilovepi)

Changes

Fixes #138345. Before this patch, gvn-sink would try to sink inline
assembly statements. Other GVN passes avoid them (see

if (CallB->isInlineAsm())

Similarly, gvn-sink should skip these instructions, since they are not
safe to move.

The test added is reduced from a failure when compiling Fuchsia. There
were two distinct failure modes. One occurred when only running gvn-sink
alone, but a different failure happened when running
correlated-propagation afterwards.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/GVNSink.cpp (+3)
  • (added) llvm/test/Transforms/GVNSink/pr138345.ll (+51)
diff --git a/llvm/lib/Transforms/Scalar/GVNSink.cpp b/llvm/lib/Transforms/Scalar/GVNSink.cpp
index 2058df33ea331..40f7a61cb5717 100644
--- a/llvm/lib/Transforms/Scalar/GVNSink.cpp
+++ b/llvm/lib/Transforms/Scalar/GVNSink.cpp
@@ -548,6 +548,9 @@ class GVNSink {
     if (isa<PHINode>(I) || I->isEHPad() || isa<AllocaInst>(I) ||
         I->getType()->isTokenTy())
       return true;
+    // Inline asm can't be sunk either.
+    if (auto *CB = dyn_cast<CallBase>(I); CB->isInlineAsm())
+      return true;
     return false;
   }
 
diff --git a/llvm/test/Transforms/GVNSink/pr138345.ll b/llvm/test/Transforms/GVNSink/pr138345.ll
new file mode 100644
index 0000000000000..1c9ff5936625f
--- /dev/null
+++ b/llvm/test/Transforms/GVNSink/pr138345.ll
@@ -0,0 +1,51 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; REQUIRES: x86-registered-target
+; RUN:  opt -passes="gvn-sink" -S %s | FileCheck %s
+; RUN:  opt -passes="gvn-sink,correlated-propagation" -S %s | FileCheck %s
+
+;; See https://github.com/llvm/llvm-project/issues/138345 for details.
+;; The program below used to crash due to taking the address of the inline asm.
+;; gvn-sink shouldn't do anything in this case, so test that the pass no longer
+;; generates invalid IR and no longer crashes.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @c(i64 %num, ptr %ptr) {
+; CHECK-LABEL: define void @c(
+; CHECK-SAME: i64 [[NUM:%.*]], ptr [[PTR:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    switch i64 [[NUM]], label %[[SW_EPILOG:.*]] [
+; CHECK-NEXT:      i64 1, label %[[SW_BB:.*]]
+; CHECK-NEXT:      i64 0, label %[[SW_BB1:.*]]
+; CHECK-NEXT:    ]
+; CHECK:       [[SW_BB]]:
+; CHECK-NEXT:    [[TMP1:%.*]] = load i8, ptr [[PTR]], align 1
+; CHECK-NEXT:    call void asm sideeffect "", "r,r,~{dirflag},~{fpsr},~{flags}"(i8 [[TMP1]], ptr @c)
+; CHECK-NEXT:    br label %[[SW_EPILOG]]
+; CHECK:       [[SW_BB1]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = load i8, ptr [[PTR]], align 1
+; CHECK-NEXT:    call void asm sideeffect "movdqu 0 [[XMM0:%.*]] \0A\09", "r,r,~{dirflag},~{fpsr},~{flags}"(i8 [[TMP2]], ptr @c)
+; CHECK-NEXT:    br label %[[SW_EPILOG]]
+; CHECK:       [[SW_EPILOG]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  switch i64 %num, label %sw.epilog [
+  i64 1, label %sw.bb
+  i64 0, label %sw.bb1
+  ]
+
+sw.bb:                                            ; preds = %entry
+  %1 = load i8, ptr %ptr, align 1
+  call void asm sideeffect "", "r,r,~{dirflag},~{fpsr},~{flags}"(i8 %1, ptr @c)
+  br label %sw.epilog
+
+sw.bb1:                                           ; preds = %entry
+  %2 = load i8, ptr %ptr, align 1
+  call void asm sideeffect "movdqu 0 %xmm0           \0A\09", "r,r,~{dirflag},~{fpsr},~{flags}"(i8 %2, ptr @c)
+  br label %sw.epilog
+
+sw.epilog:                                        ; preds = %sw.bb1, %sw.bb, %entry
+  ret void
+}

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like something that canReplaceOperandWithVariable() should be handling -- why doesn't it? Is it because the !isa<Constant> bails out before we get to the inline asm check?

Copy link
Contributor Author

ilovepi commented May 4, 2025

Yes, that seems to be why. For GVNSink, I'd guess its still better to skip numbering those instructions, but maybe the check in canReplaceOperandWithVariable() should be updated too?

@ilovepi ilovepi force-pushed the users/ilovepi/gvn-sink-bug branch from 49c2f79 to e680ddb Compare May 4, 2025 17:18
Copy link

github-actions bot commented May 4, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor Author

ilovepi commented May 4, 2025

I updated canReplaceOperandWithVariable() too and tested it w/o the change in GVNSink.cpp. I'm happy to update or remove either of those if you'd prefer.

@ilovepi ilovepi force-pushed the users/ilovepi/gvn-sink-bug branch from e680ddb to b3b25a5 Compare May 4, 2025 17:22
@nikic
Copy link
Contributor

nikic commented May 5, 2025

I'd probably only leave the canReplaceOperandWithVariable() check -- you're right that we can avoid numbering this way, but given how rare this case should be, it's probably not worth having a duplicate check.

Copy link
Contributor Author

ilovepi commented May 5, 2025

Sounds good. I'll do that and try out your suggestion.

Any idea what's happening on Windows? I'm having trouble getting llvm to build on my windows workstation for some reason (Its been quite some time since I tried, and I think some configs may have changed). It looks like a memory error, but I don't see how my change would introduce that only on Windows.

Fixes #138345. Before this patch, gvn-sink would try to sink inline
assembly statements. Other GVN passes avoid them (see
https://github.com/llvm/llvm-project/blob/b4fac94181c4cf17dbb7ecc2ae975712b0e4a6d1/llvm/lib/Transforms/Scalar/GVN.cpp#L2932
Similarly, gvn-sink should skip these instructions, since they are not
safe to move. To do this, we update the early exit in
canReplaceOperandWithVariable, since it should have caught this case.
It's more efficient to also skip numbering in GVNSink if the instruction
is InlineAsm, but that should be infrequent.

The test added is reduced from a failure when compiling Fuchsia with
gvn-sink.
@ilovepi ilovepi force-pushed the users/ilovepi/gvn-sink-bug branch from b3b25a5 to 170ab29 Compare May 5, 2025 18:04
Copy link
Contributor Author

ilovepi commented May 5, 2025

Well I guess windows is passing now, so I guess safe to ignore the question about windows. I still don't see anything in the old patch set that makes me think we were reading an invalid pointer for some reason.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Collaborator

@hiraditya hiraditya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing it

Copy link
Contributor Author

ilovepi commented May 6, 2025

Merge activity

  • May 5, 9:14 PM EDT: A user started a stack merge that includes this pull request via Graphite.
  • May 5, 9:16 PM EDT: @ilovepi merged this pull request with Graphite.

@ilovepi ilovepi merged commit 43eafc0 into main May 6, 2025
11 checks passed
@ilovepi ilovepi deleted the users/ilovepi/gvn-sink-bug branch May 6, 2025 01:16
ilovepi added a commit that referenced this pull request May 6, 2025
I had forgotten to upload the formatting change.
ilovepi added a commit that referenced this pull request May 6, 2025
I had forgotten to upload the formatting change.
@llvm-ci
Copy link
Collaborator

llvm-ci commented May 6, 2025

LLVM Buildbot has detected a new failure on builder mlir-nvidia running on mlir-nvidia while building llvm at step 7 "test-build-check-mlir-build-only-check-mlir".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/138/builds/12689

Here is the relevant piece of the build log for the reference
Step 7 (test-build-check-mlir-build-only-check-mlir) failure: test (failure)
******************** TEST 'MLIR :: Integration/GPU/CUDA/async.mlir' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/Integration/GPU/CUDA/async.mlir  | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -gpu-kernel-outlining  | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -pass-pipeline='builtin.module(gpu.module(strip-debuginfo,convert-gpu-to-nvvm),nvvm-attach-target)'  | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -gpu-async-region -gpu-to-llvm -reconcile-unrealized-casts -gpu-module-to-binary="format=fatbin"  | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -async-to-async-runtime -async-runtime-ref-counting  | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -convert-async-to-llvm -convert-func-to-llvm -convert-arith-to-llvm -convert-cf-to-llvm -reconcile-unrealized-casts  | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-runner    --shared-libs=/vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/lib/libmlir_cuda_runtime.so    --shared-libs=/vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/lib/libmlir_async_runtime.so    --shared-libs=/vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/lib/libmlir_runner_utils.so    --entry-point-result=void -O0  | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/FileCheck /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/Integration/GPU/CUDA/async.mlir
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/Integration/GPU/CUDA/async.mlir
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -gpu-kernel-outlining
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt '-pass-pipeline=builtin.module(gpu.module(strip-debuginfo,convert-gpu-to-nvvm),nvvm-attach-target)'
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -gpu-async-region -gpu-to-llvm -reconcile-unrealized-casts -gpu-module-to-binary=format=fatbin
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -async-to-async-runtime -async-runtime-ref-counting
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-opt -convert-async-to-llvm -convert-func-to-llvm -convert-arith-to-llvm -convert-cf-to-llvm -reconcile-unrealized-casts
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/mlir-runner --shared-libs=/vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/lib/libmlir_cuda_runtime.so --shared-libs=/vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/lib/libmlir_async_runtime.so --shared-libs=/vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/lib/libmlir_runner_utils.so --entry-point-result=void -O0
# .---command stderr------------
# | 'cuStreamWaitEvent(stream, event, 0)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuEventDestroy(event)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuStreamWaitEvent(stream, event, 0)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuEventDestroy(event)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuStreamWaitEvent(stream, event, 0)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuStreamWaitEvent(stream, event, 0)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuEventDestroy(event)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuEventDestroy(event)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuEventSynchronize(event)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# | 'cuEventDestroy(event)' failed with 'CUDA_ERROR_CONTEXT_IS_DESTROYED'
# `-----------------------------
# executed command: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.obj/bin/FileCheck /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/Integration/GPU/CUDA/async.mlir
# .---command stderr------------
# | /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/Integration/GPU/CUDA/async.mlir:68:12: error: CHECK: expected string not found in input
# |  // CHECK: [84, 84]
# |            ^
# | <stdin>:1:1: note: scanning from here
# | Unranked Memref base@ = 0x5cc19c81fb90 rank = 1 offset = 0 sizes = [2] strides = [1] data = 
# | ^
# | <stdin>:2:1: note: possible intended match here
# | [42, 42]
# | ^
# | 
# | Input file: <stdin>
# | Check file: /vol/worker/mlir-nvidia/mlir-nvidia/llvm.src/mlir/test/Integration/GPU/CUDA/async.mlir
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             1: Unranked Memref base@ = 0x5cc19c81fb90 rank = 1 offset = 0 sizes = [2] strides = [1] data =  
# | check:68'0     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# |             2: [42, 42] 
# | check:68'0     ~~~~~~~~~
# | check:68'1     ?         possible intended match
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:GVN GVN and NewGVN stages (Global value numbering) llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Assertion failure with gvn-sink
5 participants