[llvm-branch-commits] [llvm] [SimplifyLibCalls] Add initial support for non-8-bit bytes (PR #106542)
Sergei Barannikov via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Aug 29 04:51:32 PDT 2024
https://github.com/s-barannikov created https://github.com/llvm/llvm-project/pull/106542
The patch makes CharWidth argument of `getStringLength` mandatory
and ensures the correct values are passed in most cases.
This is *not* a complete support for unusual byte widths in
SimplifyLibCalls since `getConstantStringInfo` returns false for those.
The code guarded by `getConstantStringInfo` returning true is unchanged
because the changes are currently not testable.
>From a32b79e862c532a0a115514f6df800d5b49f05ed Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Wed, 28 Aug 2024 16:09:44 +0300
Subject: [PATCH] [SimplifyLibCalls] Add initial support for non-8-bit bytes
The patch makes CharWidth argument of `getStringLength` mandatory
and ensures the correct values are passed in most cases.
This is *not* a complete support for unusual byte widths in
SimplifyLibCalls since `getConstantStringInfo` returns false for those.
The code guarded by `getConstantStringInfo` returning true is unchanged
because the changes are currently not testable.
---
llvm/include/llvm/Analysis/ValueTracking.h | 4 +-
.../llvm/Transforms/Utils/SimplifyLibCalls.h | 4 +-
llvm/lib/Analysis/MemoryBuiltins.cpp | 3 +-
llvm/lib/Analysis/ValueTracking.cpp | 40 ++--
.../InstCombine/InstCombineCalls.cpp | 12 +-
.../InstCombine/InstructionCombining.cpp | 5 +-
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 192 ++++++++++++------
.../InstCombine/SimplifyLibCalls/fputs-b16.ll | 19 ++
.../SimplifyLibCalls/fwrite-b16.ll | 19 ++
.../SimplifyLibCalls/memchr-b16.ll | 34 ++++
.../SimplifyLibCalls/memcmp-b32.ll | 32 +++
.../SimplifyLibCalls/memcpy-b16.ll | 69 +++++++
.../SimplifyLibCalls/memcpy_chk-b16.ll | 17 ++
.../SimplifyLibCalls/mempcpy-b16.ll | 17 ++
.../SimplifyLibCalls/memrchr-b16.ll | 20 ++
.../SimplifyLibCalls/memset-b16.ll | 66 ++++++
.../SimplifyLibCalls/stpcpy-b16.ll | 31 +++
.../SimplifyLibCalls/stpcpy_chk-b16.ll | 44 ++++
.../SimplifyLibCalls/stpncpy-b16.ll | 47 +++++
.../SimplifyLibCalls/strcat-b16.ll | 20 ++
.../SimplifyLibCalls/strchr-b16.ll | 45 ++++
.../SimplifyLibCalls/strcmp-b32.ll | 50 +++++
.../SimplifyLibCalls/strcpy-b16.ll | 18 ++
.../SimplifyLibCalls/strcpy_chk-b16.ll | 30 +++
.../SimplifyLibCalls/strlcpy-b16.ll | 18 ++
.../SimplifyLibCalls/strlen-b16.ll | 16 ++
.../SimplifyLibCalls/strncat-b16.ll | 20 ++
.../SimplifyLibCalls/strncmp-b32.ll | 34 ++++
.../SimplifyLibCalls/strncpy-b16.ll | 43 ++++
.../SimplifyLibCalls/strndup-b16.ll | 17 ++
.../SimplifyLibCalls/strnlen-b16.ll | 18 ++
.../SimplifyLibCalls/wcslen-b16.ll | 19 ++
llvm/test/Transforms/InstCombine/bcmp-1.ll | 2 +-
llvm/test/Transforms/InstCombine/memcmp-1.ll | 2 +-
llvm/test/Transforms/InstCombine/strncmp-1.ll | 2 +-
35 files changed, 928 insertions(+), 101 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/fputs-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/fwrite-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/memchr-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcmp-b32.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy_chk-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/mempcpy-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/memrchr-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/memset-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy_chk-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpncpy-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcat-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strchr-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcmp-b32.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy_chk-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlcpy-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlen-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncat-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncmp-b32.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncpy-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strndup-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/strnlen-b16.ll
create mode 100644 llvm/test/Transforms/InstCombine/SimplifyLibCalls/wcslen-b16.ll
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index ddd3cf22488f72..6594e114547fca 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -684,7 +684,7 @@ struct ConstantDataArraySlice {
/// If successful \p Slice will point to a ConstantDataArray info object
/// with an appropriate offset.
bool getConstantDataArrayInfo(const Value *V, ConstantDataArraySlice &Slice,
- unsigned ElementSize, uint64_t Offset = 0);
+ unsigned ElementBitWidth, uint64_t Offset = 0);
/// This function computes the length of a null-terminated C string pointed to
/// by V. If successful, it returns true and returns the string in Str. If
@@ -697,7 +697,7 @@ bool getConstantStringInfo(const Value *V, StringRef &Str, unsigned CharWidth,
/// If we can compute the length of the string pointed to by the specified
/// pointer, return 'len+1'. If we can't, return 0.
-uint64_t GetStringLength(const Value *V, unsigned CharSize = 8);
+uint64_t getStringLength(const Value *V, unsigned CharWidth);
/// This function returns call pointer argument that is considered the same by
/// aliasing rules. You CAN'T use it to replace one value with another. If
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 43b5c9250a8908..231e63957f5ef3 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -253,8 +253,8 @@ class LibCallSimplifier {
bool hasFloatVersion(const Module *M, StringRef FuncName);
/// Shared code to optimize strlen+wcslen and strnlen+wcsnlen.
- Value *optimizeStringLength(CallInst *CI, IRBuilderBase &B, unsigned CharSize,
- Value *Bound = nullptr);
+ Value *optimizeStringLength(CallInst *CI, IRBuilderBase &B,
+ unsigned CharWidth, Value *Bound = nullptr);
};
} // End llvm namespace
diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp
index e1abf5e4d885ec..d4dfd734ea960d 100644
--- a/llvm/lib/Analysis/MemoryBuiltins.cpp
+++ b/llvm/lib/Analysis/MemoryBuiltins.cpp
@@ -380,7 +380,8 @@ llvm::getAllocSize(const CallBase *CB, const TargetLibraryInfo *TLI,
// Handle strdup-like functions separately.
if (FnData->AllocTy == StrDupLike) {
- APInt Size(IntTyBits, GetStringLength(Mapper(CB->getArgOperand(0))));
+ APInt Size(IntTyBits, getStringLength(Mapper(CB->getArgOperand(0)),
+ DL.getByteWidth()));
if (!Size)
return std::nullopt;
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c82a33dcdfe748..96ad12f9b3cef2 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6236,17 +6236,14 @@ bool llvm::isGEPBasedOnPointerToString(const GEPOperator *GEP,
}
// If V refers to an initialized global constant, set Slice either to
-// its initializer if the size of its elements equals ElementSize, or,
-// for ElementSize == 8, to its representation as an array of unsiged
-// char. Return true on success.
-// Offset is in the unit "nr of ElementSize sized elements".
+// its initializer if the bit width of its elements equals ElementBitWidth,
+// or, for ElementBitWidth == CHAR_BIT, to its representation as an array
+// of unsigned char. Return true on success.
+// Offset is in the unit "nr of ElementBitWidth sized elements".
bool llvm::getConstantDataArrayInfo(const Value *V,
ConstantDataArraySlice &Slice,
- unsigned ElementSize, uint64_t Offset) {
+ unsigned ElementBitWidth, uint64_t Offset) {
assert(V && "V should not be null.");
- assert((ElementSize % 8) == 0 &&
- "ElementSize expected to be a multiple of the size of a byte.");
- unsigned ElementSizeInBytes = ElementSize / 8;
// Drill down into the pointer expression V, ignoring any intervening
// casts, and determine the identity of the object it references along
@@ -6258,6 +6255,11 @@ bool llvm::getConstantDataArrayInfo(const Value *V,
return false;
const DataLayout &DL = GV->getDataLayout();
+ unsigned ByteWidth = DL.getByteWidth();
+ assert((ElementBitWidth % ByteWidth) == 0 &&
+ "ElementBitWidth is expected to be a multiple of the byte width");
+ unsigned ElementSizeInBytes = ElementBitWidth / ByteWidth;
+
APInt Off(DL.getIndexTypeSizeInBits(V->getType()), 0);
if (GV != V->stripAndAccumulateConstantOffsets(DL, Off,
@@ -6297,7 +6299,7 @@ bool llvm::getConstantDataArrayInfo(const Value *V,
auto *Init = const_cast<Constant *>(GV->getInitializer());
if (auto *ArrayInit = dyn_cast<ConstantDataArray>(Init)) {
Type *InitElTy = ArrayInit->getElementType();
- if (InitElTy->isIntegerTy(ElementSize)) {
+ if (InitElTy->isIntegerTy(ElementBitWidth)) {
// If Init is an initializer for an array of the expected type
// and size, use it as is.
Array = ArrayInit;
@@ -6306,7 +6308,7 @@ bool llvm::getConstantDataArrayInfo(const Value *V,
}
if (!Array) {
- if (ElementSize != 8)
+ if (ElementBitWidth != CHAR_BIT)
// TODO: Handle conversions to larger integral types.
return false;
@@ -6384,9 +6386,9 @@ bool llvm::getConstantStringInfo(const Value *V, StringRef &Str,
/// If we can compute the length of the string pointed to by
/// the specified pointer, return 'len+1'. If we can't, return 0.
-static uint64_t GetStringLengthH(const Value *V,
- SmallPtrSetImpl<const PHINode*> &PHIs,
- unsigned CharSize) {
+static uint64_t getStringLength(const Value *V,
+ SmallPtrSetImpl<const PHINode *> &PHIs,
+ unsigned CharWidth) {
// Look through noop bitcast instructions.
V = V->stripPointerCasts();
@@ -6399,7 +6401,7 @@ static uint64_t GetStringLengthH(const Value *V,
// If it was new, see if all the input strings are the same length.
uint64_t LenSoFar = ~0ULL;
for (Value *IncValue : PN->incoming_values()) {
- uint64_t Len = GetStringLengthH(IncValue, PHIs, CharSize);
+ uint64_t Len = getStringLength(IncValue, PHIs, CharWidth);
if (Len == 0) return 0; // Unknown length -> unknown.
if (Len == ~0ULL) continue;
@@ -6415,9 +6417,9 @@ static uint64_t GetStringLengthH(const Value *V,
// strlen(select(c,x,y)) -> strlen(x) ^ strlen(y)
if (const SelectInst *SI = dyn_cast<SelectInst>(V)) {
- uint64_t Len1 = GetStringLengthH(SI->getTrueValue(), PHIs, CharSize);
+ uint64_t Len1 = getStringLength(SI->getTrueValue(), PHIs, CharWidth);
if (Len1 == 0) return 0;
- uint64_t Len2 = GetStringLengthH(SI->getFalseValue(), PHIs, CharSize);
+ uint64_t Len2 = getStringLength(SI->getFalseValue(), PHIs, CharWidth);
if (Len2 == 0) return 0;
if (Len1 == ~0ULL) return Len2;
if (Len2 == ~0ULL) return Len1;
@@ -6427,7 +6429,7 @@ static uint64_t GetStringLengthH(const Value *V,
// Otherwise, see if we can read the string.
ConstantDataArraySlice Slice;
- if (!getConstantDataArrayInfo(V, Slice, CharSize))
+ if (!getConstantDataArrayInfo(V, Slice, CharWidth))
return 0;
if (Slice.Array == nullptr)
@@ -6449,12 +6451,12 @@ static uint64_t GetStringLengthH(const Value *V,
/// If we can compute the length of the string pointed to by
/// the specified pointer, return 'len+1'. If we can't, return 0.
-uint64_t llvm::GetStringLength(const Value *V, unsigned CharSize) {
+uint64_t llvm::getStringLength(const Value *V, unsigned CharWidth) {
if (!V->getType()->isPointerTy())
return 0;
SmallPtrSet<const PHINode*, 32> PHIs;
- uint64_t Len = GetStringLengthH(V, PHIs, CharSize);
+ uint64_t Len = ::getStringLength(V, PHIs, CharWidth);
// If Len is ~0ULL, we had an infinite phi cycle: this is dead code, so return
// an empty string as a length.
return Len == ~0ULL ? 1 : Len;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index eb94e894b57b06..46858738d1853a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -156,7 +156,8 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) {
uint64_t Size = MemOpLength->getLimitedValue();
assert(Size && "0-sized memory transferring should be removed already.");
- if (Size > 8 || (Size&(Size-1)))
+ uint64_t MemOpWidth = Size * DL.getByteWidth();
+ if (MemOpWidth > 64 || (Size & (Size - 1)))
return nullptr; // If not 1/2/4/8 bytes, exit.
// If it is an atomic and alignment is less than the size then we will
@@ -168,7 +169,7 @@ Instruction *InstCombinerImpl::SimplifyAnyMemTransfer(AnyMemTransferInst *MI) {
return nullptr;
// Use an integer load+store unless we can find something better.
- IntegerType* IntType = IntegerType::get(MI->getContext(), Size<<3);
+ IntegerType *IntType = IntegerType::get(MI->getContext(), MemOpWidth);
// If the memcpy has metadata describing the members, see if we can get the
// TBAA, scope and noalias tags describing our copy.
@@ -244,7 +245,7 @@ Instruction *InstCombinerImpl::SimplifyAnyMemSet(AnyMemSetInst *MI) {
// Extract the length and alignment and fill if they are constant.
ConstantInt *LenC = dyn_cast<ConstantInt>(MI->getLength());
ConstantInt *FillC = dyn_cast<ConstantInt>(MI->getValue());
- if (!LenC || !FillC || !FillC->getType()->isIntegerTy(8))
+ if (!LenC || !FillC || !FillC->getType()->isIntegerTy(DL.getByteWidth()))
return nullptr;
const uint64_t Len = LenC->getLimitedValue();
assert(Len && "0-sized memory setting should be removed already.");
@@ -259,12 +260,13 @@ Instruction *InstCombinerImpl::SimplifyAnyMemSet(AnyMemSetInst *MI) {
return nullptr;
// memset(s,c,n) -> store s, c (for n=1,2,4,8)
- if (Len <= 8 && isPowerOf2_32((uint32_t)Len)) {
+ uint64_t MemOpWidth = Len * DL.getByteWidth();
+ if (MemOpWidth <= 64 && isPowerOf2_32((uint32_t)Len)) {
Value *Dest = MI->getDest();
// Extract the fill value and store.
Constant *FillVal = ConstantInt::get(
- MI->getContext(), APInt::getSplat(Len * 8, FillC->getValue()));
+ MI->getContext(), APInt::getSplat(MemOpWidth, FillC->getValue()));
StoreInst *S = Builder.CreateStore(FillVal, Dest, MI->isVolatile());
S->copyMetadata(*MI, LLVMContext::MD_DIAssignID);
auto replaceOpForAssignmentMarkers = [FillC, FillVal](auto *DbgAssign) {
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 37eddcf6c6dc94..603e11e4390584 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2778,8 +2778,9 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) {
if (MadeChange)
return &GEP;
- // Canonicalize constant GEPs to i8 type.
- if (!GEPEltType->isIntegerTy(8) && GEP.hasAllConstantIndices()) {
+ // Canonicalize constant GEPs to byte type.
+ if (!GEPEltType->isIntegerTy(DL.getByteWidth()) &&
+ GEP.hasAllConstantIndices()) {
APInt Offset(DL.getIndexTypeSizeInBits(GEPType), 0);
if (GEP.accumulateConstantOffset(DL, Offset))
return replaceInstUsesWith(
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 28d0c0bd915792..69b26e7c2f269f 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -356,13 +356,15 @@ static StringRef substr(StringRef Str, uint64_t Len) {
//===----------------------------------------------------------------------===//
Value *LibCallSimplifier::optimizeStrCat(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
+
// Extract some information from the instruction
Value *Dst = CI->getArgOperand(0);
Value *Src = CI->getArgOperand(1);
annotateNonNullNoUndefBasedOnAccess(CI, {0, 1});
// See if we can get the length of the input string.
- uint64_t Len = GetStringLength(Src);
+ uint64_t Len = getStringLength(Src, CharWidth);
if (Len)
annotateDereferenceableBytes(CI, 1, Len);
else
@@ -378,6 +380,8 @@ Value *LibCallSimplifier::optimizeStrCat(CallInst *CI, IRBuilderBase &B) {
Value *LibCallSimplifier::emitStrLenMemCpy(Value *Src, Value *Dst, uint64_t Len,
IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
+
// We need to find the end of the destination string. That's where the
// memory is to be moved to. We just generate a call to strlen.
Value *DstLen = emitStrLen(Dst, B, DL, TLI);
@@ -387,7 +391,8 @@ Value *LibCallSimplifier::emitStrLenMemCpy(Value *Src, Value *Dst, uint64_t Len,
// Now that we have the destination's length, we must index into the
// destination's pointer to get the actual memcpy destination (end of
// the string .. we're concatenating).
- Value *CpyDst = B.CreateInBoundsGEP(B.getInt8Ty(), Dst, DstLen, "endptr");
+ Value *CpyDst =
+ B.CreateInBoundsGEP(B.getIntNTy(CharWidth), Dst, DstLen, "endptr");
// We have enough information to now generate the memcpy call to do the
// concatenation for us. Make a memcpy to copy the nul byte with align = 1.
@@ -398,6 +403,8 @@ Value *LibCallSimplifier::emitStrLenMemCpy(Value *Src, Value *Dst, uint64_t Len,
}
Value *LibCallSimplifier::optimizeStrNCat(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
+
// Extract some information from the instruction.
Value *Dst = CI->getArgOperand(0);
Value *Src = CI->getArgOperand(1);
@@ -419,7 +426,7 @@ Value *LibCallSimplifier::optimizeStrNCat(CallInst *CI, IRBuilderBase &B) {
}
// See if we can get the length of the input string.
- uint64_t SrcLen = GetStringLength(Src);
+ uint64_t SrcLen = getStringLength(Src, CharWidth);
if (SrcLen) {
annotateDereferenceableBytes(CI, 1, SrcLen);
--SrcLen; // Unbias length.
@@ -450,7 +457,7 @@ static Value* memChrToCharCompare(CallInst *CI, Value *NBytes,
Value *CharVal = CI->getArgOperand(1);
// Fold memchr(A, C, N) == A to N && *A == C.
- Type *CharTy = B.getInt8Ty();
+ Type *CharTy = B.getIntNTy(DL.getByteWidth());
Value *Char0 = B.CreateLoad(CharTy, Src);
CharVal = B.CreateTrunc(CharVal, CharTy);
Value *Cmp = B.CreateICmpEQ(Char0, CharVal, "char0cmp");
@@ -478,7 +485,7 @@ Value *LibCallSimplifier::optimizeStrChr(CallInst *CI, IRBuilderBase &B) {
// of the input string and turn this into memchr.
ConstantInt *CharC = dyn_cast<ConstantInt>(CharVal);
if (!CharC) {
- uint64_t Len = GetStringLength(SrcStr);
+ uint64_t Len = getStringLength(SrcStr, CharWidth);
if (Len)
annotateDereferenceableBytes(CI, 0, Len);
else
@@ -512,7 +519,8 @@ Value *LibCallSimplifier::optimizeStrChr(CallInst *CI, IRBuilderBase &B) {
if (!getConstantStringInfo(SrcStr, Str, CharWidth)) {
if (CharC->isZero()) // strchr(p, 0) -> p + strlen(p)
if (Value *StrLen = emitStrLen(SrcStr, B, DL, TLI))
- return B.CreateInBoundsGEP(B.getInt8Ty(), SrcStr, StrLen, "strchr");
+ return B.CreateInBoundsGEP(B.getIntNTy(CharWidth), SrcStr, StrLen,
+ "strchr");
return nullptr;
}
@@ -569,19 +577,30 @@ Value *LibCallSimplifier::optimizeStrCmp(CallInst *CI, IRBuilderBase &B) {
return ConstantInt::get(CI->getType(),
std::clamp(Str1.compare(Str2), -1, 1));
- if (HasStr1 && Str1.empty()) // strcmp("", x) -> -*x
- return B.CreateNeg(B.CreateZExt(
- B.CreateLoad(B.getInt8Ty(), Str2P, "strcmpload"), CI->getType()));
+ uint64_t Len1 = getStringLength(Str1P, CharWidth);
+ uint64_t Len2 = getStringLength(Str2P, CharWidth);
- if (HasStr2 && Str2.empty()) // strcmp(x,"") -> *x
- return B.CreateZExt(B.CreateLoad(B.getInt8Ty(), Str1P, "strcmpload"),
- CI->getType());
+ if (Len1 == 1) {
+ Value *RHS0 = B.CreateLoad(B.getIntNTy(CharWidth), Str2P, "strcmpload");
+ // strcmp("", x) -> -*x
+ if (CharWidth < CI->getType()->getIntegerBitWidth())
+ return B.CreateNeg(B.CreateZExt(RHS0, CI->getType()));
+ // strcmp("", x) -> sext(*x != '\0')
+ return B.CreateSExt(B.CreateIsNotNull(RHS0), CI->getType());
+ }
+
+ if (Len2 == 1) {
+ Value *LHS0 = B.CreateLoad(B.getIntNTy(CharWidth), Str1P, "strcmpload");
+ // strcmp(x, "") -> *x
+ if (CharWidth < CI->getType()->getIntegerBitWidth())
+ return B.CreateZExt(LHS0, CI->getType());
+ // strcmp(x, "") -> zext(*x != '\0')
+ return B.CreateZExt(B.CreateIsNotNull(LHS0), CI->getType());
+ }
// strcmp(P, "x") -> memcmp(P, "x", 2)
- uint64_t Len1 = GetStringLength(Str1P);
if (Len1)
annotateDereferenceableBytes(CI, 0, Len1);
- uint64_t Len2 = GetStringLength(Str2P);
if (Len2)
annotateDereferenceableBytes(CI, 1, Len2);
@@ -656,18 +675,29 @@ Value *LibCallSimplifier::optimizeStrNCmp(CallInst *CI, IRBuilderBase &B) {
std::clamp(SubStr1.compare(SubStr2), -1, 1));
}
- if (HasStr1 && Str1.empty()) // strncmp("", x, n) -> -*x
- return B.CreateNeg(B.CreateZExt(
- B.CreateLoad(B.getInt8Ty(), Str2P, "strcmpload"), CI->getType()));
+ uint64_t Len1 = getStringLength(Str1P, CharWidth);
+ uint64_t Len2 = getStringLength(Str2P, CharWidth);
- if (HasStr2 && Str2.empty()) // strncmp(x, "", n) -> *x
- return B.CreateZExt(B.CreateLoad(B.getInt8Ty(), Str1P, "strcmpload"),
- CI->getType());
+ if (Len1 == 1) {
+ Value *RHS0 = B.CreateLoad(B.getIntNTy(CharWidth), Str2P, "strcmpload");
+ // strncmp("", x, n) -> -*x
+ if (CharWidth < CI->getType()->getIntegerBitWidth())
+ return B.CreateNeg(B.CreateZExt(RHS0, CI->getType()));
+ // strncmp("", x, n) -> sext(*x != '\0')
+ return B.CreateSExt(B.CreateIsNotNull(RHS0), CI->getType());
+ }
+
+ if (Len2 == 1) {
+ // strncmp(x, "", n) -> *x
+ Value *LHS0 = B.CreateLoad(B.getIntNTy(CharWidth), Str1P, "strcmpload");
+ if (CharWidth < CI->getType()->getIntegerBitWidth())
+ return B.CreateZExt(LHS0, CI->getType());
+ // strncmp(x, "", n) -> zext(*x != '\0')
+ return B.CreateZExt(B.CreateIsNotNull(LHS0), CI->getType());
+ }
- uint64_t Len1 = GetStringLength(Str1P);
if (Len1)
annotateDereferenceableBytes(CI, 0, Len1);
- uint64_t Len2 = GetStringLength(Str2P);
if (Len2)
annotateDereferenceableBytes(CI, 1, Len2);
@@ -694,9 +724,11 @@ Value *LibCallSimplifier::optimizeStrNCmp(CallInst *CI, IRBuilderBase &B) {
}
Value *LibCallSimplifier::optimizeStrNDup(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
Value *Src = CI->getArgOperand(0);
ConstantInt *Size = dyn_cast<ConstantInt>(CI->getArgOperand(1));
- uint64_t SrcLen = GetStringLength(Src);
+
+ uint64_t SrcLen = getStringLength(Src, CharWidth);
if (SrcLen && Size) {
annotateDereferenceableBytes(CI, 0, SrcLen);
if (SrcLen <= Size->getZExtValue() + 1)
@@ -707,13 +739,15 @@ Value *LibCallSimplifier::optimizeStrNDup(CallInst *CI, IRBuilderBase &B) {
}
Value *LibCallSimplifier::optimizeStrCpy(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
+
Value *Dst = CI->getArgOperand(0), *Src = CI->getArgOperand(1);
if (Dst == Src) // strcpy(x,x) -> x
return Src;
annotateNonNullNoUndefBasedOnAccess(CI, {0, 1});
// See if we can get the length of the input string.
- uint64_t Len = GetStringLength(Src);
+ uint64_t Len = getStringLength(Src, CharWidth);
if (Len)
annotateDereferenceableBytes(CI, 1, Len);
else
@@ -729,6 +763,7 @@ Value *LibCallSimplifier::optimizeStrCpy(CallInst *CI, IRBuilderBase &B) {
}
Value *LibCallSimplifier::optimizeStpCpy(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
Function *Callee = CI->getCalledFunction();
Value *Dst = CI->getArgOperand(0), *Src = CI->getArgOperand(1);
@@ -738,11 +773,12 @@ Value *LibCallSimplifier::optimizeStpCpy(CallInst *CI, IRBuilderBase &B) {
if (Dst == Src) { // stpcpy(x,x) -> x+strlen(x)
Value *StrLen = emitStrLen(Src, B, DL, TLI);
- return StrLen ? B.CreateInBoundsGEP(B.getInt8Ty(), Dst, StrLen) : nullptr;
+ return StrLen ? B.CreateInBoundsGEP(B.getIntNTy(CharWidth), Dst, StrLen)
+ : nullptr;
}
// See if we can get the length of the input string.
- uint64_t Len = GetStringLength(Src);
+ uint64_t Len = getStringLength(Src, CharWidth);
if (Len)
annotateDereferenceableBytes(CI, 1, Len);
else
@@ -750,8 +786,9 @@ Value *LibCallSimplifier::optimizeStpCpy(CallInst *CI, IRBuilderBase &B) {
Type *PT = Callee->getFunctionType()->getParamType(0);
Value *LenV = ConstantInt::get(DL.getIntPtrType(PT), Len);
- Value *DstEnd = B.CreateInBoundsGEP(
- B.getInt8Ty(), Dst, ConstantInt::get(DL.getIntPtrType(PT), Len - 1));
+ Value *DstEnd =
+ B.CreateInBoundsGEP(B.getIntNTy(CharWidth), Dst,
+ ConstantInt::get(DL.getIntPtrType(PT), Len - 1));
// We have enough information to now generate the memcpy call to do the
// copy for us. Make a memcpy to copy the nul byte with align = 1.
@@ -785,7 +822,7 @@ Value *LibCallSimplifier::optimizeStrLCpy(CallInst *CI, IRBuilderBase &B) {
if (NBytes <= 1) {
if (NBytes == 1)
// For a call to strlcpy(D, S, 1) first store a nul in *D.
- B.CreateStore(B.getInt8(0), Dst);
+ B.CreateStore(B.getIntN(CharWidth, 0), Dst);
// Transform strlcpy(D, S, 0) to a call to strlen(S).
return copyFlags(*CI, emitStrLen(Src, B, DL, TLI));
@@ -869,7 +906,7 @@ Value *LibCallSimplifier::optimizeStringNCpy(CallInst *CI, bool RetEnd,
return Dst;
if (N == 1) {
- Type *CharTy = B.getInt8Ty();
+ Type *CharTy = B.getIntNTy(CharWidth);
Value *CharVal = B.CreateLoad(CharTy, Src, "stxncpy.char0");
B.CreateStore(CharVal, Dst);
if (!RetEnd)
@@ -886,7 +923,7 @@ Value *LibCallSimplifier::optimizeStringNCpy(CallInst *CI, bool RetEnd,
}
// If the length of the input string is known set SrcLen to it.
- uint64_t SrcLen = GetStringLength(Src);
+ uint64_t SrcLen = getStringLength(Src, CharWidth);
if (SrcLen)
annotateDereferenceableBytes(CI, 1, SrcLen);
else
@@ -898,7 +935,8 @@ Value *LibCallSimplifier::optimizeStringNCpy(CallInst *CI, bool RetEnd,
// Transform st{p,r}ncpy(D, "", N) to memset(D, '\0', N) for any N.
Align MemSetAlign =
CI->getAttributes().getParamAttrs(0).getAlignment().valueOrOne();
- CallInst *NewCI = B.CreateMemSet(Dst, B.getInt8('\0'), Size, MemSetAlign);
+ CallInst *NewCI =
+ B.CreateMemSet(Dst, B.getIntN(CharWidth, 0), Size, MemSetAlign);
AttrBuilder ArgAttrs(CI->getContext(), CI->getAttributes().getParamAttrs(0));
NewCI->setAttributes(NewCI->getAttributes().addParamAttributes(
CI->getContext(), 0, ArgAttrs));
@@ -935,14 +973,14 @@ Value *LibCallSimplifier::optimizeStringNCpy(CallInst *CI, bool RetEnd,
// stpncpy(D, S, N) returns the address of the first null in D if it writes
// one, otherwise D + N.
Value *Off = B.getInt64(std::min(SrcLen, N));
- return B.CreateInBoundsGEP(B.getInt8Ty(), Dst, Off, "endptr");
+ return B.CreateInBoundsGEP(B.getIntNTy(CharWidth), Dst, Off, "endptr");
}
Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
- unsigned CharSize,
+ unsigned CharWidth,
Value *Bound) {
Value *Src = CI->getArgOperand(0);
- Type *CharTy = B.getIntNTy(CharSize);
+ Type *CharTy = B.getIntNTy(CharWidth);
if (isOnlyUsedInZeroEqualityComparison(CI) &&
(!Bound || isKnownNonZero(Bound, DL))) {
@@ -972,7 +1010,7 @@ Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
}
}
- if (uint64_t Len = GetStringLength(Src, CharSize)) {
+ if (uint64_t Len = getStringLength(Src, CharWidth)) {
Value *LenC = ConstantInt::get(CI->getType(), Len - 1);
// Fold strlen("xyz") -> 3 and strnlen("xyz", 2) -> 2
// and strnlen("xyz", Bound) -> min(3, Bound) for nonconstant Bound.
@@ -995,11 +1033,11 @@ Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
// very uncommon.
if (GEPOperator *GEP = dyn_cast<GEPOperator>(Src)) {
// TODO: Handle subobjects.
- if (!isGEPBasedOnPointerToString(GEP, CharSize))
+ if (!isGEPBasedOnPointerToString(GEP, CharWidth))
return nullptr;
ConstantDataArraySlice Slice;
- if (getConstantDataArrayInfo(GEP->getOperand(0), Slice, CharSize)) {
+ if (getConstantDataArrayInfo(GEP->getOperand(0), Slice, CharWidth)) {
uint64_t NullTermIdx;
if (Slice.Array == nullptr) {
NullTermIdx = 0;
@@ -1038,8 +1076,8 @@ Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
// strlen(x?"foo":"bars") --> x ? 3 : 4
if (SelectInst *SI = dyn_cast<SelectInst>(Src)) {
- uint64_t LenTrue = GetStringLength(SI->getTrueValue(), CharSize);
- uint64_t LenFalse = GetStringLength(SI->getFalseValue(), CharSize);
+ uint64_t LenTrue = getStringLength(SI->getTrueValue(), CharWidth);
+ uint64_t LenFalse = getStringLength(SI->getFalseValue(), CharWidth);
if (LenTrue && LenFalse) {
ORE.emit([&]() {
return OptimizationRemark("instcombine", "simplify-libcalls", CI)
@@ -1055,7 +1093,7 @@ Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilderBase &B,
}
Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilderBase &B) {
- if (Value *V = optimizeStringLength(CI, B, 8))
+ if (Value *V = optimizeStringLength(CI, B, DL.getByteWidth()))
return V;
annotateNonNullNoUndefBasedOnAccess(CI, 0);
return nullptr;
@@ -1063,7 +1101,7 @@ Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilderBase &B) {
Value *LibCallSimplifier::optimizeStrNLen(CallInst *CI, IRBuilderBase &B) {
Value *Bound = CI->getArgOperand(1);
- if (Value *V = optimizeStringLength(CI, B, 8, Bound))
+ if (Value *V = optimizeStringLength(CI, B, DL.getByteWidth(), Bound))
return V;
if (isKnownNonZero(Bound, DL))
@@ -1073,12 +1111,12 @@ Value *LibCallSimplifier::optimizeStrNLen(CallInst *CI, IRBuilderBase &B) {
Value *LibCallSimplifier::optimizeWcslen(CallInst *CI, IRBuilderBase &B) {
Module &M = *CI->getModule();
- unsigned WCharSize = TLI->getWCharSize(M) * 8;
+ unsigned WCharWidth = TLI->getWCharSize(M) * DL.getByteWidth();
// We cannot perform this optimization without wchar_size metadata.
- if (WCharSize == 0)
+ if (WCharWidth == 0)
return nullptr;
- return optimizeStringLength(CI, B, WCharSize);
+ return optimizeStringLength(CI, B, WCharWidth);
}
Value *LibCallSimplifier::optimizeStrPBrk(CallInst *CI, IRBuilderBase &B) {
@@ -1246,9 +1284,10 @@ Value *LibCallSimplifier::optimizeMemRChr(CallInst *CI, IRBuilderBase &B) {
if (LenC->isOne()) {
// Fold memrchr(x, y, 1) --> *x == y ? x : null for any x and y,
// constant or otherwise.
- Value *Val = B.CreateLoad(B.getInt8Ty(), SrcStr, "memrchr.char0");
+ Type *CharTy = B.getIntNTy(CharWidth);
+ Value *Val = B.CreateLoad(CharTy, SrcStr, "memrchr.char0");
// Slice off the character's high end bits.
- CharVal = B.CreateTrunc(CharVal, B.getInt8Ty());
+ CharVal = B.CreateTrunc(CharVal, CharTy);
Value *Cmp = B.CreateICmpEQ(Val, CharVal, "memrchr.char0cmp");
return B.CreateSelect(Cmp, SrcStr, NullPtr, "memrchr.sel");
}
@@ -1342,9 +1381,10 @@ Value *LibCallSimplifier::optimizeMemChr(CallInst *CI, IRBuilderBase &B) {
if (LenC->isOne()) {
// Fold memchr(x, y, 1) --> *x == y ? x : null for any x and y,
// constant or otherwise.
- Value *Val = B.CreateLoad(B.getInt8Ty(), SrcStr, "memchr.char0");
+ Type *CharTy = B.getIntNTy(CharWidth);
+ Value *Val = B.CreateLoad(CharTy, SrcStr, "memchr.char0");
// Slice off the character's high end bits.
- CharVal = B.CreateTrunc(CharVal, B.getInt8Ty());
+ CharVal = B.CreateTrunc(CharVal, CharTy);
Value *Cmp = B.CreateICmpEQ(Val, CharVal, "memchr.char0cmp");
return B.CreateSelect(Cmp, SrcStr, NullPtr, "memchr.sel");
}
@@ -1562,23 +1602,31 @@ static Value *optimizeMemCmpVarSize(CallInst *CI, Value *LHS, Value *RHS,
static Value *optimizeMemCmpConstantSize(CallInst *CI, Value *LHS, Value *RHS,
uint64_t Len, IRBuilderBase &B,
const DataLayout &DL) {
+ unsigned CharWidth = DL.getByteWidth();
+
if (Len == 0) // memcmp(s1,s2,0) -> 0
return Constant::getNullValue(CI->getType());
- // memcmp(S1,S2,1) -> *(unsigned char*)LHS - *(unsigned char*)RHS
if (Len == 1) {
- Value *LHSV = B.CreateZExt(B.CreateLoad(B.getInt8Ty(), LHS, "lhsc"),
- CI->getType(), "lhsv");
- Value *RHSV = B.CreateZExt(B.CreateLoad(B.getInt8Ty(), RHS, "rhsc"),
- CI->getType(), "rhsv");
- return B.CreateSub(LHSV, RHSV, "chardiff");
+ Type *CharTy = B.getIntNTy(CharWidth);
+ Value *LHSC = B.CreateLoad(CharTy, LHS, "lhsc");
+ Value *RHSC = B.CreateLoad(CharTy, RHS, "rhsc");
+
+ // memcmp(S1,S2,1) -> *(unsigned char*)LHS - *(unsigned char*)RHS
+ if (CharWidth < CI->getType()->getIntegerBitWidth())
+ return B.CreateSub(B.CreateZExt(LHSC, CI->getType(), "lhsv"),
+ B.CreateZExt(RHSC, CI->getType(), "rhsv"), "chardiff");
+
+ // memcmp(LHS, RHS, 1) -> *(unsigned char *)LHS <=> *(unsigned char *)RHS
+ return B.CreateIntrinsic(CI->getType(), Intrinsic::ucmp, {LHSC, RHSC});
}
// memcmp(S1,S2,N/8)==0 -> (*(intN_t*)S1 != *(intN_t*)S2)==0
// TODO: The case where both inputs are constants does not need to be limited
// to legal integers or equality comparison. See block below this.
- if (DL.isLegalInteger(Len * 8) && isOnlyUsedInZeroEqualityComparison(CI)) {
- IntegerType *IntType = IntegerType::get(CI->getContext(), Len * 8);
+ if (DL.isLegalInteger(Len * CharWidth) &&
+ isOnlyUsedInZeroEqualityComparison(CI)) {
+ IntegerType *IntType = IntegerType::get(CI->getContext(), Len * CharWidth);
Align PrefAlignment = DL.getPrefTypeAlign(IntType);
// First, see if we can fold either argument to a constant.
@@ -1702,6 +1750,7 @@ Value *LibCallSimplifier::optimizeMemCCpy(CallInst *CI, IRBuilderBase &B) {
}
Value *LibCallSimplifier::optimizeMemPCpy(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
Value *Dst = CI->getArgOperand(0);
Value *N = CI->getArgOperand(2);
// mempcpy(x, y, n) -> llvm.memcpy(align 1 x, align 1 y, n), x + n
@@ -1711,7 +1760,7 @@ Value *LibCallSimplifier::optimizeMemPCpy(CallInst *CI, IRBuilderBase &B) {
// any return attributes are compliant.
// TODO: Attach return value attributes to the 1st operand to preserve them?
mergeAttributesAndFlags(NewCI, *CI);
- return B.CreateInBoundsGEP(B.getInt8Ty(), Dst, N);
+ return B.CreateInBoundsGEP(B.getIntNTy(CharWidth), Dst, N);
}
Value *LibCallSimplifier::optimizeMemMove(CallInst *CI, IRBuilderBase &B) {
@@ -1728,13 +1777,14 @@ Value *LibCallSimplifier::optimizeMemMove(CallInst *CI, IRBuilderBase &B) {
}
Value *LibCallSimplifier::optimizeMemSet(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
Value *Size = CI->getArgOperand(2);
annotateNonNullAndDereferenceable(CI, 0, Size, DL);
if (isa<IntrinsicInst>(CI))
return nullptr;
// memset(p, v, n) -> llvm.memset(align 1 p, v, n)
- Value *Val = B.CreateIntCast(CI->getArgOperand(1), B.getInt8Ty(), false);
+ Value *Val = B.CreateTrunc(CI->getArgOperand(1), B.getIntNTy(CharWidth));
CallInst *NewCI = B.CreateMemSet(CI->getArgOperand(0), Val, Size, Align(1));
mergeAttributesAndFlags(NewCI, *CI);
return CI->getArgOperand(0);
@@ -3423,7 +3473,7 @@ Value *LibCallSimplifier::optimizeSPrintFString(CallInst *CI,
// sprintf(dest, "%s", str) -> strcpy(dest, str)
return copyFlags(*CI, emitStrCpy(Dest, CI->getArgOperand(2), B, TLI));
- uint64_t SrcLen = GetStringLength(CI->getArgOperand(2));
+ uint64_t SrcLen = getStringLength(CI->getArgOperand(2), /*CharWidth=*/8);
if (SrcLen) {
B.CreateMemCpy(
Dest, Align(1), CI->getArgOperand(2), Align(1),
@@ -3722,6 +3772,7 @@ Value *LibCallSimplifier::optimizeFPrintF(CallInst *CI, IRBuilderBase &B) {
}
Value *LibCallSimplifier::optimizeFWrite(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
optimizeErrorReporting(CI, B, 3);
// Get the element size and count.
@@ -3737,7 +3788,8 @@ Value *LibCallSimplifier::optimizeFWrite(CallInst *CI, IRBuilderBase &B) {
// If this is writing one byte, turn it into fputc.
// This optimisation is only valid, if the return value is unused.
if (Bytes == 1 && CI->use_empty()) { // fwrite(S,1,1,F) -> fputc(S[0],F)
- Value *Char = B.CreateLoad(B.getInt8Ty(), CI->getArgOperand(0), "char");
+ Value *Char =
+ B.CreateLoad(B.getIntNTy(CharWidth), CI->getArgOperand(0), "char");
Type *IntTy = B.getIntNTy(TLI->getIntSize());
Value *Cast = B.CreateIntCast(Char, IntTy, /*isSigned*/ true, "chari");
Value *NewCI = emitFPutC(Cast, CI->getArgOperand(3), B, TLI);
@@ -3749,6 +3801,8 @@ Value *LibCallSimplifier::optimizeFWrite(CallInst *CI, IRBuilderBase &B) {
}
Value *LibCallSimplifier::optimizeFPuts(CallInst *CI, IRBuilderBase &B) {
+ unsigned CharWidth = DL.getByteWidth();
+
optimizeErrorReporting(CI, B, 1);
// Don't rewrite fputs to fwrite when optimising for size because fwrite
@@ -3764,7 +3818,7 @@ Value *LibCallSimplifier::optimizeFPuts(CallInst *CI, IRBuilderBase &B) {
return nullptr;
// fputs(s,F) --> fwrite(s,strlen(s),1,F)
- uint64_t Len = GetStringLength(CI->getArgOperand(0));
+ uint64_t Len = getStringLength(CI->getArgOperand(0), CharWidth);
if (!Len)
return nullptr;
@@ -4277,7 +4331,8 @@ bool FortifiedLibCallSimplifier::isFortifiedCallFoldable(
if (OnlyLowerUnknownSize)
return false;
if (StrOp) {
- uint64_t Len = GetStringLength(CI->getArgOperand(*StrOp));
+ uint64_t Len = getStringLength(CI->getArgOperand(*StrOp),
+ CI->getDataLayout().getByteWidth());
// If the length is 0 we don't know how long it is and so we can't
// remove the check.
if (Len)
@@ -4323,7 +4378,8 @@ Value *FortifiedLibCallSimplifier::optimizeMemMoveChk(CallInst *CI,
Value *FortifiedLibCallSimplifier::optimizeMemSetChk(CallInst *CI,
IRBuilderBase &B) {
if (isFortifiedCallFoldable(CI, 3, 2)) {
- Value *Val = B.CreateIntCast(CI->getArgOperand(1), B.getInt8Ty(), false);
+ Type *CharTy = B.getIntNTy(CI->getDataLayout().getByteWidth());
+ Value *Val = B.CreateIntCast(CI->getArgOperand(1), CharTy, false);
CallInst *NewCI = B.CreateMemSet(CI->getArgOperand(0), Val,
CI->getArgOperand(2), Align(1));
mergeAttributesAndFlags(NewCI, *CI);
@@ -4347,13 +4403,15 @@ Value *FortifiedLibCallSimplifier::optimizeStrpCpyChk(CallInst *CI,
IRBuilderBase &B,
LibFunc Func) {
const DataLayout &DL = CI->getDataLayout();
+ unsigned CharWidth = DL.getByteWidth();
Value *Dst = CI->getArgOperand(0), *Src = CI->getArgOperand(1),
*ObjSize = CI->getArgOperand(2);
// __stpcpy_chk(x,x,...) -> x+strlen(x)
if (Func == LibFunc_stpcpy_chk && !OnlyLowerUnknownSize && Dst == Src) {
Value *StrLen = emitStrLen(Src, B, DL, TLI);
- return StrLen ? B.CreateInBoundsGEP(B.getInt8Ty(), Dst, StrLen) : nullptr;
+ return StrLen ? B.CreateInBoundsGEP(B.getIntNTy(CharWidth), Dst, StrLen)
+ : nullptr;
}
// If a) we don't have any length information, or b) we know this will
@@ -4372,7 +4430,7 @@ Value *FortifiedLibCallSimplifier::optimizeStrpCpyChk(CallInst *CI,
return nullptr;
// Maybe we can stil fold __st[rp]cpy_chk to __memcpy_chk.
- uint64_t Len = GetStringLength(Src);
+ uint64_t Len = getStringLength(Src, CharWidth);
if (Len)
annotateDereferenceableBytes(CI, 1, Len);
else
@@ -4385,7 +4443,7 @@ Value *FortifiedLibCallSimplifier::optimizeStrpCpyChk(CallInst *CI,
// If the function was an __stpcpy_chk, and we were able to fold it into
// a __memcpy_chk, we still need to return the correct end pointer.
if (Ret && Func == LibFunc_stpcpy_chk)
- return B.CreateInBoundsGEP(B.getInt8Ty(), Dst,
+ return B.CreateInBoundsGEP(B.getIntNTy(CharWidth), Dst,
ConstantInt::get(SizeTTy, Len - 1));
return copyFlags(*CI, cast<CallInst>(Ret));
}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/fputs-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/fputs-b16.ll
new file mode 100644
index 00000000000000..cc841c6f9c5a05
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/fputs-b16.ll
@@ -0,0 +1,19 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare i32 @fputs(ptr, ptr)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+; fputs(str, stream) -> fwrite(str, strlen(str), 1, stream)
+define void @test(ptr %stream) {
+; CHECK-LABEL: define void @test(
+; CHECK-SAME: ptr [[STREAM:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call i32 @fwrite(ptr nonnull @hello, i32 5, i32 1, ptr [[STREAM]])
+; CHECK-NEXT: ret void
+;
+ call i32 @fputs(ptr @hello, ptr %stream)
+ ret void
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/fwrite-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/fwrite-b16.ll
new file mode 100644
index 00000000000000..07c2468da43583
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/fwrite-b16.ll
@@ -0,0 +1,19 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare i32 @fwrite(ptr, i32, i32, ptr)
+
+; fwrite(str, 1, 1, stream) -> fputc(*str, stream)
+define void @test(ptr %str, ptr %stream) {
+; CHECK-LABEL: define void @test(
+; CHECK-SAME: ptr [[STR:%.*]], ptr [[STREAM:%.*]]) {
+; CHECK-NEXT: [[CHAR:%.*]] = load i16, ptr [[STR]], align 1
+; CHECK-NEXT: [[CHARI:%.*]] = sext i16 [[CHAR]] to i32
+; CHECK-NEXT: [[FPUTC:%.*]] = call i32 @fputc(i32 [[CHARI]], ptr [[STREAM]])
+; CHECK-NEXT: ret void
+;
+ call i32 @fwrite(ptr %str, i32 1, i32 1, ptr %stream)
+ ret void
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memchr-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memchr-b16.ll
new file mode 100644
index 00000000000000..0b48dac2c719f8
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memchr-b16.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32-n64"
+
+declare ptr @memchr(ptr, i32, i32)
+
+; memchr(str, c, n) == str -> (unsigned char)*str == (unsigned char)c
+define i1 @test_eq(ptr %str, i32 %c) {
+; CHECK-LABEL: define i1 @test_eq(
+; CHECK-SAME: ptr [[STR:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[STR]], align 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[C]] to i16
+; CHECK-NEXT: [[CHAR0CMP:%.*]] = icmp eq i16 [[TMP1]], [[TMP2]]
+; CHECK-NEXT: ret i1 [[CHAR0CMP]]
+;
+ %call = call ptr @memchr(ptr %str, i32 %c, i32 42)
+ %cmp = icmp eq ptr %call, %str
+ ret i1 %cmp
+}
+
+; memchr(str, c, 1) -> (unsigned char)*str == (unsigned char)c ? str : null
+define ptr @test_n_1(ptr %str, i32 %c) {
+; CHECK-LABEL: define ptr @test_n_1(
+; CHECK-SAME: ptr [[STR:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT: [[MEMCHR_CHAR0:%.*]] = load i16, ptr [[STR]], align 1
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C]] to i16
+; CHECK-NEXT: [[MEMCHR_CHAR0CMP:%.*]] = icmp eq i16 [[MEMCHR_CHAR0]], [[TMP1]]
+; CHECK-NEXT: [[MEMCHR_SEL:%.*]] = select i1 [[MEMCHR_CHAR0CMP]], ptr [[STR]], ptr null
+; CHECK-NEXT: ret ptr [[MEMCHR_SEL]]
+;
+ %call = call ptr @memchr(ptr %str, i32 %c, i32 1)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcmp-b32.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcmp-b32.ll
new file mode 100644
index 00000000000000..a21e9b1cadde18
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcmp-b32.ll
@@ -0,0 +1,32 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:32-p:32:32-n64"
+
+declare i32 @memcmp(ptr, ptr, i32)
+
+; memcmp(lhs, rhs, 1) -> *(unsigned char *)lhs <=> *(unsigned char *)rhs
+define i32 @test_size_1(ptr %lhs, ptr %rhs) {
+; CHECK-LABEL: define i32 @test_size_1(
+; CHECK-SAME: ptr [[LHS:%.*]], ptr [[RHS:%.*]]) {
+; CHECK-NEXT: [[MEMCMP_LHS0:%.*]] = load i32, ptr [[LHS]], align 1
+; CHECK-NEXT: [[MEMCMP_RHS0:%.*]] = load i32, ptr [[RHS]], align 1
+; CHECK-NEXT: [[MEMCMP_CHAR0CMP:%.*]] = call i32 @llvm.ucmp.i32.i32(i32 [[MEMCMP_LHS0]], i32 [[MEMCMP_RHS0]])
+; CHECK-NEXT: ret i32 [[MEMCMP_CHAR0CMP]]
+;
+ %call = call i32 @memcmp(ptr %lhs, ptr %rhs, i32 1) ret i32 %call
+}
+
+; memcmp(lhs, rhs, 2) == 0 -> (*(uint64_t *)lhs != *(uint64_t *)rhs) == 0
+define i1 @test_eq_size_2(ptr %lhs, ptr %rhs) {
+; CHECK-LABEL: define i1 @test_eq_size_2(
+; CHECK-SAME: ptr [[LHS:%.*]], ptr [[RHS:%.*]]) {
+; CHECK-NEXT: [[LHSV:%.*]] = load i64, ptr [[LHS]], align 1
+; CHECK-NEXT: [[RHSV:%.*]] = load i64, ptr [[RHS]], align 1
+; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i64 [[LHSV]], [[RHSV]]
+; CHECK-NEXT: ret i1 [[DOTNOT]]
+;
+ %call = call i32 @memcmp(ptr %lhs, ptr %rhs, i32 2)
+ %cmp = icmp eq i32 %call, 0
+ ret i1 %cmp
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy-b16.ll
new file mode 100644
index 00000000000000..2c59132a8a6ec0
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy-b16.ll
@@ -0,0 +1,69 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @memcpy(ptr, ptr, i32)
+
+define ptr @test_size_1(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_size_1(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[SRC]], align 1
+; CHECK-NEXT: store i16 [[TMP1]], ptr [[DST]], align 1
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memcpy(ptr %dst, ptr %src, i32 1)
+ ret ptr %call
+}
+
+define ptr @test_size_2(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_size_2(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[SRC]], align 1
+; CHECK-NEXT: store i32 [[TMP1]], ptr [[DST]], align 1
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memcpy(ptr %dst, ptr %src, i32 2)
+ ret ptr %call
+}
+
+define ptr @test_size_3(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_size_3(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], ptr noundef nonnull align 1 dereferenceable(3) [[SRC]], i32 3, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memcpy(ptr %dst, ptr %src, i32 3)
+ ret ptr %call
+}
+
+define ptr @test_size_4(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_size_4(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[SRC]], align 1
+; CHECK-NEXT: store i64 [[TMP1]], ptr [[DST]], align 1
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memcpy(ptr %dst, ptr %src, i32 4)
+ ret ptr %call
+}
+
+define ptr @test_size_5(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_size_5(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(5) [[DST]], ptr noundef nonnull align 1 dereferenceable(5) [[SRC]], i32 5, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memcpy(ptr %dst, ptr %src, i32 5)
+ ret ptr %call
+}
+
+define ptr @test_size_8(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_size_8(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(8) [[DST]], ptr noundef nonnull align 1 dereferenceable(8) [[SRC]], i32 8, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memcpy(ptr %dst, ptr %src, i32 8)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy_chk-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy_chk-b16.ll
new file mode 100644
index 00000000000000..f40e5fc7205f32
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memcpy_chk-b16.ll
@@ -0,0 +1,17 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @__memset_chk(ptr, i32, i32, i32)
+
+define ptr @test(ptr %dst, i32 %c, i32 %n) {
+; CHECK-LABEL: define ptr @test(
+; CHECK-SAME: ptr [[DST:%.*]], i32 [[C:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C]] to i16
+; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr align 1 [[DST]], i16 [[TMP1]], i32 [[N]], i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @__memset_chk(ptr %dst, i32 %c, i32 %n, i32 -1)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/mempcpy-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/mempcpy-b16.ll
new file mode 100644
index 00000000000000..fd6bdcd423785c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/mempcpy-b16.ll
@@ -0,0 +1,17 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @mempcpy(ptr, ptr, i32)
+
+define ptr @test(ptr %dst, ptr %src, i32 %n) {
+; CHECK-LABEL: define ptr @test(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST]], ptr align 1 [[SRC]], i32 [[N]], i1 false)
+; CHECK-NEXT: [[CALL:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 [[N]]
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+ %call = call ptr @mempcpy(ptr %dst, ptr %src, i32 %n)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memrchr-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memrchr-b16.ll
new file mode 100644
index 00000000000000..985dc3c249c0ed
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memrchr-b16.ll
@@ -0,0 +1,20 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32-n64"
+
+declare ptr @memrchr(ptr, i32, i32)
+
+; memrchr(str, c, 1) -> (unsigned char)*str == (unsigned char)c ? str : null
+define ptr @test_n_1(ptr %str, i32 %c) {
+; CHECK-LABEL: define ptr @test_n_1(
+; CHECK-SAME: ptr [[STR:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT: [[MEMRCHR_CHAR0:%.*]] = load i16, ptr [[STR]], align 1
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C]] to i16
+; CHECK-NEXT: [[MEMRCHR_CHAR0CMP:%.*]] = icmp eq i16 [[MEMRCHR_CHAR0]], [[TMP1]]
+; CHECK-NEXT: [[MEMRCHR_SEL:%.*]] = select i1 [[MEMRCHR_CHAR0CMP]], ptr [[STR]], ptr null
+; CHECK-NEXT: ret ptr [[MEMRCHR_SEL]]
+;
+ %call = call ptr @memrchr(ptr %str, i32 %c, i32 1)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memset-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memset-b16.ll
new file mode 100644
index 00000000000000..83bd0c84ae93bc
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/memset-b16.ll
@@ -0,0 +1,66 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @memset(ptr, i32, i32)
+
+define ptr @test_size_1(ptr %dst) {
+; CHECK-LABEL: define ptr @test_size_1(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: store i16 42, ptr [[DST]], align 1
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memset(ptr %dst, i32 42, i32 1)
+ ret ptr %call
+}
+
+define ptr @test_size_2(ptr %dst) {
+; CHECK-LABEL: define ptr @test_size_2(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: store i32 2752554, ptr [[DST]], align 1
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memset(ptr %dst, i32 42, i32 2)
+ ret ptr %call
+}
+
+define ptr @test_size_3(ptr %dst) {
+; CHECK-LABEL: define ptr @test_size_3(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr noundef nonnull align 1 dereferenceable(3) [[DST]], i16 42, i32 3, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memset(ptr %dst, i32 42, i32 3)
+ ret ptr %call
+}
+
+define ptr @test_size_4(ptr %dst) {
+; CHECK-LABEL: define ptr @test_size_4(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: store i64 11822129413226538, ptr [[DST]], align 1
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memset(ptr %dst, i32 42, i32 4)
+ ret ptr %call
+}
+
+define ptr @test_size_5(ptr %dst) {
+; CHECK-LABEL: define ptr @test_size_5(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr noundef nonnull align 1 dereferenceable(5) [[DST]], i16 42, i32 5, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memset(ptr %dst, i32 42, i32 5)
+ ret ptr %call
+}
+
+define ptr @test_size_8(ptr %dst) {
+; CHECK-LABEL: define ptr @test_size_8(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr noundef nonnull align 1 dereferenceable(8) [[DST]], i16 42, i32 8, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @memset(ptr %dst, i32 42, i32 8)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy-b16.ll
new file mode 100644
index 00000000000000..42b9ec7d4653ef
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy-b16.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @stpcpy(ptr, ptr)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+; stpcpy(x, x) -> x + strlen(x)
+define ptr @test_1(ptr %str) {
+; CHECK-LABEL: define ptr @test_1(
+; CHECK-SAME: ptr [[STR:%.*]]) {
+; CHECK-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[STR]])
+; CHECK-NEXT: [[CALL:%.*]] = getelementptr inbounds i16, ptr [[STR]], i32 [[STRLEN]]
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+ %call = call ptr @stpcpy(ptr %str, ptr %str)
+ ret ptr %call
+}
+
+define ptr @test_2(ptr %dst) {
+; CHECK-LABEL: define ptr @test_2(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: [[CALL:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 5
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[DST]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+ %call = call ptr @stpcpy(ptr %dst, ptr @hello)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy_chk-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy_chk-b16.ll
new file mode 100644
index 00000000000000..8262f94c58a396
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpcpy_chk-b16.ll
@@ -0,0 +1,44 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @__stpcpy_chk(ptr, ptr, i32)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+; __stpcpy_chk(str, str, n) -> str + strlen(x)
+define ptr @test_1(ptr %str, i32 %n) {
+; CHECK-LABEL: define ptr @test_1(
+; CHECK-SAME: ptr [[STR:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[STR]])
+; CHECK-NEXT: [[CALL:%.*]] = getelementptr inbounds i16, ptr [[STR]], i32 [[STRLEN]]
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+ %call = call ptr @__stpcpy_chk(ptr %str, ptr %str, i32 %n)
+ ret ptr %call
+}
+
+; __stpcpy_chk(dst, src, n) -> stpcpy(dst, src)
+define ptr @test_2(ptr %dst) {
+; CHECK-LABEL: define ptr @test_2(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: [[STPCPY:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 5
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[DST]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: ret ptr [[STPCPY]]
+;
+ %call = call ptr @__stpcpy_chk(ptr %dst, ptr @hello, i32 42)
+ ret ptr %call
+}
+
+; __stpcpy_chk(dst, src, n) -> __memcpy_chk(dst, src, strlen(src), n), dst + strlen(src)
+define ptr @test_3(ptr %dst, i32 %n) {
+; CHECK-LABEL: define ptr @test_3(
+; CHECK-SAME: ptr [[DST:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__memcpy_chk(ptr [[DST]], ptr nonnull @hello, i32 6, i32 [[N]])
+; CHECK-NEXT: [[CALL:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 5
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+ %call = call ptr @__stpcpy_chk(ptr %dst, ptr @hello, i32 %n)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpncpy-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpncpy-b16.ll
new file mode 100644
index 00000000000000..799ef8315ce121
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/stpncpy-b16.ll
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @stpncpy(ptr, ptr, i32)
+
+ at empty = constant [1 x i16] [i16 0]
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+; stpncpy(dst, src, 1) -> (*dst = *src) ? dst + 1 : dst
+define ptr @test_1(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_1(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: [[STXNCPY_CHAR0:%.*]] = load i16, ptr [[SRC]], align 1
+; CHECK-NEXT: store i16 [[STXNCPY_CHAR0]], ptr [[DST]], align 1
+; CHECK-NEXT: [[STPNCPY_CHAR0CMP:%.*]] = icmp ne i16 [[STXNCPY_CHAR0]], 0
+; CHECK-NEXT: [[STPNCPY_SEL_IDX:%.*]] = zext i1 [[STPNCPY_CHAR0CMP]] to i32
+; CHECK-NEXT: [[STPNCPY_SEL:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 [[STPNCPY_SEL_IDX]]
+; CHECK-NEXT: ret ptr [[STPNCPY_SEL]]
+;
+ %call = call ptr @stpncpy(ptr %dst, ptr %src, i32 1)
+ ret ptr %call
+}
+
+; stpncpy(dst, "", n) -> memset(dst, '\0', n), dst
+define ptr @test_2(ptr %dst, i32 %n) {
+; CHECK-LABEL: define ptr @test_2(
+; CHECK-SAME: ptr [[DST:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr align 1 [[DST]], i16 0, i32 [[N]], i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @stpncpy(ptr %dst, ptr @empty, i32 %n)
+ ret ptr %call
+}
+
+; stpncpy(dst, src, n) -> memcpy(dst, src, n), dst + min(strlen(src), n)
+define ptr @test_3(ptr %dst) {
+; CHECK-LABEL: define ptr @test_3(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[DST]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 5
+; CHECK-NEXT: ret ptr [[ENDPTR]]
+;
+ %call = call ptr @stpncpy(ptr %dst, ptr @hello, i32 6)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcat-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcat-b16.ll
new file mode 100644
index 00000000000000..643357157548c2
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcat-b16.ll
@@ -0,0 +1,20 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @strcat(ptr, ptr)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+define ptr @test(ptr %dst) {
+; CHECK-LABEL: define ptr @test(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[DST]])
+; CHECK-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 [[STRLEN]]
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[ENDPTR]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @strcat(ptr %dst, ptr @hello)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strchr-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strchr-b16.ll
new file mode 100644
index 00000000000000..98c45b3bf1926b
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strchr-b16.ll
@@ -0,0 +1,45 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32-n64"
+
+declare ptr @strchr(ptr, i32)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+; strchr(str, c) == str -> (unsigned char)str[0] == (unsigned char)c
+define i1 @test_1(ptr %str, i32 %c) {
+; CHECK-LABEL: define i1 @test_1(
+; CHECK-SAME: ptr [[STR:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[STR]], align 1
+; CHECK-NEXT: [[TMP2:%.*]] = trunc i32 [[C]] to i16
+; CHECK-NEXT: [[CHAR0CMP:%.*]] = icmp eq i16 [[TMP1]], [[TMP2]]
+; CHECK-NEXT: ret i1 [[CHAR0CMP]]
+;
+ %call = call ptr @strchr(ptr %str, i32 %c)
+ %cmp = icmp eq ptr %call, %str
+ ret i1 %cmp
+}
+
+; strchr(str, c) -> memchr(str, c, strlen(str) + 1)
+define ptr @test_2(i32 %c) {
+; CHECK-LABEL: define ptr @test_2(
+; CHECK-SAME: i32 [[C:%.*]]) {
+; CHECK-NEXT: [[STRCHR:%.*]] = call ptr @memchr(ptr noundef nonnull dereferenceable(1) @hello, i32 [[C]], i32 6)
+; CHECK-NEXT: ret ptr [[STRCHR]]
+;
+ %call = call ptr @strchr(ptr @hello, i32 %c)
+ ret ptr %call
+}
+
+; strchr(str, '\0') -> len = strlen(str), str + len
+define ptr @test_3(ptr %str) {
+; CHECK-LABEL: define ptr @test_3(
+; CHECK-SAME: ptr [[STR:%.*]]) {
+; CHECK-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[STR]])
+; CHECK-NEXT: [[STRCHR:%.*]] = getelementptr inbounds i16, ptr [[STR]], i32 [[STRLEN]]
+; CHECK-NEXT: ret ptr [[STRCHR]]
+;
+ %call = call ptr @strchr(ptr %str, i32 0)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcmp-b32.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcmp-b32.ll
new file mode 100644
index 00000000000000..d292ff4cfe5148
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcmp-b32.ll
@@ -0,0 +1,50 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:32-p:32:32"
+
+declare i32 @strcmp(ptr, ptr)
+
+ at empty = constant [1 x i32] [i32 0]
+ at hello = constant [6 x i32] [i32 104, i32 101, i32 108, i32 108, i32 111, i32 0]
+ at bell = constant [5 x i32] [i32 98, i32 101, i32 108, i32 108, i32 0]
+ at cell = constant [5 x i32] [i32 99, i32 101, i32 108, i32 108, i32 0]
+
+; strcmp("", rhs) -> sext(rhs[0] != '\0')
+define i32 @test_1(ptr %rhs) {
+; CHECK-LABEL: define i32 @test_1(
+; CHECK-SAME: ptr [[RHS:%.*]]) {
+; CHECK-NEXT: [[STRCMPLOAD:%.*]] = load i32, ptr [[RHS]], align 1
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[STRCMPLOAD]], 0
+; CHECK-NEXT: [[CALL:%.*]] = sext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+ %call = call i32 @strcmp(ptr @empty, ptr %rhs)
+ ret i32 %call
+}
+
+; strcmp(lhs, "") -> zext(lhs[0] != '\0')
+define i32 @test_2(ptr %lhs) {
+; CHECK-LABEL: define i32 @test_2(
+; CHECK-SAME: ptr [[LHS:%.*]]) {
+; CHECK-NEXT: [[STRCMPLOAD:%.*]] = load i32, ptr [[LHS]], align 1
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[STRCMPLOAD]], 0
+; CHECK-NEXT: [[CALL:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+ %call = call i32 @strcmp(ptr %lhs, ptr @empty)
+ ret i32 %call
+}
+
+; strcmp(lhs, rhs) -> memcmp(lhs, rhs, min(strlen(lhs), strlen(rhs)) + 1)
+define i32 @test_3(i1 %b) {
+; CHECK-LABEL: define i32 @test_3(
+; CHECK-SAME: i1 [[B:%.*]]) {
+; CHECK-NEXT: [[RHS:%.*]] = select i1 [[B]], ptr @bell, ptr @cell
+; CHECK-NEXT: [[MEMCMP:%.*]] = call i32 @memcmp(ptr noundef nonnull dereferenceable(5) @hello, ptr noundef nonnull dereferenceable(5) [[RHS]], i32 5)
+; CHECK-NEXT: ret i32 [[MEMCMP]]
+;
+ %rhs = select i1 %b, ptr @bell, ptr @cell
+ %call = call i32 @strcmp(ptr @hello, ptr %rhs)
+ ret i32 %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy-b16.ll
new file mode 100644
index 00000000000000..5f718a12d5b47e
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy-b16.ll
@@ -0,0 +1,18 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @strcpy(ptr, ptr)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+define ptr @test(ptr %dst) {
+; CHECK-LABEL: define ptr @test(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[DST]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @strcpy(ptr %dst, ptr @hello)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy_chk-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy_chk-b16.ll
new file mode 100644
index 00000000000000..593c65dc1c6187
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strcpy_chk-b16.ll
@@ -0,0 +1,30 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @__strcpy_chk(ptr, ptr, i32)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+; __strcpy_chk(dst, src, n) -> strcpy(dst, src)
+define ptr @test_2(ptr %dst) {
+; CHECK-LABEL: define ptr @test_2(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[DST]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @__strcpy_chk(ptr %dst, ptr @hello, i32 42)
+ ret ptr %call
+}
+
+; __strcpy_chk(dst, src, n) -> __memcpy_chk(dst, src, strlen(src), n)
+define ptr @test_3(ptr %dst, i32 %n) {
+; CHECK-LABEL: define ptr @test_3(
+; CHECK-SAME: ptr [[DST:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__memcpy_chk(ptr [[DST]], ptr nonnull @hello, i32 6, i32 [[N]])
+; CHECK-NEXT: ret ptr [[TMP1]]
+;
+ %call = call ptr @__strcpy_chk(ptr %dst, ptr @hello, i32 %n)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlcpy-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlcpy-b16.ll
new file mode 100644
index 00000000000000..82e0558b28e8ce
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlcpy-b16.ll
@@ -0,0 +1,18 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare i32 @strlcpy(ptr, ptr, i32)
+
+; strlcpy(dst, src, 1) -> *dst = 0, strlen(src)
+define i32 @test_1(ptr %dst, ptr %src) {
+; CHECK-LABEL: define i32 @test_1(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: store i16 0, ptr [[DST]], align 1
+; CHECK-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[SRC]])
+; CHECK-NEXT: ret i32 [[STRLEN]]
+;
+ %call = call i32 @strlcpy(ptr %dst, ptr %src, i32 1)
+ ret i32 %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlen-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlen-b16.ll
new file mode 100644
index 00000000000000..228802c7ee6071
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strlen-b16.ll
@@ -0,0 +1,16 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare i32 @strlen(ptr)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+define i32 @test() {
+; CHECK-LABEL: define i32 @test() {
+; CHECK-NEXT: ret i32 5
+;
+ %call = call i32 @strlen(ptr @hello)
+ ret i32 %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncat-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncat-b16.ll
new file mode 100644
index 00000000000000..d6fddabe91426f
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncat-b16.ll
@@ -0,0 +1,20 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @strncat(ptr, ptr, i32)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+define ptr @test(ptr %dst) {
+; CHECK-LABEL: define ptr @test(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[DST]])
+; CHECK-NEXT: [[ENDPTR:%.*]] = getelementptr inbounds i16, ptr [[DST]], i32 [[STRLEN]]
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[ENDPTR]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @strncat(ptr %dst, ptr @hello, i32 42)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncmp-b32.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncmp-b32.ll
new file mode 100644
index 00000000000000..1363af49659690
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncmp-b32.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:32-p:32:32"
+
+declare i32 @strncmp(ptr, ptr, i32)
+
+ at empty = constant [1 x i32] [i32 0]
+
+; strncmp("", rhs, n) -> sext(rhs[0] != '\0')
+define i32 @test_1(ptr %rhs) {
+; CHECK-LABEL: define i32 @test_1(
+; CHECK-SAME: ptr [[RHS:%.*]]) {
+; CHECK-NEXT: [[STRCMPLOAD:%.*]] = load i32, ptr [[RHS]], align 1
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[STRCMPLOAD]], 0
+; CHECK-NEXT: [[CALL:%.*]] = sext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+ %call = call i32 @strncmp(ptr @empty, ptr %rhs, i32 42)
+ ret i32 %call
+}
+
+; strncmp(lhs, "", n) -> zext(lhs[0] != '\0')
+define i32 @test_2(ptr %lhs) {
+; CHECK-LABEL: define i32 @test_2(
+; CHECK-SAME: ptr [[LHS:%.*]]) {
+; CHECK-NEXT: [[STRCMPLOAD:%.*]] = load i32, ptr [[LHS]], align 1
+; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[STRCMPLOAD]], 0
+; CHECK-NEXT: [[CALL:%.*]] = zext i1 [[TMP1]] to i32
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+ %call = call i32 @strncmp(ptr %lhs, ptr @empty, i32 42)
+ ret i32 %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncpy-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncpy-b16.ll
new file mode 100644
index 00000000000000..ca8ff2bea59a75
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strncpy-b16.ll
@@ -0,0 +1,43 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @strncpy(ptr, ptr, i32)
+
+ at empty = constant [1 x i16] [i16 0]
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+; strncpy(dst, src, 1) -> (*dst = *src), dst
+define ptr @test_1(ptr %dst, ptr %src) {
+; CHECK-LABEL: define ptr @test_1(
+; CHECK-SAME: ptr [[DST:%.*]], ptr [[SRC:%.*]]) {
+; CHECK-NEXT: [[STXNCPY_CHAR0:%.*]] = load i16, ptr [[SRC]], align 1
+; CHECK-NEXT: store i16 [[STXNCPY_CHAR0]], ptr [[DST]], align 1
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @strncpy(ptr %dst, ptr %src, i32 1)
+ ret ptr %call
+}
+
+; strncpy(dst, "", n) -> memset(dst, '\0', n), dst
+define ptr @test_2(ptr %dst, i32 %n) {
+; CHECK-LABEL: define ptr @test_2(
+; CHECK-SAME: ptr [[DST:%.*]], i32 [[N:%.*]]) {
+; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr align 1 [[DST]], i16 0, i32 [[N]], i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @strncpy(ptr %dst, ptr @empty, i32 %n)
+ ret ptr %call
+}
+
+; strncpy(dst, src, n) -> memcpy(dst, src, n), dst
+define ptr @test_3(ptr %dst) {
+; CHECK-LABEL: define ptr @test_3(
+; CHECK-SAME: ptr [[DST:%.*]]) {
+; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr noundef nonnull align 1 dereferenceable(6) [[DST]], ptr noundef nonnull align 1 dereferenceable(6) @hello, i32 6, i1 false)
+; CHECK-NEXT: ret ptr [[DST]]
+;
+ %call = call ptr @strncpy(ptr %dst, ptr @hello, i32 6)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strndup-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strndup-b16.ll
new file mode 100644
index 00000000000000..8294bf668f454c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strndup-b16.ll
@@ -0,0 +1,17 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare ptr @strndup(ptr, i32)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+define ptr @test() {
+; CHECK-LABEL: define ptr @test() {
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(6) ptr @strdup(ptr nonnull @hello)
+; CHECK-NEXT: ret ptr [[STRDUP]]
+;
+ %call = call ptr @strndup(ptr @hello, i32 6)
+ ret ptr %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strnlen-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strnlen-b16.ll
new file mode 100644
index 00000000000000..641022838ff7ad
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/strnlen-b16.ll
@@ -0,0 +1,18 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare i32 @strnlen(ptr, i32)
+
+ at hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
+
+define i32 @test(i32 %n) {
+; CHECK-LABEL: define i32 @test(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.umin.i32(i32 [[N]], i32 5)
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+ %call = call i32 @strnlen(ptr @hello, i32 %n)
+ ret i32 %call
+}
diff --git a/llvm/test/Transforms/InstCombine/SimplifyLibCalls/wcslen-b16.ll b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/wcslen-b16.ll
new file mode 100644
index 00000000000000..5cb31257336b56
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/SimplifyLibCalls/wcslen-b16.ll
@@ -0,0 +1,19 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -p instcombine -S %s | FileCheck %s
+
+target datalayout = "b:16-p:32:32"
+
+declare i32 @wcslen(ptr)
+
+ at hello = constant [6 x i32] [i32 104, i32 101, i32 108, i32 108, i32 111, i32 0]
+
+define i32 @test() {
+; CHECK-LABEL: define i32 @test() {
+; CHECK-NEXT: ret i32 5
+;
+ %call = call i32 @wcslen(ptr @hello)
+ ret i32 %call
+}
+
+!0 = !{i32 1, !"wchar_size", i32 2}
+!llvm.module.flags = !{!0}
diff --git a/llvm/test/Transforms/InstCombine/bcmp-1.ll b/llvm/test/Transforms/InstCombine/bcmp-1.ll
index 73daae44d020d9..b62adcd4f03852 100644
--- a/llvm/test/Transforms/InstCombine/bcmp-1.ll
+++ b/llvm/test/Transforms/InstCombine/bcmp-1.ll
@@ -36,8 +36,8 @@ define i32 @test_simplify2(ptr %mem1, ptr %mem2) {
define i32 @test_simplify3(ptr %mem1, ptr %mem2) {
; CHECK-LABEL: @test_simplify3(
; CHECK-NEXT: [[LHSC:%.*]] = load i8, ptr [[MEM1:%.*]], align 1
-; CHECK-NEXT: [[LHSV:%.*]] = zext i8 [[LHSC]] to i32
; CHECK-NEXT: [[RHSC:%.*]] = load i8, ptr [[MEM2:%.*]], align 1
+; CHECK-NEXT: [[LHSV:%.*]] = zext i8 [[LHSC]] to i32
; CHECK-NEXT: [[RHSV:%.*]] = zext i8 [[RHSC]] to i32
; CHECK-NEXT: [[CHARDIFF:%.*]] = sub nsw i32 [[LHSV]], [[RHSV]]
; CHECK-NEXT: ret i32 [[CHARDIFF]]
diff --git a/llvm/test/Transforms/InstCombine/memcmp-1.ll b/llvm/test/Transforms/InstCombine/memcmp-1.ll
index 054896647b5188..956cb39c08da10 100644
--- a/llvm/test/Transforms/InstCombine/memcmp-1.ll
+++ b/llvm/test/Transforms/InstCombine/memcmp-1.ll
@@ -36,8 +36,8 @@ define i32 @test_simplify2(ptr %mem1, ptr %mem2) {
define i32 @test_simplify3(ptr %mem1, ptr %mem2) {
; CHECK-LABEL: @test_simplify3(
; CHECK-NEXT: [[LHSC:%.*]] = load i8, ptr %mem1, align 1
-; CHECK-NEXT: [[LHSV:%.*]] = zext i8 [[LHSC]] to i32
; CHECK-NEXT: [[RHSC:%.*]] = load i8, ptr %mem2, align 1
+; CHECK-NEXT: [[LHSV:%.*]] = zext i8 [[LHSC]] to i32
; CHECK-NEXT: [[RHSV:%.*]] = zext i8 [[RHSC]] to i32
; CHECK-NEXT: [[CHARDIFF:%.*]] = sub nsw i32 [[LHSV]], [[RHSV]]
; CHECK-NEXT: ret i32 [[CHARDIFF]]
diff --git a/llvm/test/Transforms/InstCombine/strncmp-1.ll b/llvm/test/Transforms/InstCombine/strncmp-1.ll
index 9a33b85a4049c0..d5e11ca8da1f26 100644
--- a/llvm/test/Transforms/InstCombine/strncmp-1.ll
+++ b/llvm/test/Transforms/InstCombine/strncmp-1.ll
@@ -68,8 +68,8 @@ define i32 @test5() {
define i32 @test6(ptr %str1, ptr %str2) {
; CHECK-LABEL: @test6(
; CHECK-NEXT: [[LHSC:%.*]] = load i8, ptr [[STR1:%.*]], align 1
-; CHECK-NEXT: [[LHSV:%.*]] = zext i8 [[LHSC]] to i32
; CHECK-NEXT: [[RHSC:%.*]] = load i8, ptr [[STR2:%.*]], align 1
+; CHECK-NEXT: [[LHSV:%.*]] = zext i8 [[LHSC]] to i32
; CHECK-NEXT: [[RHSV:%.*]] = zext i8 [[RHSC]] to i32
; CHECK-NEXT: [[CHARDIFF:%.*]] = sub nsw i32 [[LHSV]], [[RHSV]]
; CHECK-NEXT: ret i32 [[CHARDIFF]]
More information about the llvm-branch-commits
mailing list