Skip to content

[SLP] Make getSameOpcode support interchangeable instructions. #132887

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 3 commits into from
Mar 25, 2025

Conversation

HanKuanChen
Copy link
Contributor

If the Converter has AltOp, that means I should be converted to AltOp
instead of MainOp (which is AltOp in BinOpSameOpcodeHelper).

@llvmbot
Copy link
Member

llvmbot commented Mar 25, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Han-Kuan Chen (HanKuanChen)

Changes

If the Converter has AltOp, that means I should be converted to AltOp
instead of MainOp (which is AltOp in BinOpSameOpcodeHelper).


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp (+3-6)
  • (added) llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll (+36)
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 72e18be9fe71e..6d96aedc5ff9f 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -1094,12 +1094,9 @@ class BinOpSameOpcodeHelper {
   unsigned getAltOpcode() const {
     return hasAltOp() ? AltOp.getOpcode() : getMainOpcode();
   }
-  SmallVector<Value *> getMainOperand(Instruction *I) const {
+  SmallVector<Value *> getOperand(Instruction *I) const {
     return MainOp.getOperand(I);
   }
-  SmallVector<Value *> getAltOperand(Instruction *I) const {
-    return AltOp.getOperand(I);
-  }
 };
 
 bool isConvertible(Instruction *I, Instruction *MainOp, Instruction *AltOp) {
@@ -1126,8 +1123,8 @@ convertTo(Instruction *I, Instruction *MainOp, Instruction *AltOp) {
   assert(I->isBinaryOp() && "Cannot convert the instruction.");
   BinOpSameOpcodeHelper Converter(I);
   if (Converter.add(I) && Converter.add(MainOp) && !Converter.hasAltOp())
-    return std::make_pair(MainOp, Converter.getMainOperand(MainOp));
-  return std::make_pair(AltOp, Converter.getAltOperand(AltOp));
+    return std::make_pair(MainOp, Converter.getOperand(MainOp));
+  return std::make_pair(AltOp, Converter.getOperand(AltOp));
 }
 
 /// Main data required for vectorization of instructions.
diff --git a/llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll b/llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll
new file mode 100644
index 0000000000000..6f27555aeb3f1
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -mtriple=x86_64-unknown-linux-gnu -passes=slp-vectorizer -S %s | FileCheck %s
+
+define void @test() {
+; CHECK-LABEL: @test(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 0, 0
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i64 0, 1
+; CHECK-NEXT:    [[TMP2:%.*]] = lshr i64 [[TMP1]], 0
+; CHECK-NEXT:    [[UMIN120:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP0]], i64 [[TMP2]])
+; CHECK-NEXT:    [[TMP3:%.*]] = sub i64 0, 0
+; CHECK-NEXT:    [[TMP4:%.*]] = lshr i64 [[TMP3]], 0
+; CHECK-NEXT:    [[UMIN122:%.*]] = call i64 @llvm.umin.i64(i64 [[UMIN120]], i64 [[TMP4]])
+; CHECK-NEXT:    [[TMP5:%.*]] = add i64 0, 1
+; CHECK-NEXT:    [[TMP6:%.*]] = lshr i64 [[TMP5]], 0
+; CHECK-NEXT:    [[UMIN123:%.*]] = call i64 @llvm.umin.i64(i64 [[UMIN122]], i64 [[TMP6]])
+; CHECK-NEXT:    [[UMIN124:%.*]] = call i64 @llvm.umin.i64(i64 [[UMIN123]], i64 0)
+; CHECK-NEXT:    ret void
+;
+entry:
+  %0 = mul i64 0, 0
+  %1 = lshr i64 %0, 0
+  %2 = sub i64 0, 1
+  %3 = lshr i64 %2, 0
+  %umin120 = call i64 @llvm.umin.i64(i64 %1, i64 %3)
+  %4 = sub i64 0, 0
+  %5 = lshr i64 %4, 0
+  %umin122 = call i64 @llvm.umin.i64(i64 %umin120, i64 %5)
+  %6 = add i64 0, 1
+  %7 = lshr i64 %6, 0
+  %umin123 = call i64 @llvm.umin.i64(i64 %umin122, i64 %7)
+  %umin124 = call i64 @llvm.umin.i64(i64 %umin123, i64 0)
+  ret void
+}
+
+declare i64 @llvm.umin.i64(i64, i64)

@llvmbot
Copy link
Member

llvmbot commented Mar 25, 2025

@llvm/pr-subscribers-vectorizers

Author: Han-Kuan Chen (HanKuanChen)

Changes

If the Converter has AltOp, that means I should be converted to AltOp
instead of MainOp (which is AltOp in BinOpSameOpcodeHelper).


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp (+3-6)
  • (added) llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll (+36)
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 72e18be9fe71e..6d96aedc5ff9f 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -1094,12 +1094,9 @@ class BinOpSameOpcodeHelper {
   unsigned getAltOpcode() const {
     return hasAltOp() ? AltOp.getOpcode() : getMainOpcode();
   }
-  SmallVector<Value *> getMainOperand(Instruction *I) const {
+  SmallVector<Value *> getOperand(Instruction *I) const {
     return MainOp.getOperand(I);
   }
-  SmallVector<Value *> getAltOperand(Instruction *I) const {
-    return AltOp.getOperand(I);
-  }
 };
 
 bool isConvertible(Instruction *I, Instruction *MainOp, Instruction *AltOp) {
@@ -1126,8 +1123,8 @@ convertTo(Instruction *I, Instruction *MainOp, Instruction *AltOp) {
   assert(I->isBinaryOp() && "Cannot convert the instruction.");
   BinOpSameOpcodeHelper Converter(I);
   if (Converter.add(I) && Converter.add(MainOp) && !Converter.hasAltOp())
-    return std::make_pair(MainOp, Converter.getMainOperand(MainOp));
-  return std::make_pair(AltOp, Converter.getAltOperand(AltOp));
+    return std::make_pair(MainOp, Converter.getOperand(MainOp));
+  return std::make_pair(AltOp, Converter.getOperand(AltOp));
 }
 
 /// Main data required for vectorization of instructions.
diff --git a/llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll b/llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll
new file mode 100644
index 0000000000000..6f27555aeb3f1
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll
@@ -0,0 +1,36 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -mtriple=x86_64-unknown-linux-gnu -passes=slp-vectorizer -S %s | FileCheck %s
+
+define void @test() {
+; CHECK-LABEL: @test(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[TMP0:%.*]] = lshr i64 0, 0
+; CHECK-NEXT:    [[TMP1:%.*]] = sub i64 0, 1
+; CHECK-NEXT:    [[TMP2:%.*]] = lshr i64 [[TMP1]], 0
+; CHECK-NEXT:    [[UMIN120:%.*]] = call i64 @llvm.umin.i64(i64 [[TMP0]], i64 [[TMP2]])
+; CHECK-NEXT:    [[TMP3:%.*]] = sub i64 0, 0
+; CHECK-NEXT:    [[TMP4:%.*]] = lshr i64 [[TMP3]], 0
+; CHECK-NEXT:    [[UMIN122:%.*]] = call i64 @llvm.umin.i64(i64 [[UMIN120]], i64 [[TMP4]])
+; CHECK-NEXT:    [[TMP5:%.*]] = add i64 0, 1
+; CHECK-NEXT:    [[TMP6:%.*]] = lshr i64 [[TMP5]], 0
+; CHECK-NEXT:    [[UMIN123:%.*]] = call i64 @llvm.umin.i64(i64 [[UMIN122]], i64 [[TMP6]])
+; CHECK-NEXT:    [[UMIN124:%.*]] = call i64 @llvm.umin.i64(i64 [[UMIN123]], i64 0)
+; CHECK-NEXT:    ret void
+;
+entry:
+  %0 = mul i64 0, 0
+  %1 = lshr i64 %0, 0
+  %2 = sub i64 0, 1
+  %3 = lshr i64 %2, 0
+  %umin120 = call i64 @llvm.umin.i64(i64 %1, i64 %3)
+  %4 = sub i64 0, 0
+  %5 = lshr i64 %4, 0
+  %umin122 = call i64 @llvm.umin.i64(i64 %umin120, i64 %5)
+  %6 = add i64 0, 1
+  %7 = lshr i64 %6, 0
+  %umin123 = call i64 @llvm.umin.i64(i64 %umin122, i64 %7)
+  %umin124 = call i64 @llvm.umin.i64(i64 %umin123, i64 0)
+  ret void
+}
+
+declare i64 @llvm.umin.i64(i64, i64)

@HanKuanChen HanKuanChen changed the title [SLP] Fix incorrect convert when interchangeable instruction is used. [SLP] Make getSameOpcode support interchangeable instructions. Mar 25, 2025
HanKuanChen and others added 3 commits March 25, 2025 02:59
…127450)

We use the term "interchangeable instructions" to refer to different
operators that have the same meaning (e.g., `add x, 0` is equivalent to
`mul x, 1`).
Non-constant values are not supported, as they may incur high costs with
little benefit.

---------

Co-authored-by: Alexey Bataev <[email protected]>
If the Converter has AltOp, that means I should be converted to AltOp
instead of MainOp (which is AltOp in BinOpSameOpcodeHelper).
@HanKuanChen HanKuanChen force-pushed the slp-BinOpSameOpcodeHelper branch from 120fb17 to 58f09d6 Compare March 25, 2025 10:08
Copy link

⚠️ undef deprecator found issues in your code. ⚠️

You can test this locally with the following command:
git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef[^a-zA-Z0-9_-]|UndefValue::get)' 07c82b1622de1c5c4329ffb769bef7fef1b07429 58f09d66f8b22bf52994c8f3e125f99506c96fe8 llvm/test/Transforms/SLPVectorizer/X86/BinOpSameOpcodeHelper.ll llvm/test/Transforms/SLPVectorizer/isOpcodeOrAlt.ll llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp llvm/test/Transforms/SLPVectorizer/AArch64/vec3-base.ll llvm/test/Transforms/SLPVectorizer/RISCV/reversed-strided-node-with-external-ptr.ll llvm/test/Transforms/SLPVectorizer/RISCV/vec3-base.ll llvm/test/Transforms/SLPVectorizer/X86/barriercall.ll llvm/test/Transforms/SLPVectorizer/X86/bottom-to-top-reorder.ll llvm/test/Transforms/SLPVectorizer/X86/buildvector-postpone-for-dependency.ll llvm/test/Transforms/SLPVectorizer/X86/bv-shuffle-mask.ll llvm/test/Transforms/SLPVectorizer/X86/extract-scalar-from-undef.ll llvm/test/Transforms/SLPVectorizer/X86/extractcost.ll llvm/test/Transforms/SLPVectorizer/X86/gathered-delayed-nodes-with-reused-user.ll llvm/test/Transforms/SLPVectorizer/X86/minbitwidth-drop-wrapping-flags.ll llvm/test/Transforms/SLPVectorizer/X86/multi-extracts-bv-combined.ll llvm/test/Transforms/SLPVectorizer/X86/non-scheduled-inst-reused-as-last-inst.ll llvm/test/Transforms/SLPVectorizer/X86/propagate_ir_flags.ll llvm/test/Transforms/SLPVectorizer/X86/reduced-val-vectorized-in-transform.ll llvm/test/Transforms/SLPVectorizer/X86/reorder_diamond_match.ll llvm/test/Transforms/SLPVectorizer/X86/shuffle-mask-emission.ll llvm/test/Transforms/SLPVectorizer/X86/vec3-base.ll llvm/test/Transforms/SLPVectorizer/X86/vect_copyable_in_binops.ll llvm/test/Transforms/SLPVectorizer/alternate-opcode-sindle-bv.ll llvm/test/Transforms/SLPVectorizer/resized-alt-shuffle-after-minbw.ll llvm/test/Transforms/SLPVectorizer/shuffle-mask-resized.ll

The following files introduce new uses of undef:

  • llvm/test/Transforms/SLPVectorizer/X86/extract-scalar-from-undef.ll

Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields undef. You should use poison values for placeholders instead.

In tests, avoid using undef and having tests that trigger undefined behavior. If you need an operand with some unimportant value, you can add a new argument to the function and use that instead.

For example, this is considered a bad practice:

define void @fn() {
  ...
  br i1 undef, ...
}

Please use the following instead:

define void @fn(i1 %cond) {
  ...
  br i1 %cond, ...
}

Please refer to the Undefined Behavior Manual for more information.

@HanKuanChen HanKuanChen merged commit 6e66cfe into llvm:main Mar 25, 2025
10 of 11 checks passed
@HanKuanChen HanKuanChen deleted the slp-BinOpSameOpcodeHelper branch March 25, 2025 11:46
@llvm-ci
Copy link
Collaborator

llvm-ci commented Mar 25, 2025

LLVM Buildbot has detected a new failure on builder lldb-aarch64-ubuntu running on linaro-lldb-aarch64-ubuntu while building llvm at step 6 "test".

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

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
PASS: lldb-api :: commands/target/dump/TestTargetDumpTypeSystem.py (215 of 2109)
PASS: lldb-api :: commands/target/create-deps/TestTargetCreateDeps.py (216 of 2109)
PASS: lldb-api :: commands/target/modules/lookup/TestImageLookupPCExpression.py (217 of 2109)
PASS: lldb-api :: commands/target/select/TestTargetSelect.py (218 of 2109)
PASS: lldb-api :: commands/settings/TestSettings.py (219 of 2109)
PASS: lldb-api :: commands/target/modules/search-paths/insert/TestTargetModulesSearchpathsInsert.py (220 of 2109)
PASS: lldb-api :: commands/target/stop-hook/delete/TestTargetStopHookDelete.py (221 of 2109)
PASS: lldb-api :: commands/target/stop-hook/disable/TestTargetStopHookDisable.py (222 of 2109)
PASS: lldb-api :: commands/target/stop-hook/enable/TestTargetStopHookEnable.py (223 of 2109)
PASS: lldb-api :: commands/target/stop-hooks/TestStopHooks.py (224 of 2109)
FAIL: lldb-api :: commands/thread/select/TestThreadSelect.py (225 of 2109)
******************** TEST 'lldb-api :: commands/thread/select/TestThreadSelect.py' FAILED ********************
Script:
--
/usr/bin/python3.10 /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib --env LLVM_INCLUDE_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/include --env LLVM_TOOLS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --arch aarch64 --build-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex --lldb-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/lldb --compiler /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/clang --dsymutil /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --lldb-obj-root /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb --lldb-libs-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/commands/thread/select -p TestThreadSelect.py
--
Exit Code: -11

Command Output (stdout):
--
lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision 6e66cfeeaec6f09a4454400e45d690457ecdd3de)
  clang revision 6e66cfeeaec6f09a4454400e45d690457ecdd3de
  llvm revision 6e66cfeeaec6f09a4454400e45d690457ecdd3de
Skipping the following test categories: ['libc++', 'dsym', 'gmodules', 'debugserver', 'objc']

--
Command Output (stderr):
--
UNSUPPORTED: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_invalid_arg_dsym (TestThreadSelect.TestCase) (test case does not fall in any category of interest for this run) 
PASS: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_invalid_arg_dwarf (TestThreadSelect.TestCase)
PASS: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_invalid_arg_dwo (TestThreadSelect.TestCase)
UNSUPPORTED: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_thread_select_tid_dsym (TestThreadSelect.TestCase) (test case does not fall in any category of interest for this run) 
PASS: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_thread_select_tid_dwarf (TestThreadSelect.TestCase)
PASS: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_thread_select_tid_dwo (TestThreadSelect.TestCase)
----------------------------------------------------------------------
Ran 6 tests in 0.974s

OK (skipped=2)

--

********************
PASS: lldb-api :: commands/thread/backtrace/TestThreadBacktraceRepeat.py (226 of 2109)
PASS: lldb-api :: commands/target/basic/TestTargetCommand.py (227 of 2109)
UNSUPPORTED: lldb-api :: commands/trace/TestTraceDumpFunctionCalls.py (228 of 2109)
UNSUPPORTED: lldb-api :: commands/trace/TestTraceDumpInfo.py (229 of 2109)
UNSUPPORTED: lldb-api :: commands/trace/TestTraceEvents.py (230 of 2109)
UNSUPPORTED: lldb-api :: commands/trace/TestTraceDumpInstructions.py (231 of 2109)
UNSUPPORTED: lldb-api :: commands/trace/TestTraceExport.py (232 of 2109)

@alexfh
Copy link
Contributor

alexfh commented Mar 28, 2025

We see a Clang crash after 8122bb9 and 6e66cfe (unfortunately, I don't know yet more precisely, which of the two is the culprit, but they were committed one after another). See 8122bb9#commitcomment-154471110

@alexfh
Copy link
Contributor

alexfh commented Mar 28, 2025

We see a Clang crash after 8122bb9 and 6e66cfe (unfortunately, I don't know yet more precisely, which of the two is the culprit, but they were committed one after another). See 8122bb9#commitcomment-154471110

It's addressed by the revert a2e5932.

HanKuanChen added a commit to HanKuanChen/llvm-project that referenced this pull request Apr 1, 2025
…132887)

We use the term "interchangeable instructions" to refer to different
operators that have the same meaning (e.g., `add x, 0` is equivalent to
`mul x, 1`).
Non-constant values are not supported, as they may incur high costs with
little benefit.

---------

Co-authored-by: Alexey Bataev <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants