This is an archive of the discontinued LLVM Phabricator instance.

[LICM] Simplify (X < A && X < B) into (X < MIN(A, B)) if MIN(A, B) is loop-invariant
ClosedPublic

Authored by mkazantsev on Feb 10 2023, 4:38 AM.

Details

Summary

We don't do this transform in InstCombine in general case for arbitrary values, because cost of
AND and 2 ICMP's isn't higher than of MIN and ICMP. However, LICM also has a notion
about the loop structure. This transform becomes profitable if A and B are loop-invariant and
X is not: by doing this, we can compute min outside the loop.

Diff Detail

Event Timeline

mkazantsev created this revision.Feb 10 2023, 4:38 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 10 2023, 4:38 AM
mkazantsev requested review of this revision.Feb 10 2023, 4:38 AM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 10 2023, 4:38 AM

My high level question would be whether this should be part of LICM. Basically, what we're doing here is a reassociation transform to expose a loop invariant, which can then be hoisted. This recently also came up in other contexts, e.g. D143631.

It looks like currently LoopInstSimplify is scheduled before LICM, so the operands might not have been hoisted yet. Having LICM rewrite expressions to enable hoisting would make sure that operands are hoisted first.

mkazantsev planned changes to this revision.Feb 20 2023, 1:38 AM

My high level question would be whether this should be part of LICM. Basically, what we're doing here is a reassociation transform to expose a loop invariant, which can then be hoisted. This recently also came up in other contexts, e.g. D143631.

It looks like currently LoopInstSimplify is scheduled before LICM, so the operands might not have been hoisted yet. Having LICM rewrite expressions to enable hoisting would make sure that operands are hoisted first.

Making this in LICM also makes sense. We have a different pipeline, so in our case this happens after LICM (and other passes) as a cleanup.

mkazantsev retitled this revision from [LoopInstSimplify] Simplify (X < A && X < B) into (X < MIN(A, B)) if MIN(A, B) is loop-invariant to [LICM] Simplify (X < A && X < B) into (X < MIN(A, B)) if MIN(A, B) is loop-invariant.
mkazantsev edited the summary of this revision. (Show Details)

Moved to LICM.

nikic requested changes to this revision.Mar 7 2023, 1:04 AM
nikic added inline comments.
llvm/lib/Transforms/Scalar/LICM.cpp
2426

This transform is not legal for logical and/or. Counter-proof: https://alive2.llvm.org/ce/z/7sVXdy Please limit this to just m_Or/m_And and add a test that logical case is not folded.

2430–2432
2441

Personally, I'd include swapping the invariant to the right in the initial patch. Otherwise the transform is fragile.

2469

ICmpInst::Predicate

2472

I think you need takeName() to avoid the unnecesary 1 suffix.

llvm/test/Transforms/LICM/min_max.ll
482–1210

Some missing negative tests:

  • Equality predicate
  • Multi-use comparison
  • Logical and/or (mentioned above)
  • Swapped operands (but better implement support...)
  • One icmp without invariant ops
  • Mismatched predicates (e.g. one ult one ule)
  • No common variant operand
This revision now requires changes to proceed.Mar 7 2023, 1:04 AM
nikic added a comment.Mar 7 2023, 1:05 AM

Note that there are two codegen tests that need to be updated.

mkazantsev added inline comments.Mar 9 2023, 7:10 PM
llvm/lib/Transforms/Scalar/LICM.cpp
2426

Good catch! Thanks for pointing out.

mkazantsev marked 5 inline comments as done.Mar 9 2023, 7:45 PM
mkazantsev added inline comments.Mar 9 2023, 9:19 PM
llvm/lib/Transforms/Scalar/LICM.cpp
2426

Logican and can be supported if RHS's are noundef. But I'll make it in a follow-up.

mkazantsev marked an inline comment as done.Mar 9 2023, 10:01 PM
mkazantsev added inline comments.
llvm/test/Transforms/LICM/min_max.ll
482–1210

Actually some of them can be optimized, I'll add both positive and negative tests for these situations.

mkazantsev marked an inline comment as done.Mar 9 2023, 10:01 PM

Addressed comments & rebased on top of new tests.

Removed some stray semicolons.

nikic accepted this revision.Mar 10 2023, 12:38 AM

LGTM

llvm/lib/Transforms/Scalar/LICM.cpp
2426

If RHS is undef, logical and will be converted to bitwise and by InstCombine. If we want to support the transform for logical and, we need to freeze the RHS.

2443

This TODO is already done :)

llvm/test/Transforms/LICM/min_max.ll
882

ule against constant is already canonicalized to ult by InstCombine, no need to handle here.

This revision is now accepted and ready to land.Mar 10 2023, 12:38 AM
mkazantsev added inline comments.Mar 10 2023, 2:07 AM
llvm/lib/Transforms/Scalar/LICM.cpp
2426

That's an option too.

2443

Indeed.

This revision was landed with ongoing or failed builds.Mar 10 2023, 2:37 AM
This revision was automatically updated to reflect the committed changes.
nikic added a comment.Mar 10 2023, 6:42 AM

This showed up as a small compile-time regression: http://llvm-compile-time-tracker.com/compare.php?from=946f8030b58d3d6975369c1b8e047385b86c7ab0&to=6b03ce374e0dc64868b4b6665056dfc3fda0e98f&stat=instructions:u I have no idea why, I don't see anything expensive in this patch.

nikic added a comment.Mar 10 2023, 7:17 AM

Looks like fetching the preheader is expensive. Compile-time regression should be addressed by https://github.com/llvm/llvm-project/commit/a7322a2171e99fe9c465c3e13c49563b19402ae0.

See assertion failures on this patch. https://storage.googleapis.com/fuchsia-artifacts/builds/8787007186219892481/build-debug/clang-crashreports/adler32-5bb726.c and https://storage.googleapis.com/fuchsia-artifacts/builds/8787007186219892481/build-debug/clang-crashreports/adler32-5bb726.sh for the reproducer. I tested locally and the failure goes away after reverting this change. Please take a look or revert if the fix will take a while.

clang: /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/DerivedTypes.h:725: Type *llvm::Type::getWithNewBitWidth(unsigned int) const: Assertion `isIntOrIntVectorTy() && "Original type expected to be a vector of integers or a scalar integer."' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: ./bin/clang -cc1 -triple aarch64-unknown-fuchsia -emit-obj -massembler-fatal-warnings -disable-free -clear-ast-before-backend -main-file-name adler32.c -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=non-leaf -ffp-contract=on -fno-rounding-math -mconstructor-aliases -ffreestanding -target-cpu generic -target-feature +v8a -target-feature +crc -target-feature -fp-armv8 -target-feature -crypto -target-feature -neon -target-feature +tpidr-el1 -target-feature -sha2 -target-feature -aes -target-feature +strict-align -target-feature +reserve-x20 -target-feature +fix-cortex-a53-835769 -target-abi aapcs -tune-cpu generic -Wunaligned-access -mllvm -treat-scalable-fixed-error-as-warning -debug-info-kind=constructor -dwarf-version=4 -debugger-tuning=gdb -mllvm -crash-diagnostics-dir=clang-crashreports -ffunction-sections -fdata-sections -fcoverage-compilation-dir=. -sys-header-deps -D _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1 -D ZX_ASSERT_LEVEL=2 -D LK_DEBUGLEVEL=2 -D WITH_FRAME_POINTERS=1 -O2 -Wno-strict-prototypes -Wall -Wextra -Wconversion -Wextra-semi -Wimplicit-fallthrough -Wnewline-eof -Wstrict-prototypes -Wwrite-strings -Wno-sign-conversion -Wno-unused-parameter -Wnonportable-system-include-path -Wno-type-limits -Wthread-safety -Werror -Wno-error=deprecated-declarations -Wformat=2 -Wmissing-declarations -Wvla -Wshadow -Wno-conversion -Wmissing-prototypes -std=c11 -fconst-strings -fdebug-compilation-dir=. -ferror-limit 19 -fvisibility=hidden -fsanitize=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound,shadow-call-stack -fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound -fno-sanitize-memory-param-retval -fno-sanitize-address-use-odr-indicator -fno-finite-loops -stack-protector 2 -ftrivial-auto-var-init=pattern -fno-signed-char -fgnuc-version=4.2.1 -fsized-deallocation -fcolor-diagnostics -vectorize-loops -vectorize-slp -debug-info-kind=constructor -target-feature +outline-atomics -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -x c adler32-5bb726.c
1.      <eof> parser at end of file
2.      Optimizer
 #0 0x0000564e541cc75d llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Support/Unix/Signals.inc:602:11
 #1 0x0000564e541ccbeb PrintStackTraceSignalHandler(void*) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Support/Unix/Signals.inc:676:1
 #2 0x0000564e541cb116 llvm::sys::RunSignalHandlers() /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Support/Signals.cpp:104:5
 #3 0x0000564e541cd1c5 SignalHandler(int) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Support/Unix/Signals.inc:413:1
 #4 0x00007f3ba1458f90 (/lib/x86_64-linux-gnu/libc.so.6+0x3bf90)
 #5 0x00007f3ba14a7ccc (/lib/x86_64-linux-gnu/libc.so.6+0x8accc)
 #6 0x00007f3ba1458ef2 raise (/lib/x86_64-linux-gnu/libc.so.6+0x3bef2)
 #7 0x00007f3ba1443472 abort (/lib/x86_64-linux-gnu/libc.so.6+0x26472)
 #8 0x00007f3ba1443395 (/lib/x86_64-linux-gnu/libc.so.6+0x26395)
 #9 0x00007f3ba1451df2 (/lib/x86_64-linux-gnu/libc.so.6+0x34df2)
#10 0x0000564e50ae14bd llvm::Type::getWithNewBitWidth(unsigned int) const /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/DerivedTypes.h:0:3
#11 0x0000564e50e5369f llvm::BasicTTIImplBase<llvm::AArch64TTIImpl>::getTypeBasedIntrinsicInstrCost(llvm::IntrinsicCostAttributes const&, llvm::TargetTransformInfo::TargetCostKind) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/CodeGen/BasicTTIImpl.h:1917:13
#12 0x0000564e50e4bd47 llvm::BasicTTIImplBase<llvm::AArch64TTIImpl>::getIntrinsicInstrCost(llvm::IntrinsicCostAttributes const&, llvm::TargetTransformInfo::TargetCostKind) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/CodeGen/BasicTTIImpl.h:1691:21
#13 0x0000564e50e39ca0 llvm::AArch64TTIImpl::getIntrinsicInstrCost(llvm::IntrinsicCostAttributes const&, llvm::TargetTransformInfo::TargetCostKind) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp:522:17
#14 0x0000564e50d420e4 llvm::TargetTransformInfoImplCRTPBase<llvm::AArch64TTIImpl>::getInstructionCost(llvm::User const*, llvm::ArrayRef<llvm::Value const*>, llvm::TargetTransformInfo::TargetCostKind) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h:1056:25
#15 0x0000564e50d3e881 llvm::TargetTransformInfo::Model<llvm::AArch64TTIImpl>::getInstructionCost(llvm::User const*, llvm::ArrayRef<llvm::Value const*>, llvm::TargetTransformInfo::TargetCostKind) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/Analysis/TargetTransformInfo.h:1988:17
#16 0x0000564e53282dce llvm::TargetTransformInfo::getInstructionCost(llvm::User const*, llvm::ArrayRef<llvm::Value const*>, llvm::TargetTransformInfo::TargetCostKind) const /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Analysis/TargetTransformInfo.cpp:233:35
#17 0x0000564e51125fda llvm::TargetTransformInfo::getInstructionCost(llvm::User const*, llvm::TargetTransformInfo::TargetCostKind) const /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/Analysis/TargetTransformInfo.h:339:12
#18 0x0000564e527a3b19 isFreeInLoop(llvm::Instruction const&, llvm::Loop const*, llvm::TargetTransformInfo const*) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Transforms/Scalar/LICM.cpp:1352:12
#19 0x0000564e5279c7f5 isNotUsedOrFreeInLoop(llvm::Instruction const&, llvm::Loop const*, llvm::LoopSafetyInfo const*, llvm::TargetTransformInfo*, bool&, bool) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Transforms/Scalar/LICM.cpp:1385:8
#20 0x0000564e5279c5a9 llvm::sinkRegion(llvm::DomTreeNodeBase<llvm::BasicBlock>*, llvm::AAResults*, llvm::LoopInfo*, llvm::DominatorTree*, llvm::TargetLibraryInfo*, llvm::TargetTransformInfo*, llvm::Loop*, llvm::MemorySSAUpdater&, llvm::ICFLoopSafetyInfo*, llvm::SinkAndHoistLICMFlags&, llvm::OptimizationRemarkEmitter*, llvm::Loop*) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Transforms/Scalar/LICM.cpp:594:76
#21 0x0000564e5279b4e1 (anonymous namespace)::LoopInvariantCodeMotion::runOnLoop(llvm::Loop*, llvm::AAResults*, llvm::LoopInfo*, llvm::DominatorTree*, llvm::AssumptionCache*, llvm::TargetLibraryInfo*, llvm::TargetTransformInfo*, llvm::ScalarEvolution*, llvm::MemorySSA*, llvm::OptimizationRemarkEmitter*, bool) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Transforms/Scalar/LICM.cpp:458:15
#22 0x0000564e5279b1c8 llvm::LICMPass::run(llvm::Loop&, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>&, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Transforms/Scalar/LICM.cpp:287:7
#23 0x0000564e504ca9a4 llvm::detail::PassModel<llvm::Loop, llvm::LICMPass, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&>::run(llvm::Loop&, llvm::AnalysisManager<llvm::Loop, llvm::LoopStandardAnalysisResults&>&, llvm::LoopStandardAnalysisResults&, llvm::LPMUpdater&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:89:17
#24 0x0000564e528160a8 llvm::FunctionToLoopPassAdaptor::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/Transforms/Scalar/LoopPassManager.cpp:308:17
#25 0x0000564e504c74f4 llvm::detail::PassModel<llvm::Function, llvm::FunctionToLoopPassAdaptor, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:89:17
#26 0x0000564e53cd9567 llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/PassManager.h:521:33
#27 0x0000564e4b7f0b34 llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:89:17
#28 0x0000564e53cd8305 llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/lib/IR/PassManager.cpp:124:38
#29 0x0000564e4b7f73d4 llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/PassManagerInternal.h:89:17
#30 0x0000564e53cd8887 llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) /usr/local/google/home/abrachet/Developer/llvm-project/llvm/include/llvm/IR/PassManager.h:521:33
#31 0x0000564e4b7d1bfb (anonymous namespace)::EmitAssemblyHelper::RunOptimizationPipeline(clang::BackendAction, std::__2::unique_ptr<llvm::raw_pwrite_stream, std::__2::default_delete<llvm::raw_pwrite_stream>>&, std::__2::unique_ptr<llvm::ToolOutputFile, std::__2::default_delete<llvm::ToolOutputFile>>&) /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/CodeGen/BackendUtil.cpp:1042:5
#32 0x0000564e4b7ccf0f (anonymous namespace)::EmitAssemblyHelper::EmitAssembly(clang::BackendAction, std::__2::unique_ptr<llvm::raw_pwrite_stream, std::__2::default_delete<llvm::raw_pwrite_stream>>) /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/CodeGen/BackendUtil.cpp:1099:3
#33 0x0000564e4b7cc434 clang::EmitBackendOutput(clang::DiagnosticsEngine&, clang::HeaderSearchOptions const&, clang::CodeGenOptions const&, clang::TargetOptions const&, clang::LangOptions const&, llvm::StringRef, llvm::Module*, clang::BackendAction, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::__2::unique_ptr<llvm::raw_pwrite_stream, std::__2::default_delete<llvm::raw_pwrite_stream>>) /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/CodeGen/BackendUtil.cpp:1265:17
#34 0x0000564e4bcb8d2a clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/CodeGen/CodeGenAction.cpp:383:7
#35 0x0000564e4db7b1c3 clang::ParseAST(clang::Sema&, bool, bool) /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/Parse/ParseAST.cpp:182:12
#36 0x0000564e4bad5ed6 clang::ASTFrontendAction::ExecuteAction() /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/Frontend/FrontendAction.cpp:1170:1
#37 0x0000564e4bcb4584 clang::CodeGenAction::ExecuteAction() /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/CodeGen/CodeGenAction.cpp:1173:5
#38 0x0000564e4bad591c clang::FrontendAction::Execute() /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/Frontend/FrontendAction.cpp:1062:7
#39 0x0000564e4ba001c8 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/Frontend/CompilerInstance.cpp:1049:23
#40 0x0000564e4bca42c7 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) /usr/local/google/home/abrachet/Developer/llvm-project/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp:264:8
#41 0x0000564e4a90d341 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) /usr/local/google/home/abrachet/Developer/llvm-project/clang/tools/driver/cc1_main.cpp:251:13
#42 0x0000564e4a902442 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) /usr/local/google/home/abrachet/Developer/llvm-project/clang/tools/driver/driver.cpp:363:5
#43 0x0000564e4a9011c7 clang_main(int, char**, llvm::ToolContext const&) /usr/local/google/home/abrachet/Developer/llvm-project/clang/tools/driver/driver.cpp:441:5
#44 0x0000564e4abd5ec3 findTool(int, char**, char const*) /usr/local/google/home/abrachet/Developer/llvm-project/build/tools/llvm-driver/LLVMDriverTools.def:6:1
#45 0x0000564e4abd5ab9 main /usr/local/google/home/abrachet/Developer/llvm-project/llvm/tools/llvm-driver/llvm-driver.cpp:83:35
#46 0x00007f3ba144418a (/lib/x86_64-linux-gnu/libc.so.6+0x2718a)
#47 0x00007f3ba1444245 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x27245)
#48 0x0000564e4a868129 _start (./bin/clang+0x5480129)