[llvm] [LoopIdiom] Don't introduce `wcslen` calls when `wchar` size is not the default (PR #213629)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 3 02:20:53 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: Ömer Sinan Ağacan (osa1)
<details>
<summary>Changes</summary>
Fixes #<!-- -->190340.
---
Full diff: https://github.com/llvm/llvm-project/pull/213629.diff
6 Files Affected:
- (modified) llvm/include/llvm/Analysis/TargetLibraryInfo.h (+8)
- (modified) llvm/lib/Analysis/TargetLibraryInfo.cpp (+4)
- (modified) llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp (+11-1)
- (added) llvm/test/Transforms/LoopIdiom/wcslen-wchar-override.ll (+133)
- (modified) llvm/test/Transforms/LoopIdiom/wcslen16.ll (+9-12)
- (modified) llvm/test/Transforms/LoopIdiom/wcslen32.ll (-1)
``````````diff
diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.h b/llvm/include/llvm/Analysis/TargetLibraryInfo.h
index 629b126db17c2..70af9f261b6ce 100644
--- a/llvm/include/llvm/Analysis/TargetLibraryInfo.h
+++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.h
@@ -234,6 +234,9 @@ class TargetLibraryInfoImpl {
/// This queries the 'wchar_size' metadata.
LLVM_ABI unsigned getWCharSize(const Module &M) const;
+ /// Returns the size of the default wchar_t type in bytes.
+ LLVM_ABI unsigned getDefaultWCharSize(const Module &M) const;
+
/// Returns the size of the size_t type in bits.
LLVM_ABI unsigned getSizeTSize(const Module &M) const;
@@ -552,6 +555,11 @@ class TargetLibraryInfo {
return Impl->getWCharSize(M);
}
+ /// \copydoc TargetLibraryInfoImpl::getDefaultWCharSize()
+ unsigned getDefaultWCharSize(const Module &M) const {
+ return Impl->getDefaultWCharSize(M);
+ }
+
/// \copydoc TargetLibraryInfoImpl::getSizeTSize()
unsigned getSizeTSize(const Module &M) const { return Impl->getSizeTSize(M); }
diff --git a/llvm/lib/Analysis/TargetLibraryInfo.cpp b/llvm/lib/Analysis/TargetLibraryInfo.cpp
index 763c04b9b06f7..f82da068d1888 100644
--- a/llvm/lib/Analysis/TargetLibraryInfo.cpp
+++ b/llvm/lib/Analysis/TargetLibraryInfo.cpp
@@ -1443,6 +1443,10 @@ unsigned TargetLibraryInfoImpl::getWCharSize(const Module &M) const {
if (auto *ShortWChar = cast_or_null<ConstantAsMetadata>(
M.getModuleFlag("wchar_size")))
return cast<ConstantInt>(ShortWChar->getValue())->getZExtValue();
+ return getDefaultWCharSize(M);
+}
+
+unsigned TargetLibraryInfoImpl::getDefaultWCharSize(const Module &M) const {
return Triple(M.getTargetTriple()).getDefaultWCharSize();
}
diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index 4e5cd8462cc4d..011e11c11ac3f 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -2107,9 +2107,19 @@ class StrlenVerifier {
return false;
if (OpWidth != 8 && OpWidth != 16 && OpWidth != 32)
return false;
- if (OpWidth >= 16)
+ if (OpWidth >= 16) {
+ // Do not introduce a wcslen library call if we're overriding the default
+ // wchar size, to avoid ABI incompatibility between the library and the
+ // call site.
+ unsigned DefaultWcharSize =
+ TLI->getDefaultWCharSize(*LoopLoad->getModule());
+ if (WcharSize != DefaultWcharSize) {
+ return false;
+ }
+
if (OpWidth != WcharSize * 8)
return false;
+ }
// Scan every instruction in the loop to ensure there are no side effects.
for (Instruction &I : *LoopBody)
diff --git a/llvm/test/Transforms/LoopIdiom/wcslen-wchar-override.ll b/llvm/test/Transforms/LoopIdiom/wcslen-wchar-override.ll
new file mode 100644
index 0000000000000..6449182b591dd
--- /dev/null
+++ b/llvm/test/Transforms/LoopIdiom/wcslen-wchar-override.ll
@@ -0,0 +1,133 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes='loop(loop-idiom)' < %s -S | FileCheck %s
+
+; When wchar size is overridden as a different value than the default, don't
+; introduce calls to wcslen, to avoid ABI mismatch between wcslen and the call
+; sites.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i64 @valid_wcslen32(ptr %src) {
+; CHECK-LABEL: define i64 @valid_wcslen32(
+; CHECK-SAME: ptr [[SRC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null
+; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]]
+; CHECK: [[LOR_LHS_FALSE]]:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SRC]], align 4
+; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP0]], 0
+; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]]
+; CHECK: [[WHILE_COND_PREHEADER]]:
+; CHECK-NEXT: br label %[[WHILE_COND:.*]]
+; CHECK: [[WHILE_COND]]:
+; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ]
+; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 4
+; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[CURR_0]], align 4
+; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP3]], 0
+; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]]
+; CHECK: [[WHILE_END]]:
+; CHECK-NEXT: [[END:%.*]] = phi ptr [ [[CURR_0]], %[[WHILE_COND]] ]
+; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[END]] to i64
+; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64
+; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
+; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: ret i64 [[RETVAL_0]]
+;
+entry:
+ %cmp = icmp eq ptr %src, null
+ br i1 %cmp, label %return, label %lor.lhs.false
+
+lor.lhs.false: ; preds = %entry
+ %0 = load i32, ptr %src, align 4
+ %cmp1 = icmp eq i32 %0, 0
+ br i1 %cmp1, label %return, label %while.cond.preheader
+
+while.cond.preheader: ; preds = %lor.lhs.false
+ br label %while.cond
+
+while.cond: ; preds = %while.cond.preheader, %while.cond
+ %src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %while.cond.preheader ]
+ %curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 4
+ %1 = load i32, ptr %curr.0, align 4
+ %tobool.not = icmp eq i32 %1, 0
+ br i1 %tobool.not, label %while.end, label %while.cond
+
+while.end: ; preds = %while.cond
+ %curr.0.lcssa = phi ptr [ %curr.0, %while.cond ]
+ %sub.ptr.lhs.cast = ptrtoint ptr %curr.0.lcssa to i64
+ %sub.ptr.rhs.cast = ptrtoint ptr %src to i64
+ %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
+ %sub.ptr.div = ashr exact i64 %sub.ptr.sub, 2
+ br label %return
+
+return: ; preds = %entry, %lor.lhs.false, %while.end
+ %retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ]
+ ret i64 %retval.0
+}
+
+define i64 @invalid_char_size(ptr %src) {
+; CHECK-LABEL: define i64 @invalid_char_size(
+; CHECK-SAME: ptr [[SRC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null
+; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]]
+; CHECK: [[LOR_LHS_FALSE]]:
+; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[SRC]], align 2
+; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i16 [[TMP0]], 0
+; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]]
+; CHECK: [[WHILE_COND_PREHEADER]]:
+; CHECK-NEXT: br label %[[WHILE_COND:.*]]
+; CHECK: [[WHILE_COND]]:
+; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ]
+; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 2
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[CURR_0]], align 2
+; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i16 [[TMP1]], 0
+; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]]
+; CHECK: [[WHILE_END]]:
+; CHECK-NEXT: [[CURR_0_LCSSA:%.*]] = phi ptr [ [[CURR_0]], %[[WHILE_COND]] ]
+; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[CURR_0_LCSSA]] to i64
+; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64
+; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
+; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 1
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: ret i64 [[RETVAL_0]]
+;
+entry:
+ %cmp = icmp eq ptr %src, null
+ br i1 %cmp, label %return, label %lor.lhs.false
+
+lor.lhs.false: ; preds = %entry
+ %0 = load i16, ptr %src, align 2
+ %cmp1 = icmp eq i16 %0, 0
+ br i1 %cmp1, label %return, label %while.cond.preheader
+
+while.cond.preheader: ; preds = %lor.lhs.false
+ br label %while.cond
+
+while.cond: ; preds = %while.cond.preheader, %while.cond
+ %src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %while.cond.preheader ]
+ %curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 2
+ %1 = load i16, ptr %curr.0, align 2
+ %tobool.not = icmp eq i16 %1, 0
+ br i1 %tobool.not, label %while.end, label %while.cond
+
+while.end: ; preds = %while.cond
+ %curr.0.lcssa = phi ptr [ %curr.0, %while.cond ]
+ %sub.ptr.lhs.cast = ptrtoint ptr %curr.0.lcssa to i64
+ %sub.ptr.rhs.cast = ptrtoint ptr %src to i64
+ %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
+ %sub.ptr.div = ashr exact i64 %sub.ptr.sub, 1
+ br label %return
+
+return: ; preds = %entry, %lor.lhs.false, %while.end
+ %retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ]
+ ret i64 %retval.0
+}
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"wchar_size", i32 2}
diff --git a/llvm/test/Transforms/LoopIdiom/wcslen16.ll b/llvm/test/Transforms/LoopIdiom/wcslen16.ll
index cd8a31f4af3dd..13161b44ccde4 100644
--- a/llvm/test/Transforms/LoopIdiom/wcslen16.ll
+++ b/llvm/test/Transforms/LoopIdiom/wcslen16.ll
@@ -2,7 +2,7 @@
; RUN: opt -passes='loop(loop-idiom)' < %s -S | FileCheck %s
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
+target triple = "i386-unknown-linux-gnu"
define i64 @valid_strlen16(ptr %src) {
; CHECK-LABEL: define i64 @valid_strlen16(
@@ -15,19 +15,15 @@ define i64 @valid_strlen16(ptr %src) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i16 [[TMP0]], 0
; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]]
; CHECK: [[WHILE_COND_PREHEADER]]:
-; CHECK-NEXT: [[NEWGEP:%.*]] = getelementptr i8, ptr [[SRC]], i64 2
-; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[NEWGEP]])
-; CHECK-NEXT: [[TMP1:%.*]] = shl i64 [[WCSLEN]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = add i64 [[TMP1]], 2
-; CHECK-NEXT: [[END:%.*]] = getelementptr i8, ptr [[SRC]], i64 [[TMP2]]
; CHECK-NEXT: br label %[[WHILE_COND:.*]]
; CHECK: [[WHILE_COND]]:
; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ]
; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 2
; CHECK-NEXT: [[TMP3:%.*]] = load i16, ptr [[CURR_0]], align 2
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i16 [[TMP3]], 0
-; CHECK-NEXT: br i1 true, label %[[WHILE_END:.*]], label %[[WHILE_COND]]
+; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]]
; CHECK: [[WHILE_END]]:
+; CHECK-NEXT: [[END:%.*]] = phi ptr [ [[CURR_0]], %[[WHILE_COND]] ]
; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[END]] to i64
; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64
; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
@@ -76,15 +72,19 @@ define i64 @invalid_char_size(ptr %src) {
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP0]], 0
; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]]
; CHECK: [[WHILE_COND_PREHEADER]]:
+; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr i8, ptr [[SRC]], i64 4
+; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[SCEVGEP]])
+; CHECK-NEXT: [[TMP3:%.*]] = shl i64 [[WCSLEN]], 2
+; CHECK-NEXT: [[TMP2:%.*]] = add i64 [[TMP3]], 4
+; CHECK-NEXT: [[CURR_0_LCSSA:%.*]] = getelementptr i8, ptr [[SRC]], i64 [[TMP2]]
; CHECK-NEXT: br label %[[WHILE_COND:.*]]
; CHECK: [[WHILE_COND]]:
; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ [[CURR_0:%.*]], %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ]
; CHECK-NEXT: [[CURR_0]] = getelementptr inbounds i8, ptr [[SRC_PN]], i64 4
; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[CURR_0]], align 4
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 [[TMP1]], 0
-; CHECK-NEXT: br i1 [[TOBOOL_NOT]], label %[[WHILE_END:.*]], label %[[WHILE_COND]]
+; CHECK-NEXT: br i1 true, label %[[WHILE_END:.*]], label %[[WHILE_COND]]
; CHECK: [[WHILE_END]]:
-; CHECK-NEXT: [[CURR_0_LCSSA:%.*]] = phi ptr [ [[CURR_0]], %[[WHILE_COND]] ]
; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[CURR_0_LCSSA]] to i64
; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64
; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
@@ -121,6 +121,3 @@ return: ; preds = %entry, %lor.lhs.fal
%retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ]
ret i64 %retval.0
}
-!llvm.module.flags = !{!0}
-!0 = !{i32 1, !"wchar_size", i32 2}
-
diff --git a/llvm/test/Transforms/LoopIdiom/wcslen32.ll b/llvm/test/Transforms/LoopIdiom/wcslen32.ll
index f1cddf9424ffd..851f988718fe7 100644
--- a/llvm/test/Transforms/LoopIdiom/wcslen32.ll
+++ b/llvm/test/Transforms/LoopIdiom/wcslen32.ll
@@ -131,4 +131,3 @@ return: ; preds = %entry, %lor.lhs.fal
}
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"wchar_size", i32 4}
-
``````````
</details>
https://github.com/llvm/llvm-project/pull/213629
More information about the llvm-commits
mailing list