[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
Sun Nov 3 18:14:54 PST 2024


https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/106542

>From 9859c07861131607e36d3de2ee0d2a9980b8d6da 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 | 194 ++++++++++++------
 .../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, 930 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 76fd7bd4dfe66e..d5fb899e0d714c 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -687,7 +687,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
@@ -700,7 +700,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 128502b99d9a37..9168a00d5cc504 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -260,8 +260,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 dc2dc4c1733b5e..1145d295e28d30 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 afa67249afd304..c82a3db2b3e328 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -6392,17 +6392,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
@@ -6414,6 +6411,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,
@@ -6453,7 +6455,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;
@@ -6462,7 +6464,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;
 
@@ -6540,9 +6542,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();
 
@@ -6555,7 +6557,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;
@@ -6571,9 +6573,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;
@@ -6583,7 +6585,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)
@@ -6605,12 +6607,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 07b9405b941d68..96ee67210a4c1d 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 285dc3f3dee6b5..415ec9f99c5f85 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2833,8 +2833,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 6e56e46b1d0211..071cf97ce2ff5f 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -362,13 +362,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
@@ -384,6 +386,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);
@@ -393,7 +397,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.
@@ -404,6 +409,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);
@@ -425,7 +432,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.
@@ -456,7 +463,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");
@@ -484,7 +491,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
@@ -518,7 +525,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;
   }
 
@@ -575,19 +583,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);
 
@@ -662,18 +681,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);
 
@@ -700,9 +730,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)
@@ -713,13 +745,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
@@ -735,6 +769,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);
 
@@ -744,11 +779,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
@@ -756,8 +792,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.
@@ -791,7 +828,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));
@@ -875,7 +912,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)
@@ -892,7 +929,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
@@ -904,7 +941,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));
@@ -941,14 +979,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))) {
@@ -978,7 +1016,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.
@@ -1001,11 +1039,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;
@@ -1044,8 +1082,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)
@@ -1061,7 +1099,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;
@@ -1069,7 +1107,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))
@@ -1079,12 +1117,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) {
@@ -1252,9 +1290,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");
     }
@@ -1348,9 +1387,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");
     }
@@ -1568,23 +1608,33 @@ 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()) {
+      Value *LHSV = B.CreateZExt(LHSC, CI->getType(), "lhsv");
+      Value *RHSV = B.CreateZExt(RHSC, CI->getType(), "rhsv");
+      return B.CreateSub(LHSV, 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.
@@ -1708,6 +1758,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
@@ -1717,7 +1768,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) {
@@ -1734,13 +1785,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);
@@ -3501,7 +3553,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),
@@ -3800,6 +3852,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.
@@ -3815,7 +3868,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);
@@ -3827,6 +3881,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
@@ -3842,7 +3898,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;
 
@@ -4363,7 +4419,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)
@@ -4409,7 +4466,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);
@@ -4433,13 +4491,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
@@ -4458,7 +4518,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
@@ -4471,7 +4531,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