[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