Skip to content
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

[SimplifyCFG][JumpThreading] Don't duplicate inline-asm instructions #71571

Closed
wants to merge 1 commit into from

Conversation

mcinally
Copy link
Contributor

@mcinally mcinally commented Nov 7, 2023

Conservatively, don't duplicate an inline-asm instruction during Threading. In this particular case, a label is defined in the inline-asm instruction. Duplicating that inline-asm instruction will result in 2 identical labels being defined, which causes an assembler error.

…during Jump Threading

Conservatively, don't duplicate an inline-asm instruction during Jump
Threading. In this particular case, a label is defined in the inline-asm
instruction. Duplicating that inline-asm instruction will result in 2
identical labels being defined, which causes an assembler error.
@mcinally mcinally added the threading issues related to threading label Nov 7, 2023
@mcinally mcinally self-assigned this Nov 7, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 7, 2023

@llvm/pr-subscribers-llvm-transforms

Author: Cameron McInally (mcinally)

Changes

Conservatively, don't duplicate an inline-asm instruction during Threading. In this particular case, a label is defined in the inline-asm instruction. Duplicating that inline-asm instruction will result in 2 identical labels being defined, which causes an assembler error.


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

4 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/JumpThreading.cpp (+3-1)
  • (modified) llvm/lib/Transforms/Utils/SimplifyCFG.cpp (+3-2)
  • (added) llvm/test/Transforms/JumpThreading/inline-asm.ll (+47)
  • (added) llvm/test/Transforms/SimplifyCFG/inline-asm-threading.ll (+49)
diff --git a/llvm/lib/Transforms/Scalar/JumpThreading.cpp b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
index 7a8128c5b6c0901..18d5b7b45ad2618 100644
--- a/llvm/lib/Transforms/Scalar/JumpThreading.cpp
+++ b/llvm/lib/Transforms/Scalar/JumpThreading.cpp
@@ -491,8 +491,10 @@ static unsigned getJumpThreadDuplicationCost(const TargetTransformInfo *TTI,
 
     // Blocks with NoDuplicate are modelled as having infinite cost, so they
     // are never duplicated.
+    // Conservatively disallow inline-asm instructions. Duplicating inline-asm
+    // instructions can potentially create duplicate labels.
     if (const CallInst *CI = dyn_cast<CallInst>(I))
-      if (CI->cannotDuplicate() || CI->isConvergent())
+      if (CI->cannotDuplicate() || CI->isConvergent() || CI->isInlineAsm())
         return ~0U;
 
     if (TTI->getInstructionCost(&*I, TargetTransformInfo::TCK_SizeAndLatency) ==
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 5ef3a5292af545c..b5ddc2d54fb6a2b 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -3170,9 +3170,10 @@ static bool BlockIsSimpleEnoughToThreadThrough(BasicBlock *BB) {
   // Walk the loop in reverse so that we can identify ephemeral values properly
   // (values only feeding assumes).
   for (Instruction &I : reverse(BB->instructionsWithoutDebug(false))) {
-    // Can't fold blocks that contain noduplicate or convergent calls.
+    // Can't fold blocks that contain noduplicate, convergent calls, or
+    // inline assembly.
     if (CallInst *CI = dyn_cast<CallInst>(&I))
-      if (CI->cannotDuplicate() || CI->isConvergent())
+      if (CI->cannotDuplicate() || CI->isConvergent() || CI->isInlineAsm())
         return false;
 
     // Ignore ephemeral values which are deleted during codegen.
diff --git a/llvm/test/Transforms/JumpThreading/inline-asm.ll b/llvm/test/Transforms/JumpThreading/inline-asm.ll
new file mode 100644
index 000000000000000..2e698e934547807
--- /dev/null
+++ b/llvm/test/Transforms/JumpThreading/inline-asm.ll
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
+; RUN: opt -S -passes=jump-threading < %s | FileCheck %s
+
+; Jump threading should not be able to duplicate a block containing inline
+; assembly, since we do not know what is in the inline-asm instruction.
+
+define void @foo(i32 %n.arg, ptr %b.arg, ptr %c.arg) {
+; CHECK-LABEL: define void @foo(
+; CHECK-SAME: i32 [[N_ARG:%.*]], ptr [[B_ARG:%.*]], ptr [[C_ARG:%.*]]) {
+; CHECK-NEXT:  L.entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i32 [[N_ARG]], 0
+; CHECK-NEXT:    br i1 [[TMP0]], label [[L_B0000:%.*]], label [[L_B0002:%.*]]
+; CHECK:       L.B0002:
+; CHECK-NEXT:    store ptr null, ptr [[C_ARG]], align 8
+; CHECK-NEXT:    store ptr null, ptr [[B_ARG]], align 8
+; CHECK-NEXT:    br label [[L_B0000]]
+; CHECK:       L.B0000:
+; CHECK-NEXT:    call void asm sideeffect " .LABEL: \0A\09", ""()
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i32 [[N_ARG]], 0
+; CHECK-NEXT:    br i1 [[TMP1]], label [[L_B0001:%.*]], label [[L_B0003:%.*]]
+; CHECK:       L.B0003:
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[B_ARG]], align 8
+; CHECK-NEXT:    call void @bar(ptr [[TMP2]])
+; CHECK-NEXT:    br label [[L_B0001]]
+; CHECK:       L.B0001:
+; CHECK-NEXT:    ret void
+;
+L.entry:
+  %0 = icmp eq i32  %n.arg, 0
+  br i1  %0, label %L.B0000, label %L.B0002
+L.B0002:
+  store ptr null, ptr %c.arg
+  store ptr null, ptr %b.arg
+  br label %L.B0000
+L.B0000:
+  call void asm sideeffect "   .LABEL: \0A\09", "" ()
+  %1 = icmp eq i32  %n.arg, 0
+  br i1  %1, label %L.B0001, label %L.B0003
+L.B0003:
+  %2 = load ptr, ptr %b.arg
+  call void  @bar (ptr  %2)
+  br label %L.B0001
+L.B0001:
+  ret void
+}
+
+declare void @bar(ptr)
diff --git a/llvm/test/Transforms/SimplifyCFG/inline-asm-threading.ll b/llvm/test/Transforms/SimplifyCFG/inline-asm-threading.ll
new file mode 100644
index 000000000000000..7ea7f373b3d9f88
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/inline-asm-threading.ll
@@ -0,0 +1,49 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
+; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
+
+; Jump threading should not be able to duplicate a block containing inline
+; assembly, since we do not know what is in the inline-asm instruction.
+
+define void @foo(i32 %n.arg, ptr %b.arg, ptr %c.arg) {
+; CHECK-LABEL: define void @foo(
+; CHECK-SAME: i32 [[N_ARG:%.*]], ptr [[B_ARG:%.*]], ptr [[C_ARG:%.*]]) {
+; CHECK-NEXT:  L.entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i32 [[N_ARG]], 0
+; CHECK-NEXT:    br i1 [[TMP0]], label [[L_B0000:%.*]], label [[L_B0002:%.*]]
+; CHECK:       L.B0002:
+; CHECK-NEXT:    store ptr null, ptr [[C_ARG]], align 8
+; CHECK-NEXT:    store ptr null, ptr [[B_ARG]], align 8
+; CHECK-NEXT:    br label [[L_B0000]]
+; CHECK:       L.B0000:
+; CHECK-NEXT:    call void asm sideeffect " .LABEL: \0A\09", ""()
+; CHECK-NEXT:    br i1 [[TMP0]], label [[L_B0001:%.*]], label [[L_B0003:%.*]]
+; CHECK:       L.B0003:
+; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[B_ARG]], align 8
+; CHECK-NEXT:    call void @bar(ptr [[TMP1]])
+; CHECK-NEXT:    br label [[L_B0001]]
+; CHECK:       L.B0001:
+; CHECK-NEXT:    ret void
+;
+L.entry:
+  %0 = icmp eq i32 %n.arg, 0
+  br i1 %0, label %L.B0000, label %L.B0002
+
+L.B0002:                                          ; preds = %L.entry
+  store ptr null, ptr %c.arg
+  store ptr null, ptr %b.arg
+  br label %L.B0000
+
+L.B0000:                                          ; preds = %L.B0002, %L.entry
+  call void asm sideeffect "   .LABEL: \0A\09", ""()
+  br i1 %0, label %L.B0001, label %L.B0003
+
+L.B0003:                                          ; preds = %L.B0000
+  %1 = load ptr, ptr %b.arg
+  call void @bar(ptr %1)
+  br label %L.B0001
+
+L.B0001:                                          ; preds = %L.B0003, %L.B0000
+  ret void
+}
+
+declare void @bar(ptr)

@yxsamliu
Copy link
Collaborator

yxsamliu commented Nov 8, 2023

A concern is that this may cause perf degradation for inline asm that allows duplication.

Does call void asm sideeffect prevent duplication?

if (const CallInst *CI = dyn_cast<CallInst>(I))
if (CI->cannotDuplicate() || CI->isConvergent())
if (CI->cannotDuplicate() || CI->isConvergent() || CI->isInlineAsm())
Copy link
Contributor

Choose a reason for hiding this comment

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

If we want to say that inline asm cannot be duplicated, then that should be part of cannotDuplicate(), not a separate query. There are a number of other places checking cannotDuplicate().

Copy link
Member

@nickdesaulniers nickdesaulniers left a comment

Choose a reason for hiding this comment

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

We've seen this in the Linux kernel before, pretty sure \@ should be used on the label to make it unique per instantiation. Let me see if I can find the kernel commit I'm thinking of.

(Cant find the kernel commit I'm thinking of). Sorry %= is the sigil. This problem is why it exists.

Under certain circumstances, GCC may duplicate (or remove duplicates of) your assembly code when optimizing. This can lead to unexpected duplicate symbol errors during compilation if your asm code defines symbols or labels. Using ‘%=’ (see AssemblerTemplate) may help resolve this problem.

\@ is for asm .macro expansion, but same idea.
https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html#SEC109

@mcinally
Copy link
Contributor Author

mcinally commented Nov 9, 2023

We've seen this in the Linux kernel before, pretty sure \@ should be used on the label to make it unique per instantiation. Let me see if I can find the kernel commit I'm thinking of.

(Cant find the kernel commit I'm thinking of). Sorry %= is the sigil. This problem is why it exists.

Under certain circumstances, GCC may duplicate (or remove duplicates of) your assembly code when optimizing. This can lead to unexpected duplicate symbol errors during compilation if your asm code defines symbols or labels. Using ‘%=’ (see AssemblerTemplate) may help resolve this problem.

\@ is for asm .macro expansion, but same idea. https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html#SEC109

Ah, that's compelling. Thanks, Nick!

Closing this issue...

@mcinally mcinally closed this Nov 9, 2023
@mcinally mcinally deleted the jump_threading_inline_asm branch November 9, 2023 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:transforms threading issues related to threading
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants