[llvm] [RISCV] Expand memcmp/bcmp for statically-aligned pointers on strict-align targets (PR #209738)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 15 04:55:22 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-backend-risc-v
Author: Pengcheng Wang (wangpc-pp)
<details>
<summary>Changes</summary>
`RISCVTTIImpl::enableMemCmpExpansion` previously disabled `memcmp/bcmp`
expansion entirely when the target does not support unaligned scalar
memory accesses. This is more conservative than necessary: when both
pointers are statically known to be sufficiently aligned (a common
case, e.g. comparing two naturally-aligned i32 values), the expansion
would only ever emit naturally aligned loads, which are fine on
strict-align targets.
This PR adds an alignment-aware knob `RequireNaturalAlignment` to
`MemCmpExpansionOptions`. When set, `MemCmpExpansion` filters the
available load sizes per call site against the statically-known
common alignment of both pointers, keeping only power-of-two sizes
not exceeding that alignment, and falls back to the libcall when
nothing fits.
Because the greedy load sequence only places a load of size `S` at an
offset that is a multiple of `S`, such loads are guaranteed to be
naturally aligned.
Overlapping loads and merged (non-power-of-two) tail expansions are
disabled in this mode. The filtering is shared by all strict-align
targets rather than reimplemented per target.
RISCV opts in by setting `RequireNaturalAlignment` when unaligned
scalar memory is not supported.
Fixes #<!-- -->209511.
Assisted-by: TRAE CLI (DeepSeek V4 Pro)
---
Patch is 32.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/209738.diff
5 Files Affected:
- (modified) llvm/include/llvm/Analysis/TargetTransformInfo.h (+8)
- (modified) llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp (+7-4)
- (modified) llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp (+22)
- (added) llvm/test/Transforms/ExpandMemCmp/RISCV/lit.local.cfg (+2)
- (added) llvm/test/Transforms/ExpandMemCmp/RISCV/memcmp-strict-align.ll (+445)
``````````diff
diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h
index 10c0509460b95..dac9e1e4238ce 100644
--- a/llvm/include/llvm/Analysis/TargetTransformInfo.h
+++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h
@@ -1132,6 +1132,14 @@ class TargetTransformInfo {
// requires all loads in LoadSizes to be doable in an unaligned way.
bool AllowOverlappingLoads = false;
+ // Set to true if the expansion may only emit naturally aligned loads.
+ // Strict-alignment targets that cannot perform unaligned scalar loads can
+ // still opt into expansion by setting this flag: MemCmpExpansion then only
+ // uses load sizes covered by the statically-known alignment of both
+ // pointers at the call site, falling back to the libcall when no load size
+ // fits. Should not be combined with AllowOverlappingLoads.
+ bool RequireNaturalAlignment = false;
+
// Sometimes, the amount of data that needs to be compared is smaller than
// the standard register size, but it cannot be loaded with just one load
// instruction. For example, if the size of the memory comparison is 6
diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
index a50c1664ad5a1..ffd397e6be478 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
@@ -3630,14 +3630,17 @@ bool RISCVTTIImpl::isProfitableToSinkOperands(
RISCVTTIImpl::TTI::MemCmpExpansionOptions
RISCVTTIImpl::enableMemCmpExpansion(bool OptSize, bool IsZeroCmp) const {
TTI::MemCmpExpansionOptions Options;
- // TODO: Enable expansion when unaligned access is not supported after we fix
- // issues in ExpandMemcmp.
- if (!ST->enableUnalignedScalarMem())
- return Options;
if (!ST->hasStdExtZbb() && !ST->hasStdExtZbkb() && !IsZeroCmp)
return Options;
+ // If the target does not support unaligned scalar memory access, expansion is
+ // still possible when both pointers are statically known to be sufficiently
+ // aligned. ExpandMemCmp will restrict the load sizes below to the ones
+ // covered by the known per-call-site alignment and fall back to the libcall
+ // when none fits.
+ Options.RequireNaturalAlignment = !ST->enableUnalignedScalarMem();
+
Options.AllowOverlappingLoads = true;
Options.MaxNumLoads = TLI->getMaxExpandSizeMemcmp(OptSize);
Options.NumLoadsPerBlock = Options.MaxNumLoads;
diff --git a/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp b/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp
index fe55e0947ac82..19cda1fbba7e0 100644
--- a/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp
+++ b/llvm/lib/Transforms/Scalar/ExpandMemCmp.cpp
@@ -860,6 +860,28 @@ static bool expandMemCmp(CallInst *CI, const TargetTransformInfo *TTI,
if (!OptForSize && MaxLoadsPerMemcmp.getNumOccurrences())
Options.MaxNumLoads = MaxLoadsPerMemcmp;
+ if (Options.RequireNaturalAlignment) {
+ // The target can only perform naturally aligned loads. Keep the load sizes
+ // that are powers of two and no larger than the statically known alignment
+ // of both pointers. Because the greedy load sequence only places a load of
+ // size S at an offset that is a multiple of S, a power-of-two load that
+ // does not exceed the base alignment is guaranteed to be naturally aligned.
+ // Overlapping loads and merged tail expansions can produce unaligned or
+ // non-power-of-two accesses, so they are not used in this mode.
+ Options.AllowOverlappingLoads = false;
+ Options.AllowedTailExpansions.clear();
+ const Align LhsAlign = CI->getArgOperand(0)->getPointerAlignment(*DL);
+ const Align RhsAlign = CI->getArgOperand(1)->getPointerAlignment(*DL);
+ const uint64_t MinAlign = std::min(LhsAlign.value(), RhsAlign.value());
+ llvm::erase_if(Options.LoadSizes, [&](unsigned LoadSize) {
+ return LoadSize > MinAlign || !isPowerOf2_64(LoadSize);
+ });
+ // If only single-byte loads survive, inlining a byte-wise comparison offers
+ // no benefit over the library call, so leave it as a libcall.
+ if (Options.LoadSizes.empty() || Options.LoadSizes.front() == 1)
+ return false;
+ }
+
MemCmpExpansion Expansion(CI, SizeVal, Options, IsUsedForZeroCmp, *DL, DTU);
// Don't expand if this will require more loads than desired by the target.
diff --git a/llvm/test/Transforms/ExpandMemCmp/RISCV/lit.local.cfg b/llvm/test/Transforms/ExpandMemCmp/RISCV/lit.local.cfg
new file mode 100644
index 0000000000000..17351748513d9
--- /dev/null
+++ b/llvm/test/Transforms/ExpandMemCmp/RISCV/lit.local.cfg
@@ -0,0 +1,2 @@
+if not "RISCV" in config.root.targets:
+ config.unsupported = True
diff --git a/llvm/test/Transforms/ExpandMemCmp/RISCV/memcmp-strict-align.ll b/llvm/test/Transforms/ExpandMemCmp/RISCV/memcmp-strict-align.ll
new file mode 100644
index 0000000000000..9181326edcd6b
--- /dev/null
+++ b/llvm/test/Transforms/ExpandMemCmp/RISCV/memcmp-strict-align.ll
@@ -0,0 +1,445 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: sed 's/iXLen/i32/g' %s | opt -S -passes=expand-memcmp -mtriple=riscv32 -mattr=+m | FileCheck %s --check-prefixes=RV32
+; RUN: sed 's/iXLen/i64/g' %s | opt -S -passes=expand-memcmp -mtriple=riscv64 -mattr=+m | FileCheck %s --check-prefixes=RV64
+; RUN: sed 's/iXLen/i32/g' %s | opt -S -passes=expand-memcmp -mtriple=riscv32 -mattr=+m,+unaligned-scalar-mem | FileCheck %s --check-prefixes=RV32-UNALIGNED
+; RUN: sed 's/iXLen/i64/g' %s | opt -S -passes=expand-memcmp -mtriple=riscv64 -mattr=+m,+unaligned-scalar-mem | FileCheck %s --check-prefixes=RV64-UNALIGNED
+
+; On strict-alignment targets memcmp/bcmp is still expanded when both pointers
+; are statically known to be sufficiently aligned. The load sizes are limited
+; to the ones covered by the known alignment; when no load size fits the call
+; is left as a libcall.
+
+declare i32 @bcmp(ptr, ptr, iXLen)
+declare i32 @memcmp(ptr, ptr, iXLen)
+
+; Aligned U256 equality (the motivating case): expands into naturally aligned
+; loads even though the target cannot do unaligned scalar accesses.
+define i1 @bcmp_size_32_align_8(ptr align 8 %a, ptr align 8 %b) {
+; RV32-LABEL: define i1 @bcmp_size_32_align_8(
+; RV32-SAME: ptr align 8 [[A:%.*]], ptr align 8 [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32-NEXT: [[TMP1:%.*]] = load i32, ptr [[A]], align 8
+; RV32-NEXT: [[TMP2:%.*]] = load i32, ptr [[B]], align 8
+; RV32-NEXT: [[TMP3:%.*]] = xor i32 [[TMP1]], [[TMP2]]
+; RV32-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[A]], i64 4
+; RV32-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[B]], i64 4
+; RV32-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP4]], align 4
+; RV32-NEXT: [[TMP7:%.*]] = load i32, ptr [[TMP5]], align 4
+; RV32-NEXT: [[TMP8:%.*]] = xor i32 [[TMP6]], [[TMP7]]
+; RV32-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[A]], i64 8
+; RV32-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[B]], i64 8
+; RV32-NEXT: [[TMP11:%.*]] = load i32, ptr [[TMP9]], align 8
+; RV32-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP10]], align 8
+; RV32-NEXT: [[TMP13:%.*]] = xor i32 [[TMP11]], [[TMP12]]
+; RV32-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[A]], i64 12
+; RV32-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[B]], i64 12
+; RV32-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP14]], align 4
+; RV32-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP15]], align 4
+; RV32-NEXT: [[TMP18:%.*]] = xor i32 [[TMP16]], [[TMP17]]
+; RV32-NEXT: [[TMP19:%.*]] = getelementptr i8, ptr [[A]], i64 16
+; RV32-NEXT: [[TMP20:%.*]] = getelementptr i8, ptr [[B]], i64 16
+; RV32-NEXT: [[TMP21:%.*]] = load i32, ptr [[TMP19]], align 8
+; RV32-NEXT: [[TMP22:%.*]] = load i32, ptr [[TMP20]], align 8
+; RV32-NEXT: [[TMP23:%.*]] = xor i32 [[TMP21]], [[TMP22]]
+; RV32-NEXT: [[TMP24:%.*]] = getelementptr i8, ptr [[A]], i64 20
+; RV32-NEXT: [[TMP25:%.*]] = getelementptr i8, ptr [[B]], i64 20
+; RV32-NEXT: [[TMP26:%.*]] = load i32, ptr [[TMP24]], align 4
+; RV32-NEXT: [[TMP27:%.*]] = load i32, ptr [[TMP25]], align 4
+; RV32-NEXT: [[TMP28:%.*]] = xor i32 [[TMP26]], [[TMP27]]
+; RV32-NEXT: [[TMP29:%.*]] = getelementptr i8, ptr [[A]], i64 24
+; RV32-NEXT: [[TMP30:%.*]] = getelementptr i8, ptr [[B]], i64 24
+; RV32-NEXT: [[TMP31:%.*]] = load i32, ptr [[TMP29]], align 8
+; RV32-NEXT: [[TMP32:%.*]] = load i32, ptr [[TMP30]], align 8
+; RV32-NEXT: [[TMP33:%.*]] = xor i32 [[TMP31]], [[TMP32]]
+; RV32-NEXT: [[TMP34:%.*]] = getelementptr i8, ptr [[A]], i64 28
+; RV32-NEXT: [[TMP35:%.*]] = getelementptr i8, ptr [[B]], i64 28
+; RV32-NEXT: [[TMP36:%.*]] = load i32, ptr [[TMP34]], align 4
+; RV32-NEXT: [[TMP37:%.*]] = load i32, ptr [[TMP35]], align 4
+; RV32-NEXT: [[TMP38:%.*]] = xor i32 [[TMP36]], [[TMP37]]
+; RV32-NEXT: [[TMP39:%.*]] = or i32 [[TMP3]], [[TMP8]]
+; RV32-NEXT: [[TMP40:%.*]] = or i32 [[TMP13]], [[TMP18]]
+; RV32-NEXT: [[TMP41:%.*]] = or i32 [[TMP23]], [[TMP28]]
+; RV32-NEXT: [[TMP42:%.*]] = or i32 [[TMP33]], [[TMP38]]
+; RV32-NEXT: [[TMP43:%.*]] = or i32 [[TMP39]], [[TMP40]]
+; RV32-NEXT: [[TMP44:%.*]] = or i32 [[TMP41]], [[TMP42]]
+; RV32-NEXT: [[TMP45:%.*]] = or i32 [[TMP43]], [[TMP44]]
+; RV32-NEXT: [[TMP46:%.*]] = icmp ne i32 [[TMP45]], 0
+; RV32-NEXT: [[TMP47:%.*]] = zext i1 [[TMP46]] to i32
+; RV32-NEXT: [[Z:%.*]] = icmp eq i32 [[TMP47]], 0
+; RV32-NEXT: ret i1 [[Z]]
+;
+; RV64-LABEL: define i1 @bcmp_size_32_align_8(
+; RV64-SAME: ptr align 8 [[A:%.*]], ptr align 8 [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV64-NEXT: [[TMP1:%.*]] = load i64, ptr [[A]], align 8
+; RV64-NEXT: [[TMP2:%.*]] = load i64, ptr [[B]], align 8
+; RV64-NEXT: [[TMP3:%.*]] = xor i64 [[TMP1]], [[TMP2]]
+; RV64-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[A]], i64 8
+; RV64-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[B]], i64 8
+; RV64-NEXT: [[TMP6:%.*]] = load i64, ptr [[TMP4]], align 8
+; RV64-NEXT: [[TMP7:%.*]] = load i64, ptr [[TMP5]], align 8
+; RV64-NEXT: [[TMP8:%.*]] = xor i64 [[TMP6]], [[TMP7]]
+; RV64-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[A]], i64 16
+; RV64-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[B]], i64 16
+; RV64-NEXT: [[TMP11:%.*]] = load i64, ptr [[TMP9]], align 8
+; RV64-NEXT: [[TMP12:%.*]] = load i64, ptr [[TMP10]], align 8
+; RV64-NEXT: [[TMP13:%.*]] = xor i64 [[TMP11]], [[TMP12]]
+; RV64-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[A]], i64 24
+; RV64-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[B]], i64 24
+; RV64-NEXT: [[TMP16:%.*]] = load i64, ptr [[TMP14]], align 8
+; RV64-NEXT: [[TMP17:%.*]] = load i64, ptr [[TMP15]], align 8
+; RV64-NEXT: [[TMP18:%.*]] = xor i64 [[TMP16]], [[TMP17]]
+; RV64-NEXT: [[TMP19:%.*]] = or i64 [[TMP3]], [[TMP8]]
+; RV64-NEXT: [[TMP20:%.*]] = or i64 [[TMP13]], [[TMP18]]
+; RV64-NEXT: [[TMP21:%.*]] = or i64 [[TMP19]], [[TMP20]]
+; RV64-NEXT: [[TMP22:%.*]] = icmp ne i64 [[TMP21]], 0
+; RV64-NEXT: [[TMP23:%.*]] = zext i1 [[TMP22]] to i32
+; RV64-NEXT: [[Z:%.*]] = icmp eq i32 [[TMP23]], 0
+; RV64-NEXT: ret i1 [[Z]]
+;
+; RV32-UNALIGNED-LABEL: define i1 @bcmp_size_32_align_8(
+; RV32-UNALIGNED-SAME: ptr align 8 [[A:%.*]], ptr align 8 [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV32-UNALIGNED-NEXT: [[TMP1:%.*]] = load i32, ptr [[A]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP2:%.*]] = load i32, ptr [[B]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP3:%.*]] = xor i32 [[TMP1]], [[TMP2]]
+; RV32-UNALIGNED-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[A]], i64 4
+; RV32-UNALIGNED-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[B]], i64 4
+; RV32-UNALIGNED-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP4]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP7:%.*]] = load i32, ptr [[TMP5]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP8:%.*]] = xor i32 [[TMP6]], [[TMP7]]
+; RV32-UNALIGNED-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[A]], i64 8
+; RV32-UNALIGNED-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[B]], i64 8
+; RV32-UNALIGNED-NEXT: [[TMP11:%.*]] = load i32, ptr [[TMP9]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP10]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP13:%.*]] = xor i32 [[TMP11]], [[TMP12]]
+; RV32-UNALIGNED-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[A]], i64 12
+; RV32-UNALIGNED-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[B]], i64 12
+; RV32-UNALIGNED-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP14]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP15]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP18:%.*]] = xor i32 [[TMP16]], [[TMP17]]
+; RV32-UNALIGNED-NEXT: [[TMP19:%.*]] = getelementptr i8, ptr [[A]], i64 16
+; RV32-UNALIGNED-NEXT: [[TMP20:%.*]] = getelementptr i8, ptr [[B]], i64 16
+; RV32-UNALIGNED-NEXT: [[TMP21:%.*]] = load i32, ptr [[TMP19]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP22:%.*]] = load i32, ptr [[TMP20]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP23:%.*]] = xor i32 [[TMP21]], [[TMP22]]
+; RV32-UNALIGNED-NEXT: [[TMP24:%.*]] = getelementptr i8, ptr [[A]], i64 20
+; RV32-UNALIGNED-NEXT: [[TMP25:%.*]] = getelementptr i8, ptr [[B]], i64 20
+; RV32-UNALIGNED-NEXT: [[TMP26:%.*]] = load i32, ptr [[TMP24]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP27:%.*]] = load i32, ptr [[TMP25]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP28:%.*]] = xor i32 [[TMP26]], [[TMP27]]
+; RV32-UNALIGNED-NEXT: [[TMP29:%.*]] = getelementptr i8, ptr [[A]], i64 24
+; RV32-UNALIGNED-NEXT: [[TMP30:%.*]] = getelementptr i8, ptr [[B]], i64 24
+; RV32-UNALIGNED-NEXT: [[TMP31:%.*]] = load i32, ptr [[TMP29]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP32:%.*]] = load i32, ptr [[TMP30]], align 8
+; RV32-UNALIGNED-NEXT: [[TMP33:%.*]] = xor i32 [[TMP31]], [[TMP32]]
+; RV32-UNALIGNED-NEXT: [[TMP34:%.*]] = getelementptr i8, ptr [[A]], i64 28
+; RV32-UNALIGNED-NEXT: [[TMP35:%.*]] = getelementptr i8, ptr [[B]], i64 28
+; RV32-UNALIGNED-NEXT: [[TMP36:%.*]] = load i32, ptr [[TMP34]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP37:%.*]] = load i32, ptr [[TMP35]], align 4
+; RV32-UNALIGNED-NEXT: [[TMP38:%.*]] = xor i32 [[TMP36]], [[TMP37]]
+; RV32-UNALIGNED-NEXT: [[TMP39:%.*]] = or i32 [[TMP3]], [[TMP8]]
+; RV32-UNALIGNED-NEXT: [[TMP40:%.*]] = or i32 [[TMP13]], [[TMP18]]
+; RV32-UNALIGNED-NEXT: [[TMP41:%.*]] = or i32 [[TMP23]], [[TMP28]]
+; RV32-UNALIGNED-NEXT: [[TMP42:%.*]] = or i32 [[TMP33]], [[TMP38]]
+; RV32-UNALIGNED-NEXT: [[TMP43:%.*]] = or i32 [[TMP39]], [[TMP40]]
+; RV32-UNALIGNED-NEXT: [[TMP44:%.*]] = or i32 [[TMP41]], [[TMP42]]
+; RV32-UNALIGNED-NEXT: [[TMP45:%.*]] = or i32 [[TMP43]], [[TMP44]]
+; RV32-UNALIGNED-NEXT: [[TMP46:%.*]] = icmp ne i32 [[TMP45]], 0
+; RV32-UNALIGNED-NEXT: [[TMP47:%.*]] = zext i1 [[TMP46]] to i32
+; RV32-UNALIGNED-NEXT: [[Z:%.*]] = icmp eq i32 [[TMP47]], 0
+; RV32-UNALIGNED-NEXT: ret i1 [[Z]]
+;
+; RV64-UNALIGNED-LABEL: define i1 @bcmp_size_32_align_8(
+; RV64-UNALIGNED-SAME: ptr align 8 [[A:%.*]], ptr align 8 [[B:%.*]]) #[[ATTR0:[0-9]+]] {
+; RV64-UNALIGNED-NEXT: [[TMP1:%.*]] = load i64, ptr [[A]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP2:%.*]] = load i64, ptr [[B]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP3:%.*]] = xor i64 [[TMP1]], [[TMP2]]
+; RV64-UNALIGNED-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[A]], i64 8
+; RV64-UNALIGNED-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[B]], i64 8
+; RV64-UNALIGNED-NEXT: [[TMP6:%.*]] = load i64, ptr [[TMP4]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP7:%.*]] = load i64, ptr [[TMP5]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP8:%.*]] = xor i64 [[TMP6]], [[TMP7]]
+; RV64-UNALIGNED-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[A]], i64 16
+; RV64-UNALIGNED-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[B]], i64 16
+; RV64-UNALIGNED-NEXT: [[TMP11:%.*]] = load i64, ptr [[TMP9]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP12:%.*]] = load i64, ptr [[TMP10]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP13:%.*]] = xor i64 [[TMP11]], [[TMP12]]
+; RV64-UNALIGNED-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[A]], i64 24
+; RV64-UNALIGNED-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[B]], i64 24
+; RV64-UNALIGNED-NEXT: [[TMP16:%.*]] = load i64, ptr [[TMP14]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP17:%.*]] = load i64, ptr [[TMP15]], align 8
+; RV64-UNALIGNED-NEXT: [[TMP18:%.*]] = xor i64 [[TMP16]], [[TMP17]]
+; RV64-UNALIGNED-NEXT: [[TMP19:%.*]] = or i64 [[TMP3]], [[TMP8]]
+; RV64-UNALIGNED-NEXT: [[TMP20:%.*]] = or i64 [[TMP13]], [[TMP18]]
+; RV64-UNALIGNED-NEXT: [[TMP21:%.*]] = or i64 [[TMP19]], [[TMP20]]
+; RV64-UNALIGNED-NEXT: [[TMP22:%.*]] = icmp ne i64 [[TMP21]], 0
+; RV64-UNALIGNED-NEXT: [[TMP23:%.*]] = zext i1 [[TMP22]] to i32
+; RV64-UNALIGNED-NEXT: [[Z:%.*]] = icmp eq i32 [[TMP23]], 0
+; RV64-UNALIGNED-NEXT: ret i1 [[Z]]
+;
+ %r = call i32 @bcmp(ptr %a, ptr %b, iXLen 32)
+ %z = icmp eq i32 %r, 0
+ ret i1 %z
+}
+
+; No known alignment: on strict-alignment targets nothing but i8 loads would be
+; legal, so the libcall is kept.
+define i1 @bcmp_size_32_align_1(ptr %a, ptr %b) {
+; RV32-LABEL: define i1 @bcmp_size_32_align_1(
+; RV32-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0]] {
+; RV32-NEXT: [[R:%.*]] = call i32 @bcmp(ptr [[A]], ptr [[B]], i32 32)
+; RV32-NEXT: [[Z:%.*]] = icmp eq i32 [[R]], 0
+; RV32-NEXT: ret i1 [[Z]]
+;
+; RV64-LABEL: define i1 @bcmp_size_32_align_1(
+; RV64-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0]] {
+; RV64-NEXT: [[R:%.*]] = call i32 @bcmp(ptr [[A]], ptr [[B]], i64 32)
+; RV64-NEXT: [[Z:%.*]] = icmp eq i32 [[R]], 0
+; RV64-NEXT: ret i1 [[Z]]
+;
+; RV32-UNALIGNED-LABEL: define i1 @bcmp_size_32_align_1(
+; RV32-UNALIGNED-SAME: ptr [[A:%.*]], ptr [[B:%.*]]) #[[ATTR0]] {
+; RV32-UNALIGNED-NEXT: [[TMP1:%.*]] = load i32, ptr [[A]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP2:%.*]] = load i32, ptr [[B]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP3:%.*]] = xor i32 [[TMP1]], [[TMP2]]
+; RV32-UNALIGNED-NEXT: [[TMP4:%.*]] = getelementptr i8, ptr [[A]], i64 4
+; RV32-UNALIGNED-NEXT: [[TMP5:%.*]] = getelementptr i8, ptr [[B]], i64 4
+; RV32-UNALIGNED-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP4]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP7:%.*]] = load i32, ptr [[TMP5]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP8:%.*]] = xor i32 [[TMP6]], [[TMP7]]
+; RV32-UNALIGNED-NEXT: [[TMP9:%.*]] = getelementptr i8, ptr [[A]], i64 8
+; RV32-UNALIGNED-NEXT: [[TMP10:%.*]] = getelementptr i8, ptr [[B]], i64 8
+; RV32-UNALIGNED-NEXT: [[TMP11:%.*]] = load i32, ptr [[TMP9]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP10]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP13:%.*]] = xor i32 [[TMP11]], [[TMP12]]
+; RV32-UNALIGNED-NEXT: [[TMP14:%.*]] = getelementptr i8, ptr [[A]], i64 12
+; RV32-UNALIGNED-NEXT: [[TMP15:%.*]] = getelementptr i8, ptr [[B]], i64 12
+; RV32-UNALIGNED-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP14]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP15]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP18:%.*]] = xor i32 [[TMP16]], [[TMP17]]
+; RV32-UNALIGNED-NEXT: [[TMP19:%.*]] = getelementptr i8, ptr [[A]], i64 16
+; RV32-UNALIGNED-NEXT: [[TMP20:%.*]] = getelementptr i8, ptr [[B]], i64 16
+; RV32-UNALIGNED-NEXT: [[TMP21:%.*]] = load i32, ptr [[TMP19]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP22:%.*]] = load i32, ptr [[TMP20]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP23:%.*]] = xor i32 [[TMP21]], [[TMP22]]
+; RV32-UNALIGNED-NEXT: [[TMP24:%.*]] = getelementptr i8, ptr [[A]], i64 20
+; RV32-UNALIGNED-NEXT: [[TMP25:%.*]] = getelementptr i8, ptr [[B]], i64 20
+; RV32-UNALIGNED-NEXT: [[TMP26:%.*]] = load i32, ptr [[TMP24]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP27:%.*]] = load i32, ptr [[TMP25]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP28:%.*]] = xor i32 [[TMP26]], [[TMP27]]
+; RV32-UNALIGNED-NEXT: [[TMP29:%.*]] = getelementptr i8, ptr [[A]], i64 24
+; RV32-UNALIGNED-NEXT: [[TMP30:%.*]] = getelementptr i8, ptr [[B]], i64 24
+; RV32-UNALIGNED-NEXT: [[TMP31:%.*]] = load i32, ptr [[TMP29]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP32:%.*]] = load i32, ptr [[TMP30]], align 1
+; RV32-UNALIGNED-NEXT: [[TMP33:%.*]] = xor i32 [[TMP31]], [[TMP32]]
+; RV32-UNALIGNED-NEXT: [[TMP34:%.*]] = getelementptr i8, ptr [[A]], i64 28
+; RV32-UNALIGNED-NEXT: [[TMP35:%.*]] = getelementptr i8, ptr [[B]], i64 28
+; RV32-UNALIGNED-NEXT: [[TMP36:%.*]...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/209738
More information about the llvm-commits
mailing list