forked from OSchip/llvm-project
SimplifyLibCalls: Optimize wcslen
Refactor the strlen optimization code to work for both strlen and wcslen. This especially helps with programs in the wild where people pass L"string"s to const std::wstring& function parameters and the wstring constructor gets inlined. This also fixes a lingerind API problem/bug in getConstantStringInfo() where zeroinitializers would always give you an empty string (without a length) back regardless of the actual length of the initializer which did not work well in the TrimAtNul==false causing the PR mentioned below. Note that the fixed getConstantStringInfo() needed fixes to SelectionDAG memcpy lowering and may lead to some cases for out-of-bounds zeroinitializer accesses not getting optimized anymore. So some code with UB may produce out of bound memory reads now instead of just producing zeros. The refactoring "accidentally" fixes http://llvm.org/PR32124 Differential Revision: https://reviews.llvm.org/D32839 llvm-svn: 303461
This commit is contained in:
parent
89f3bcf0b5
commit
50ec0b5dce
|
@ -191,6 +191,14 @@ public:
|
||||||
void setShouldSignExtI32Param(bool Val) {
|
void setShouldSignExtI32Param(bool Val) {
|
||||||
ShouldSignExtI32Param = Val;
|
ShouldSignExtI32Param = Val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the size of the wchar_t type in bytes.
|
||||||
|
unsigned getWCharSize(const Module &M) const;
|
||||||
|
|
||||||
|
/// Returns size of the default wchar_t type on target \p T. This is mostly
|
||||||
|
/// intended to verify that the size in the frontend matches LLVM. All other
|
||||||
|
/// queries should use getWCharSize() instead.
|
||||||
|
static unsigned getTargetWCharSize(const Triple &T);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Provides information about what library functions are available for
|
/// Provides information about what library functions are available for
|
||||||
|
@ -307,6 +315,11 @@ public:
|
||||||
return Attribute::None;
|
return Attribute::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \copydoc TargetLibraryInfoImpl::getWCharSize()
|
||||||
|
unsigned getWCharSize(const Module &M) const {
|
||||||
|
return Impl->getWCharSize(M);
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle invalidation from the pass manager.
|
/// Handle invalidation from the pass manager.
|
||||||
///
|
///
|
||||||
/// If we try to invalidate this info, just return false. It cannot become
|
/// If we try to invalidate this info, just return false. It cannot become
|
||||||
|
|
|
@ -218,9 +218,38 @@ template <typename T> class ArrayRef;
|
||||||
DL);
|
DL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the GEP is based on a pointer to a string (array of i8),
|
/// Returns true if the GEP is based on a pointer to a string (array of
|
||||||
/// and is indexing into this string.
|
// \p CharSize integers) and is indexing into this string.
|
||||||
bool isGEPBasedOnPointerToString(const GEPOperator *GEP);
|
bool isGEPBasedOnPointerToString(const GEPOperator *GEP,
|
||||||
|
unsigned CharSize = 8);
|
||||||
|
|
||||||
|
/// Represents offset+length into a ConstantDataArray.
|
||||||
|
struct ConstantDataArraySlice {
|
||||||
|
/// ConstantDataArray pointer. nullptr indicates a zeroinitializer (a valid
|
||||||
|
/// initializer, it just doesn't fit the ConstantDataArray interface).
|
||||||
|
const ConstantDataArray *Array;
|
||||||
|
/// Slice starts at this Offset.
|
||||||
|
uint64_t Offset;
|
||||||
|
/// Length of the slice.
|
||||||
|
uint64_t Length;
|
||||||
|
|
||||||
|
/// Moves the Offset and adjusts Length accordingly.
|
||||||
|
void move(uint64_t Delta) {
|
||||||
|
assert(Delta < Length);
|
||||||
|
Offset += Delta;
|
||||||
|
Length -= Delta;
|
||||||
|
}
|
||||||
|
/// Convenience accessor for elements in the slice.
|
||||||
|
uint64_t operator[](unsigned I) const {
|
||||||
|
return Array==nullptr ? 0 : Array->getElementAsInteger(I + Offset);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns true if the value \p V is a pointer into a ContantDataArray.
|
||||||
|
/// If successfull \p Index will point to a ConstantDataArray info object
|
||||||
|
/// with an apropriate offset.
|
||||||
|
bool getConstantDataArrayInfo(const Value *V, ConstantDataArraySlice &Slice,
|
||||||
|
unsigned ElementSize, uint64_t Offset = 0);
|
||||||
|
|
||||||
/// This function computes the length of a null-terminated C string pointed to
|
/// 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
|
/// by V. If successful, it returns true and returns the string in Str. If
|
||||||
|
@ -233,7 +262,7 @@ template <typename T> class ArrayRef;
|
||||||
|
|
||||||
/// If we can compute the length of the string pointed to by the specified
|
/// If we can compute the length of the string pointed to by the specified
|
||||||
/// pointer, return 'len+1'. If we can't, return 0.
|
/// pointer, return 'len+1'. If we can't, return 0.
|
||||||
uint64_t GetStringLength(const Value *V);
|
uint64_t GetStringLength(const Value *V, unsigned CharSize = 8);
|
||||||
|
|
||||||
/// This method strips off any GEP address adjustments and pointer casts from
|
/// This method strips off any GEP address adjustments and pointer casts from
|
||||||
/// the specified value, returning the original object being addressed. Note
|
/// the specified value, returning the original object being addressed. Note
|
||||||
|
|
|
@ -634,8 +634,8 @@ public:
|
||||||
/// The size of the elements is known to be a multiple of one byte.
|
/// The size of the elements is known to be a multiple of one byte.
|
||||||
uint64_t getElementByteSize() const;
|
uint64_t getElementByteSize() const;
|
||||||
|
|
||||||
/// This method returns true if this is an array of i8.
|
/// This method returns true if this is an array of \p CharSize integers.
|
||||||
bool isString() const;
|
bool isString(unsigned CharSize = 8) const;
|
||||||
|
|
||||||
/// This method returns true if the array "isString", ends with a null byte,
|
/// This method returns true if the array "isString", ends with a null byte,
|
||||||
/// and does not contains any other null bytes.
|
/// and does not contains any other null bytes.
|
||||||
|
|
|
@ -121,6 +121,7 @@ private:
|
||||||
Value *optimizeMemCpy(CallInst *CI, IRBuilder<> &B);
|
Value *optimizeMemCpy(CallInst *CI, IRBuilder<> &B);
|
||||||
Value *optimizeMemMove(CallInst *CI, IRBuilder<> &B);
|
Value *optimizeMemMove(CallInst *CI, IRBuilder<> &B);
|
||||||
Value *optimizeMemSet(CallInst *CI, IRBuilder<> &B);
|
Value *optimizeMemSet(CallInst *CI, IRBuilder<> &B);
|
||||||
|
Value *optimizeWcslen(CallInst *CI, IRBuilder<> &B);
|
||||||
// Wrapper for all String/Memory Library Call Optimizations
|
// Wrapper for all String/Memory Library Call Optimizations
|
||||||
Value *optimizeStringMemoryLibCall(CallInst *CI, IRBuilder<> &B);
|
Value *optimizeStringMemoryLibCall(CallInst *CI, IRBuilder<> &B);
|
||||||
|
|
||||||
|
@ -165,6 +166,9 @@ private:
|
||||||
/// hasFloatVersion - Checks if there is a float version of the specified
|
/// hasFloatVersion - Checks if there is a float version of the specified
|
||||||
/// function by checking for an existing function with name FuncName + f
|
/// function by checking for an existing function with name FuncName + f
|
||||||
bool hasFloatVersion(StringRef FuncName);
|
bool hasFloatVersion(StringRef FuncName);
|
||||||
|
|
||||||
|
/// Shared code to optimize strlen+wcslen.
|
||||||
|
Value *optimizeStringLength(CallInst *CI, IRBuilder<> &B, unsigned CharSize);
|
||||||
};
|
};
|
||||||
} // End llvm namespace
|
} // End llvm namespace
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||||
#include "llvm/ADT/Triple.h"
|
#include "llvm/ADT/Triple.h"
|
||||||
|
#include "llvm/IR/Constants.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
|
@ -1518,6 +1519,17 @@ TargetLibraryInfoImpl &TargetLibraryAnalysis::lookupInfoImpl(const Triple &T) {
|
||||||
return *Impl;
|
return *Impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned TargetLibraryInfoImpl::getTargetWCharSize(const Triple &T) {
|
||||||
|
// See also clang/lib/Basic/Targets.cpp.
|
||||||
|
return T.isPS4() || T.isOSWindows() || T.getArch() == Triple::xcore ? 2 : 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned TargetLibraryInfoImpl::getWCharSize(const Module &M) const {
|
||||||
|
if (auto *ShortWChar = cast_or_null<ConstantAsMetadata>(
|
||||||
|
M.getModuleFlag("wchar_size")))
|
||||||
|
return cast<ConstantInt>(ShortWChar->getValue())->getZExtValue();
|
||||||
|
return getTargetWCharSize(Triple(M.getTargetTriple()));
|
||||||
|
}
|
||||||
|
|
||||||
TargetLibraryInfoWrapperPass::TargetLibraryInfoWrapperPass()
|
TargetLibraryInfoWrapperPass::TargetLibraryInfoWrapperPass()
|
||||||
: ImmutablePass(ID), TLIImpl(), TLI(TLIImpl) {
|
: ImmutablePass(ID), TLIImpl(), TLI(TLIImpl) {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "llvm/IR/ConstantRange.h"
|
#include "llvm/IR/ConstantRange.h"
|
||||||
#include "llvm/IR/Constants.h"
|
#include "llvm/IR/Constants.h"
|
||||||
#include "llvm/IR/DataLayout.h"
|
#include "llvm/IR/DataLayout.h"
|
||||||
|
#include "llvm/IR/DerivedTypes.h"
|
||||||
#include "llvm/IR/Dominators.h"
|
#include "llvm/IR/Dominators.h"
|
||||||
#include "llvm/IR/GetElementPtrTypeIterator.h"
|
#include "llvm/IR/GetElementPtrTypeIterator.h"
|
||||||
#include "llvm/IR/GlobalAlias.h"
|
#include "llvm/IR/GlobalAlias.h"
|
||||||
|
@ -2953,14 +2954,16 @@ Value *llvm::GetPointerBaseWithConstantOffset(Value *Ptr, int64_t &Offset,
|
||||||
return Ptr;
|
return Ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool llvm::isGEPBasedOnPointerToString(const GEPOperator *GEP) {
|
bool llvm::isGEPBasedOnPointerToString(const GEPOperator *GEP,
|
||||||
|
unsigned CharSize) {
|
||||||
// Make sure the GEP has exactly three arguments.
|
// Make sure the GEP has exactly three arguments.
|
||||||
if (GEP->getNumOperands() != 3)
|
if (GEP->getNumOperands() != 3)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Make sure the index-ee is a pointer to array of i8.
|
// Make sure the index-ee is a pointer to array of \p CharSize integers.
|
||||||
|
// CharSize.
|
||||||
ArrayType *AT = dyn_cast<ArrayType>(GEP->getSourceElementType());
|
ArrayType *AT = dyn_cast<ArrayType>(GEP->getSourceElementType());
|
||||||
if (!AT || !AT->getElementType()->isIntegerTy(8))
|
if (!AT || !AT->getElementType()->isIntegerTy(CharSize))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check to make sure that the first operand of the GEP is an integer and
|
// Check to make sure that the first operand of the GEP is an integer and
|
||||||
|
@ -2972,11 +2975,9 @@ bool llvm::isGEPBasedOnPointerToString(const GEPOperator *GEP) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function computes the length of a null-terminated C string pointed to
|
bool llvm::getConstantDataArrayInfo(const Value *V,
|
||||||
/// by V. If successful, it returns true and returns the string in Str.
|
ConstantDataArraySlice &Slice,
|
||||||
/// If unsuccessful, it returns false.
|
unsigned ElementSize, uint64_t Offset) {
|
||||||
bool llvm::getConstantStringInfo(const Value *V, StringRef &Str,
|
|
||||||
uint64_t Offset, bool TrimAtNul) {
|
|
||||||
assert(V);
|
assert(V);
|
||||||
|
|
||||||
// Look through bitcast instructions and geps.
|
// Look through bitcast instructions and geps.
|
||||||
|
@ -2987,7 +2988,7 @@ bool llvm::getConstantStringInfo(const Value *V, StringRef &Str,
|
||||||
if (const GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
|
if (const GEPOperator *GEP = dyn_cast<GEPOperator>(V)) {
|
||||||
// The GEP operator should be based on a pointer to string constant, and is
|
// The GEP operator should be based on a pointer to string constant, and is
|
||||||
// indexing into the string constant.
|
// indexing into the string constant.
|
||||||
if (!isGEPBasedOnPointerToString(GEP))
|
if (!isGEPBasedOnPointerToString(GEP, ElementSize))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If the second index isn't a ConstantInt, then this is a variable index
|
// If the second index isn't a ConstantInt, then this is a variable index
|
||||||
|
@ -2998,8 +2999,8 @@ bool llvm::getConstantStringInfo(const Value *V, StringRef &Str,
|
||||||
StartIdx = CI->getZExtValue();
|
StartIdx = CI->getZExtValue();
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
return getConstantStringInfo(GEP->getOperand(0), Str, StartIdx + Offset,
|
return getConstantDataArrayInfo(GEP->getOperand(0), Slice, ElementSize,
|
||||||
TrimAtNul);
|
StartIdx + Offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The GEP instruction, constant or instruction, must reference a global
|
// The GEP instruction, constant or instruction, must reference a global
|
||||||
|
@ -3009,30 +3010,72 @@ bool llvm::getConstantStringInfo(const Value *V, StringRef &Str,
|
||||||
if (!GV || !GV->isConstant() || !GV->hasDefinitiveInitializer())
|
if (!GV || !GV->isConstant() || !GV->hasDefinitiveInitializer())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Handle the all-zeros case.
|
const ConstantDataArray *Array;
|
||||||
|
ArrayType *ArrayTy;
|
||||||
if (GV->getInitializer()->isNullValue()) {
|
if (GV->getInitializer()->isNullValue()) {
|
||||||
// This is a degenerate case. The initializer is constant zero so the
|
Type *GVTy = GV->getValueType();
|
||||||
// length of the string must be zero.
|
if ( (ArrayTy = dyn_cast<ArrayType>(GVTy)) ) {
|
||||||
Str = "";
|
// A zeroinitializer for the array; There is no ConstantDataArray.
|
||||||
return true;
|
Array = nullptr;
|
||||||
}
|
} else {
|
||||||
|
const DataLayout &DL = GV->getParent()->getDataLayout();
|
||||||
// This must be a ConstantDataArray.
|
uint64_t SizeInBytes = DL.getTypeStoreSize(GVTy);
|
||||||
const auto *Array = dyn_cast<ConstantDataArray>(GV->getInitializer());
|
uint64_t Length = SizeInBytes / (ElementSize / 8);
|
||||||
if (!Array || !Array->isString())
|
if (Length <= Offset)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Get the number of elements in the array.
|
Slice.Array = nullptr;
|
||||||
uint64_t NumElts = Array->getType()->getArrayNumElements();
|
Slice.Offset = 0;
|
||||||
|
Slice.Length = Length - Offset;
|
||||||
// Start out with the entire array in the StringRef.
|
return true;
|
||||||
Str = Array->getAsString();
|
}
|
||||||
|
} else {
|
||||||
|
// This must be a ConstantDataArray.
|
||||||
|
Array = dyn_cast<ConstantDataArray>(GV->getInitializer());
|
||||||
|
if (!Array)
|
||||||
|
return false;
|
||||||
|
ArrayTy = Array->getType();
|
||||||
|
}
|
||||||
|
if (!ArrayTy->getElementType()->isIntegerTy(ElementSize))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint64_t NumElts = ArrayTy->getArrayNumElements();
|
||||||
if (Offset > NumElts)
|
if (Offset > NumElts)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
Slice.Array = Array;
|
||||||
|
Slice.Offset = Offset;
|
||||||
|
Slice.Length = NumElts - Offset;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 unsuccessful, it returns false.
|
||||||
|
bool llvm::getConstantStringInfo(const Value *V, StringRef &Str,
|
||||||
|
uint64_t Offset, bool TrimAtNul) {
|
||||||
|
ConstantDataArraySlice Slice;
|
||||||
|
if (!getConstantDataArrayInfo(V, Slice, 8, Offset))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Slice.Array == nullptr) {
|
||||||
|
if (TrimAtNul) {
|
||||||
|
Str = StringRef();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Slice.Length == 1) {
|
||||||
|
Str = StringRef("", 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// We cannot instantiate a StringRef as we do not have an apropriate string
|
||||||
|
// of 0s at hand.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start out with the entire array in the StringRef.
|
||||||
|
Str = Slice.Array->getAsString();
|
||||||
// Skip over 'offset' bytes.
|
// Skip over 'offset' bytes.
|
||||||
Str = Str.substr(Offset);
|
Str = Str.substr(Slice.Offset);
|
||||||
|
|
||||||
if (TrimAtNul) {
|
if (TrimAtNul) {
|
||||||
// Trim off the \0 and anything after it. If the array is not nul
|
// Trim off the \0 and anything after it. If the array is not nul
|
||||||
|
@ -3050,7 +3093,8 @@ bool llvm::getConstantStringInfo(const Value *V, StringRef &Str,
|
||||||
/// If we can compute the length of the string pointed to by
|
/// If we can compute the length of the string pointed to by
|
||||||
/// the specified pointer, return 'len+1'. If we can't, return 0.
|
/// the specified pointer, return 'len+1'. If we can't, return 0.
|
||||||
static uint64_t GetStringLengthH(const Value *V,
|
static uint64_t GetStringLengthH(const Value *V,
|
||||||
SmallPtrSetImpl<const PHINode*> &PHIs) {
|
SmallPtrSetImpl<const PHINode*> &PHIs,
|
||||||
|
unsigned CharSize) {
|
||||||
// Look through noop bitcast instructions.
|
// Look through noop bitcast instructions.
|
||||||
V = V->stripPointerCasts();
|
V = V->stripPointerCasts();
|
||||||
|
|
||||||
|
@ -3063,7 +3107,7 @@ static uint64_t GetStringLengthH(const Value *V,
|
||||||
// If it was new, see if all the input strings are the same length.
|
// If it was new, see if all the input strings are the same length.
|
||||||
uint64_t LenSoFar = ~0ULL;
|
uint64_t LenSoFar = ~0ULL;
|
||||||
for (Value *IncValue : PN->incoming_values()) {
|
for (Value *IncValue : PN->incoming_values()) {
|
||||||
uint64_t Len = GetStringLengthH(IncValue, PHIs);
|
uint64_t Len = GetStringLengthH(IncValue, PHIs, CharSize);
|
||||||
if (Len == 0) return 0; // Unknown length -> unknown.
|
if (Len == 0) return 0; // Unknown length -> unknown.
|
||||||
|
|
||||||
if (Len == ~0ULL) continue;
|
if (Len == ~0ULL) continue;
|
||||||
|
@ -3079,9 +3123,9 @@ static uint64_t GetStringLengthH(const Value *V,
|
||||||
|
|
||||||
// strlen(select(c,x,y)) -> strlen(x) ^ strlen(y)
|
// strlen(select(c,x,y)) -> strlen(x) ^ strlen(y)
|
||||||
if (const SelectInst *SI = dyn_cast<SelectInst>(V)) {
|
if (const SelectInst *SI = dyn_cast<SelectInst>(V)) {
|
||||||
uint64_t Len1 = GetStringLengthH(SI->getTrueValue(), PHIs);
|
uint64_t Len1 = GetStringLengthH(SI->getTrueValue(), PHIs, CharSize);
|
||||||
if (Len1 == 0) return 0;
|
if (Len1 == 0) return 0;
|
||||||
uint64_t Len2 = GetStringLengthH(SI->getFalseValue(), PHIs);
|
uint64_t Len2 = GetStringLengthH(SI->getFalseValue(), PHIs, CharSize);
|
||||||
if (Len2 == 0) return 0;
|
if (Len2 == 0) return 0;
|
||||||
if (Len1 == ~0ULL) return Len2;
|
if (Len1 == ~0ULL) return Len2;
|
||||||
if (Len2 == ~0ULL) return Len1;
|
if (Len2 == ~0ULL) return Len1;
|
||||||
|
@ -3090,20 +3134,30 @@ static uint64_t GetStringLengthH(const Value *V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, see if we can read the string.
|
// Otherwise, see if we can read the string.
|
||||||
StringRef StrData;
|
ConstantDataArraySlice Slice;
|
||||||
if (!getConstantStringInfo(V, StrData))
|
if (!getConstantDataArrayInfo(V, Slice, CharSize))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return StrData.size()+1;
|
if (Slice.Array == nullptr)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Search for nul characters
|
||||||
|
unsigned NullIndex = 0;
|
||||||
|
for (unsigned E = Slice.Length; NullIndex < E; ++NullIndex) {
|
||||||
|
if (Slice.Array->getElementAsInteger(Slice.Offset + NullIndex) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NullIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If we can compute the length of the string pointed to by
|
/// If we can compute the length of the string pointed to by
|
||||||
/// the specified pointer, return 'len+1'. If we can't, return 0.
|
/// the specified pointer, return 'len+1'. If we can't, return 0.
|
||||||
uint64_t llvm::GetStringLength(const Value *V) {
|
uint64_t llvm::GetStringLength(const Value *V, unsigned CharSize) {
|
||||||
if (!V->getType()->isPointerTy()) return 0;
|
if (!V->getType()->isPointerTy()) return 0;
|
||||||
|
|
||||||
SmallPtrSet<const PHINode*, 32> PHIs;
|
SmallPtrSet<const PHINode*, 32> PHIs;
|
||||||
uint64_t Len = GetStringLengthH(V, PHIs);
|
uint64_t Len = GetStringLengthH(V, PHIs, CharSize);
|
||||||
// If Len is ~0ULL, we had an infinite phi cycle: this is dead code, so return
|
// If Len is ~0ULL, we had an infinite phi cycle: this is dead code, so return
|
||||||
// an empty string as a length.
|
// an empty string as a length.
|
||||||
return Len == ~0ULL ? 1 : Len;
|
return Len == ~0ULL ? 1 : Len;
|
||||||
|
|
|
@ -4685,9 +4685,10 @@ static SDValue getMemsetValue(SDValue Value, EVT VT, SelectionDAG &DAG,
|
||||||
/// used when a memcpy is turned into a memset when the source is a constant
|
/// used when a memcpy is turned into a memset when the source is a constant
|
||||||
/// string ptr.
|
/// string ptr.
|
||||||
static SDValue getMemsetStringVal(EVT VT, const SDLoc &dl, SelectionDAG &DAG,
|
static SDValue getMemsetStringVal(EVT VT, const SDLoc &dl, SelectionDAG &DAG,
|
||||||
const TargetLowering &TLI, StringRef Str) {
|
const TargetLowering &TLI,
|
||||||
|
const ConstantDataArraySlice &Slice) {
|
||||||
// Handle vector with all elements zero.
|
// Handle vector with all elements zero.
|
||||||
if (Str.empty()) {
|
if (Slice.Array == nullptr) {
|
||||||
if (VT.isInteger())
|
if (VT.isInteger())
|
||||||
return DAG.getConstant(0, dl, VT);
|
return DAG.getConstant(0, dl, VT);
|
||||||
else if (VT == MVT::f32 || VT == MVT::f64 || VT == MVT::f128)
|
else if (VT == MVT::f32 || VT == MVT::f64 || VT == MVT::f128)
|
||||||
|
@ -4706,15 +4707,15 @@ static SDValue getMemsetStringVal(EVT VT, const SDLoc &dl, SelectionDAG &DAG,
|
||||||
assert(!VT.isVector() && "Can't handle vector type here!");
|
assert(!VT.isVector() && "Can't handle vector type here!");
|
||||||
unsigned NumVTBits = VT.getSizeInBits();
|
unsigned NumVTBits = VT.getSizeInBits();
|
||||||
unsigned NumVTBytes = NumVTBits / 8;
|
unsigned NumVTBytes = NumVTBits / 8;
|
||||||
unsigned NumBytes = std::min(NumVTBytes, unsigned(Str.size()));
|
unsigned NumBytes = std::min(NumVTBytes, unsigned(Slice.Length));
|
||||||
|
|
||||||
APInt Val(NumVTBits, 0);
|
APInt Val(NumVTBits, 0);
|
||||||
if (DAG.getDataLayout().isLittleEndian()) {
|
if (DAG.getDataLayout().isLittleEndian()) {
|
||||||
for (unsigned i = 0; i != NumBytes; ++i)
|
for (unsigned i = 0; i != NumBytes; ++i)
|
||||||
Val |= (uint64_t)(unsigned char)Str[i] << i*8;
|
Val |= (uint64_t)(unsigned char)Slice[i] << i*8;
|
||||||
} else {
|
} else {
|
||||||
for (unsigned i = 0; i != NumBytes; ++i)
|
for (unsigned i = 0; i != NumBytes; ++i)
|
||||||
Val |= (uint64_t)(unsigned char)Str[i] << (NumVTBytes-i-1)*8;
|
Val |= (uint64_t)(unsigned char)Slice[i] << (NumVTBytes-i-1)*8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the "cost" of materializing the integer immediate is less than the cost
|
// If the "cost" of materializing the integer immediate is less than the cost
|
||||||
|
@ -4731,9 +4732,8 @@ SDValue SelectionDAG::getMemBasePlusOffset(SDValue Base, unsigned Offset,
|
||||||
return getNode(ISD::ADD, DL, VT, Base, getConstant(Offset, DL, VT));
|
return getNode(ISD::ADD, DL, VT, Base, getConstant(Offset, DL, VT));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// isMemSrcFromString - Returns true if memcpy source is a string constant.
|
/// Returns true if memcpy source is constant data.
|
||||||
///
|
static bool isMemSrcFromConstant(SDValue Src, ConstantDataArraySlice &Slice) {
|
||||||
static bool isMemSrcFromString(SDValue Src, StringRef &Str) {
|
|
||||||
uint64_t SrcDelta = 0;
|
uint64_t SrcDelta = 0;
|
||||||
GlobalAddressSDNode *G = nullptr;
|
GlobalAddressSDNode *G = nullptr;
|
||||||
if (Src.getOpcode() == ISD::GlobalAddress)
|
if (Src.getOpcode() == ISD::GlobalAddress)
|
||||||
|
@ -4747,8 +4747,8 @@ static bool isMemSrcFromString(SDValue Src, StringRef &Str) {
|
||||||
if (!G)
|
if (!G)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return getConstantStringInfo(G->getGlobal(), Str,
|
return getConstantDataArrayInfo(G->getGlobal(), Slice, 8,
|
||||||
SrcDelta + G->getOffset(), false);
|
SrcDelta + G->getOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the optimal series of memory ops to replace the memset / memcpy.
|
/// Determines the optimal series of memory ops to replace the memset / memcpy.
|
||||||
|
@ -4891,15 +4891,15 @@ static SDValue getMemcpyLoadsAndStores(SelectionDAG &DAG, const SDLoc &dl,
|
||||||
unsigned SrcAlign = DAG.InferPtrAlignment(Src);
|
unsigned SrcAlign = DAG.InferPtrAlignment(Src);
|
||||||
if (Align > SrcAlign)
|
if (Align > SrcAlign)
|
||||||
SrcAlign = Align;
|
SrcAlign = Align;
|
||||||
StringRef Str;
|
ConstantDataArraySlice Slice;
|
||||||
bool CopyFromStr = isMemSrcFromString(Src, Str);
|
bool CopyFromConstant = isMemSrcFromConstant(Src, Slice);
|
||||||
bool isZeroStr = CopyFromStr && Str.empty();
|
bool isZeroConstant = CopyFromConstant && Slice.Array == nullptr;
|
||||||
unsigned Limit = AlwaysInline ? ~0U : TLI.getMaxStoresPerMemcpy(OptSize);
|
unsigned Limit = AlwaysInline ? ~0U : TLI.getMaxStoresPerMemcpy(OptSize);
|
||||||
|
|
||||||
if (!FindOptimalMemOpLowering(MemOps, Limit, Size,
|
if (!FindOptimalMemOpLowering(MemOps, Limit, Size,
|
||||||
(DstAlignCanChange ? 0 : Align),
|
(DstAlignCanChange ? 0 : Align),
|
||||||
(isZeroStr ? 0 : SrcAlign),
|
(isZeroConstant ? 0 : SrcAlign),
|
||||||
false, false, CopyFromStr, true,
|
false, false, CopyFromConstant, true,
|
||||||
DstPtrInfo.getAddrSpace(),
|
DstPtrInfo.getAddrSpace(),
|
||||||
SrcPtrInfo.getAddrSpace(),
|
SrcPtrInfo.getAddrSpace(),
|
||||||
DAG, TLI))
|
DAG, TLI))
|
||||||
|
@ -4943,18 +4943,29 @@ static SDValue getMemcpyLoadsAndStores(SelectionDAG &DAG, const SDLoc &dl,
|
||||||
DstOff -= VTSize - Size;
|
DstOff -= VTSize - Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CopyFromStr &&
|
if (CopyFromConstant &&
|
||||||
(isZeroStr || (VT.isInteger() && !VT.isVector()))) {
|
(isZeroConstant || (VT.isInteger() && !VT.isVector()))) {
|
||||||
// It's unlikely a store of a vector immediate can be done in a single
|
// It's unlikely a store of a vector immediate can be done in a single
|
||||||
// instruction. It would require a load from a constantpool first.
|
// instruction. It would require a load from a constantpool first.
|
||||||
// We only handle zero vectors here.
|
// We only handle zero vectors here.
|
||||||
// FIXME: Handle other cases where store of vector immediate is done in
|
// FIXME: Handle other cases where store of vector immediate is done in
|
||||||
// a single instruction.
|
// a single instruction.
|
||||||
Value = getMemsetStringVal(VT, dl, DAG, TLI, Str.substr(SrcOff));
|
ConstantDataArraySlice SubSlice;
|
||||||
|
if (SrcOff < Slice.Length) {
|
||||||
|
SubSlice = Slice;
|
||||||
|
SubSlice.move(SrcOff);
|
||||||
|
} else {
|
||||||
|
// This is an out-of-bounds access and hence UB. Pretend we read zero.
|
||||||
|
SubSlice.Array = nullptr;
|
||||||
|
SubSlice.Offset = 0;
|
||||||
|
SubSlice.Length = VTSize;
|
||||||
|
}
|
||||||
|
Value = getMemsetStringVal(VT, dl, DAG, TLI, SubSlice);
|
||||||
if (Value.getNode())
|
if (Value.getNode())
|
||||||
Store = DAG.getStore(Chain, dl, Value,
|
Store = DAG.getStore(Chain, dl, Value,
|
||||||
DAG.getMemBasePlusOffset(Dst, DstOff, dl),
|
DAG.getMemBasePlusOffset(Dst, DstOff, dl),
|
||||||
DstPtrInfo.getWithOffset(DstOff), Align, MMOFlags);
|
DstPtrInfo.getWithOffset(DstOff), Align,
|
||||||
|
MMOFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Store.getNode()) {
|
if (!Store.getNode()) {
|
||||||
|
|
|
@ -2616,8 +2616,8 @@ Constant *ConstantDataSequential::getElementAsConstant(unsigned Elt) const {
|
||||||
return ConstantInt::get(getElementType(), getElementAsInteger(Elt));
|
return ConstantInt::get(getElementType(), getElementAsInteger(Elt));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConstantDataSequential::isString() const {
|
bool ConstantDataSequential::isString(unsigned CharSize) const {
|
||||||
return isa<ArrayType>(getType()) && getElementType()->isIntegerTy(8);
|
return isa<ArrayType>(getType()) && getElementType()->isIntegerTy(CharSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConstantDataSequential::isCString() const {
|
bool ConstantDataSequential::isCString() const {
|
||||||
|
|
|
@ -426,11 +426,12 @@ Value *LibCallSimplifier::optimizeStrNCpy(CallInst *CI, IRBuilder<> &B) {
|
||||||
return Dst;
|
return Dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilder<> &B) {
|
Value *LibCallSimplifier::optimizeStringLength(CallInst *CI, IRBuilder<> &B,
|
||||||
|
unsigned CharSize) {
|
||||||
Value *Src = CI->getArgOperand(0);
|
Value *Src = CI->getArgOperand(0);
|
||||||
|
|
||||||
// Constant folding: strlen("xyz") -> 3
|
// Constant folding: strlen("xyz") -> 3
|
||||||
if (uint64_t Len = GetStringLength(Src))
|
if (uint64_t Len = GetStringLength(Src, CharSize))
|
||||||
return ConstantInt::get(CI->getType(), Len - 1);
|
return ConstantInt::get(CI->getType(), Len - 1);
|
||||||
|
|
||||||
// If s is a constant pointer pointing to a string literal, we can fold
|
// If s is a constant pointer pointing to a string literal, we can fold
|
||||||
|
@ -442,24 +443,34 @@ Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilder<> &B) {
|
||||||
// very useful because calling strlen for a pointer of other types is
|
// very useful because calling strlen for a pointer of other types is
|
||||||
// very uncommon.
|
// very uncommon.
|
||||||
if (GEPOperator *GEP = dyn_cast<GEPOperator>(Src)) {
|
if (GEPOperator *GEP = dyn_cast<GEPOperator>(Src)) {
|
||||||
if (!isGEPBasedOnPointerToString(GEP))
|
if (!isGEPBasedOnPointerToString(GEP, CharSize))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
StringRef Str;
|
ConstantDataArraySlice Slice;
|
||||||
if (getConstantStringInfo(GEP->getOperand(0), Str, 0, false)) {
|
if (getConstantDataArrayInfo(GEP->getOperand(0), Slice, CharSize)) {
|
||||||
size_t NullTermIdx = Str.find('\0');
|
uint64_t NullTermIdx;
|
||||||
|
if (Slice.Array == nullptr) {
|
||||||
|
NullTermIdx = 0;
|
||||||
|
} else {
|
||||||
|
NullTermIdx = ~((uint64_t)0);
|
||||||
|
for (uint64_t I = 0, E = Slice.Length; I < E; ++I) {
|
||||||
|
if (Slice.Array->getElementAsInteger(I + Slice.Offset) == 0) {
|
||||||
|
NullTermIdx = I;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
// If the string does not have '\0', leave it to strlen to compute
|
// If the string does not have '\0', leave it to strlen to compute
|
||||||
// its length.
|
// its length.
|
||||||
if (NullTermIdx == StringRef::npos)
|
if (NullTermIdx == ~((uint64_t)0))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Value *Offset = GEP->getOperand(2);
|
Value *Offset = GEP->getOperand(2);
|
||||||
unsigned BitWidth = Offset->getType()->getIntegerBitWidth();
|
unsigned BitWidth = Offset->getType()->getIntegerBitWidth();
|
||||||
KnownBits Known(BitWidth);
|
KnownBits Known(BitWidth);
|
||||||
computeKnownBits(Offset, Known, DL, 0, nullptr, CI, nullptr);
|
computeKnownBits(Offset, Known, DL, 0, nullptr, CI, nullptr);
|
||||||
Known.Zero.flipAllBits();
|
Known.Zero.flipAllBits();
|
||||||
size_t ArrSize =
|
uint64_t ArrSize =
|
||||||
cast<ArrayType>(GEP->getSourceElementType())->getNumElements();
|
cast<ArrayType>(GEP->getSourceElementType())->getNumElements();
|
||||||
|
|
||||||
// KnownZero's bits are flipped, so zeros in KnownZero now represent
|
// KnownZero's bits are flipped, so zeros in KnownZero now represent
|
||||||
|
@ -474,18 +485,20 @@ Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilder<> &B) {
|
||||||
// is a pointer to an object whose memory extent is NullTermIdx+1.
|
// is a pointer to an object whose memory extent is NullTermIdx+1.
|
||||||
if ((Known.Zero.isNonNegative() && Known.Zero.ule(NullTermIdx)) ||
|
if ((Known.Zero.isNonNegative() && Known.Zero.ule(NullTermIdx)) ||
|
||||||
(GEP->isInBounds() && isa<GlobalVariable>(GEP->getOperand(0)) &&
|
(GEP->isInBounds() && isa<GlobalVariable>(GEP->getOperand(0)) &&
|
||||||
NullTermIdx == ArrSize - 1))
|
NullTermIdx == ArrSize - 1)) {
|
||||||
|
Offset = B.CreateSExtOrTrunc(Offset, CI->getType());
|
||||||
return B.CreateSub(ConstantInt::get(CI->getType(), NullTermIdx),
|
return B.CreateSub(ConstantInt::get(CI->getType(), NullTermIdx),
|
||||||
Offset);
|
Offset);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// strlen(x?"foo":"bars") --> x ? 3 : 4
|
// strlen(x?"foo":"bars") --> x ? 3 : 4
|
||||||
if (SelectInst *SI = dyn_cast<SelectInst>(Src)) {
|
if (SelectInst *SI = dyn_cast<SelectInst>(Src)) {
|
||||||
uint64_t LenTrue = GetStringLength(SI->getTrueValue());
|
uint64_t LenTrue = GetStringLength(SI->getTrueValue(), CharSize);
|
||||||
uint64_t LenFalse = GetStringLength(SI->getFalseValue());
|
uint64_t LenFalse = GetStringLength(SI->getFalseValue(), CharSize);
|
||||||
if (LenTrue && LenFalse) {
|
if (LenTrue && LenFalse) {
|
||||||
Function *Caller = CI->getParent()->getParent();
|
Function *Caller = CI->getParent()->getParent();
|
||||||
emitOptimizationRemark(CI->getContext(), "simplify-libcalls", *Caller,
|
emitOptimizationRemark(CI->getContext(), "simplify-libcalls", *Caller,
|
||||||
|
@ -505,6 +518,17 @@ Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilder<> &B) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value *LibCallSimplifier::optimizeStrLen(CallInst *CI, IRBuilder<> &B) {
|
||||||
|
return optimizeStringLength(CI, B, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *LibCallSimplifier::optimizeWcslen(CallInst *CI, IRBuilder<> &B) {
|
||||||
|
Module &M = *CI->getParent()->getParent()->getParent();
|
||||||
|
unsigned WCharSize = TLI->getWCharSize(M) * 8;
|
||||||
|
|
||||||
|
return optimizeStringLength(CI, B, WCharSize);
|
||||||
|
}
|
||||||
|
|
||||||
Value *LibCallSimplifier::optimizeStrPBrk(CallInst *CI, IRBuilder<> &B) {
|
Value *LibCallSimplifier::optimizeStrPBrk(CallInst *CI, IRBuilder<> &B) {
|
||||||
StringRef S1, S2;
|
StringRef S1, S2;
|
||||||
bool HasS1 = getConstantStringInfo(CI->getArgOperand(0), S1);
|
bool HasS1 = getConstantStringInfo(CI->getArgOperand(0), S1);
|
||||||
|
@ -2026,6 +2050,8 @@ Value *LibCallSimplifier::optimizeStringMemoryLibCall(CallInst *CI,
|
||||||
return optimizeMemMove(CI, Builder);
|
return optimizeMemMove(CI, Builder);
|
||||||
case LibFunc_memset:
|
case LibFunc_memset:
|
||||||
return optimizeMemSet(CI, Builder);
|
return optimizeMemSet(CI, Builder);
|
||||||
|
case LibFunc_wcslen:
|
||||||
|
return optimizeWcslen(CI, Builder);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,3 +190,12 @@ define i1 @test15(i32 %C) {
|
||||||
%cmp = icmp ne i8* %dst, null
|
%cmp = icmp ne i8* %dst, null
|
||||||
ret i1 %cmp
|
ret i1 %cmp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@s = internal constant [1 x i8] [i8 0], align 1
|
||||||
|
define i8* @pr32124() {
|
||||||
|
; CHECK-LABEL: @pr32124(
|
||||||
|
; CHECK-NEXT: ret i8* getelementptr inbounds ([1 x i8], [1 x i8]* @s, i32 0, i32 0)
|
||||||
|
;
|
||||||
|
%res = tail call i8* @memchr(i8* getelementptr ([1 x i8], [1 x i8]* @s, i64 0, i64 0), i32 0, i32 1)
|
||||||
|
ret i8* %res
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||||
|
; Test that the wcslen library call simplifier works correctly.
|
||||||
|
;
|
||||||
|
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||||
|
|
||||||
|
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
|
||||||
|
declare i64 @wcslen(i32*)
|
||||||
|
|
||||||
|
@hello = constant [6 x i32] [i32 104, i32 101, i32 108, i32 108, i32 111, i32 0]
|
||||||
|
@longer = constant [7 x i32] [i32 108, i32 111, i32 110, i32 103, i32 101, i32 114, i32 0]
|
||||||
|
@null = constant [1 x i32] zeroinitializer
|
||||||
|
@null_hello = constant [7 x i32] [i32 0, i32 104, i32 101, i32 108, i32 108, i32 111, i32 0]
|
||||||
|
@nullstring = constant i32 0
|
||||||
|
@a = common global [32 x i32] zeroinitializer, align 1
|
||||||
|
@null_hello_mid = constant [13 x i32] [i32 104, i32 101, i32 108, i32 108, i32 111, i32 32, i32 119, i32 111, i32 114, i32 0, i32 108, i32 100, i32 0]
|
||||||
|
|
||||||
|
define i64 @test_simplify1() {
|
||||||
|
; CHECK-LABEL: @test_simplify1(
|
||||||
|
; CHECK-NEXT: ret i64 5
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr [6 x i32], [6 x i32]* @hello, i64 0, i64 0
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify2() {
|
||||||
|
; CHECK-LABEL: @test_simplify2(
|
||||||
|
; CHECK-NEXT: ret i64 0
|
||||||
|
;
|
||||||
|
%null_p = getelementptr [1 x i32], [1 x i32]* @null, i64 0, i64 0
|
||||||
|
%null_l = call i64 @wcslen(i32* %null_p)
|
||||||
|
ret i64 %null_l
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify3() {
|
||||||
|
; CHECK-LABEL: @test_simplify3(
|
||||||
|
; CHECK-NEXT: ret i64 0
|
||||||
|
;
|
||||||
|
%null_hello_p = getelementptr [7 x i32], [7 x i32]* @null_hello, i64 0, i64 0
|
||||||
|
%null_hello_l = call i64 @wcslen(i32* %null_hello_p)
|
||||||
|
ret i64 %null_hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify4() {
|
||||||
|
; CHECK-LABEL: @test_simplify4(
|
||||||
|
; CHECK-NEXT: ret i64 0
|
||||||
|
;
|
||||||
|
%len = tail call i64 @wcslen(i32* @nullstring) nounwind
|
||||||
|
ret i64 %len
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check wcslen(x) == 0 --> *x == 0.
|
||||||
|
|
||||||
|
define i1 @test_simplify5() {
|
||||||
|
; CHECK-LABEL: @test_simplify5(
|
||||||
|
; CHECK-NEXT: ret i1 false
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr [6 x i32], [6 x i32]* @hello, i64 0, i64 0
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p)
|
||||||
|
%eq_hello = icmp eq i64 %hello_l, 0
|
||||||
|
ret i1 %eq_hello
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @test_simplify6(i32* %str_p) {
|
||||||
|
; CHECK-LABEL: @test_simplify6(
|
||||||
|
; CHECK-NEXT: [[STRLENFIRST:%.*]] = load i32, i32* [[STR_P:%.*]], align 4
|
||||||
|
; CHECK-NEXT: [[EQ_NULL:%.*]] = icmp eq i32 [[STRLENFIRST]], 0
|
||||||
|
; CHECK-NEXT: ret i1 [[EQ_NULL]]
|
||||||
|
;
|
||||||
|
%str_l = call i64 @wcslen(i32* %str_p)
|
||||||
|
%eq_null = icmp eq i64 %str_l, 0
|
||||||
|
ret i1 %eq_null
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check wcslen(x) != 0 --> *x != 0.
|
||||||
|
|
||||||
|
define i1 @test_simplify7() {
|
||||||
|
; CHECK-LABEL: @test_simplify7(
|
||||||
|
; CHECK-NEXT: ret i1 true
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr [6 x i32], [6 x i32]* @hello, i64 0, i64 0
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p)
|
||||||
|
%ne_hello = icmp ne i64 %hello_l, 0
|
||||||
|
ret i1 %ne_hello
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @test_simplify8(i32* %str_p) {
|
||||||
|
; CHECK-LABEL: @test_simplify8(
|
||||||
|
; CHECK-NEXT: [[STRLENFIRST:%.*]] = load i32, i32* [[STR_P:%.*]], align 4
|
||||||
|
; CHECK-NEXT: [[NE_NULL:%.*]] = icmp ne i32 [[STRLENFIRST]], 0
|
||||||
|
; CHECK-NEXT: ret i1 [[NE_NULL]]
|
||||||
|
;
|
||||||
|
%str_l = call i64 @wcslen(i32* %str_p)
|
||||||
|
%ne_null = icmp ne i64 %str_l, 0
|
||||||
|
ret i1 %ne_null
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify9(i1 %x) {
|
||||||
|
; CHECK-LABEL: @test_simplify9(
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i64 5, i64 6
|
||||||
|
; CHECK-NEXT: ret i64 [[TMP1]]
|
||||||
|
;
|
||||||
|
%hello = getelementptr [6 x i32], [6 x i32]* @hello, i64 0, i64 0
|
||||||
|
%longer = getelementptr [7 x i32], [7 x i32]* @longer, i64 0, i64 0
|
||||||
|
%s = select i1 %x, i32* %hello, i32* %longer
|
||||||
|
%l = call i64 @wcslen(i32* %s)
|
||||||
|
ret i64 %l
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check the case that should be simplified to a sub instruction.
|
||||||
|
; wcslen(@hello + x) --> 5 - x
|
||||||
|
|
||||||
|
define i64 @test_simplify10(i32 %x) {
|
||||||
|
; CHECK-LABEL: @test_simplify10(
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[X:%.*]] to i64
|
||||||
|
; CHECK-NEXT: [[TMP2:%.*]] = sub nsw i64 5, [[TMP1]]
|
||||||
|
; CHECK-NEXT: ret i64 [[TMP2]]
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr inbounds [6 x i32], [6 x i32]* @hello, i32 0, i32 %x
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; wcslen(@null_hello_mid + (x & 7)) --> 9 - (x & 7)
|
||||||
|
|
||||||
|
define i64 @test_simplify11(i32 %x) {
|
||||||
|
; CHECK-LABEL: @test_simplify11(
|
||||||
|
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 7
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[AND]] to i64
|
||||||
|
; CHECK-NEXT: [[TMP2:%.*]] = sub nsw i64 9, [[TMP1]]
|
||||||
|
; CHECK-NEXT: ret i64 [[TMP2]]
|
||||||
|
;
|
||||||
|
%and = and i32 %x, 7
|
||||||
|
%hello_p = getelementptr inbounds [13 x i32], [13 x i32]* @null_hello_mid, i32 0, i32 %and
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check cases that shouldn't be simplified.
|
||||||
|
|
||||||
|
define i64 @test_no_simplify1() {
|
||||||
|
; CHECK-LABEL: @test_no_simplify1(
|
||||||
|
; CHECK-NEXT: [[A_L:%.*]] = call i64 @wcslen(i32* getelementptr inbounds ([32 x i32], [32 x i32]* @a, i64 0, i64 0))
|
||||||
|
; CHECK-NEXT: ret i64 [[A_L]]
|
||||||
|
;
|
||||||
|
%a_p = getelementptr [32 x i32], [32 x i32]* @a, i64 0, i64 0
|
||||||
|
%a_l = call i64 @wcslen(i32* %a_p)
|
||||||
|
ret i64 %a_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; wcslen(@null_hello + x) should not be simplified to a sub instruction.
|
||||||
|
|
||||||
|
define i64 @test_no_simplify2(i32 %x) {
|
||||||
|
; CHECK-LABEL: @test_no_simplify2(
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = sext i32 [[X:%.*]] to i64
|
||||||
|
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [7 x i32], [7 x i32]* @null_hello, i64 0, i64 [[TMP1]]
|
||||||
|
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(i32* [[HELLO_P]])
|
||||||
|
; CHECK-NEXT: ret i64 [[HELLO_L]]
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr inbounds [7 x i32], [7 x i32]* @null_hello, i32 0, i32 %x
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; wcslen(@null_hello_mid + (x & 15)) should not be simplified to a sub instruction.
|
||||||
|
|
||||||
|
define i64 @test_no_simplify3(i32 %x) {
|
||||||
|
; CHECK-LABEL: @test_no_simplify3(
|
||||||
|
; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], 15
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[AND]] to i64
|
||||||
|
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [13 x i32], [13 x i32]* @null_hello_mid, i64 0, i64 [[TMP1]]
|
||||||
|
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(i32* [[HELLO_P]])
|
||||||
|
; CHECK-NEXT: ret i64 [[HELLO_L]]
|
||||||
|
;
|
||||||
|
%and = and i32 %x, 15
|
||||||
|
%hello_p = getelementptr inbounds [13 x i32], [13 x i32]* @null_hello_mid, i32 0, i32 %and
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
@str16 = constant [1 x i16] [i16 0]
|
||||||
|
|
||||||
|
define i64 @test_no_simplify4() {
|
||||||
|
; CHECK-LABEL: @test_no_simplify4(
|
||||||
|
; CHECK-NEXT: [[L:%.*]] = call i64 @wcslen(i32* bitcast ([1 x i16]* @str16 to i32*))
|
||||||
|
; CHECK-NEXT: ret i64 [[L]]
|
||||||
|
;
|
||||||
|
%l = call i64 @wcslen(i32* bitcast ([1 x i16]* @str16 to i32*))
|
||||||
|
ret i64 %l
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
; Test that the wcslen library call simplifier works correctly.
|
||||||
|
;
|
||||||
|
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||||
|
|
||||||
|
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
|
||||||
|
@hello = constant [6 x i32] [i32 104, i32 101, i32 108, i32 108, i32 111, i32 0]
|
||||||
|
|
||||||
|
declare i64 @wcslen(i32*, i32)
|
||||||
|
|
||||||
|
define i64 @test_no_simplify1() {
|
||||||
|
; CHECK-LABEL: @test_no_simplify1(
|
||||||
|
%hello_p = getelementptr [6 x i32], [6 x i32]* @hello, i64 0, i64 0
|
||||||
|
%hello_l = call i64 @wcslen(i32* %hello_p, i32 187)
|
||||||
|
; CHECK-NEXT: %hello_l = call i64 @wcslen
|
||||||
|
ret i64 %hello_l
|
||||||
|
; CHECK-NEXT: ret i64 %hello_l
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||||
|
; Test that the wcslen library call simplifier works correctly.
|
||||||
|
;
|
||||||
|
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||||
|
|
||||||
|
; Test behavior for wchar_size==2
|
||||||
|
!llvm.module.flags = !{!0}
|
||||||
|
!0 = !{i32 1, !"wchar_size", i32 2}
|
||||||
|
|
||||||
|
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
|
||||||
|
declare i64 @wcslen(i16*)
|
||||||
|
|
||||||
|
@hello = constant [6 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
|
||||||
|
@longer = constant [7 x i16] [i16 108, i16 111, i16 110, i16 103, i16 101, i16 114, i16 0]
|
||||||
|
@null = constant [1 x i16] zeroinitializer
|
||||||
|
@null_hello = constant [7 x i16] [i16 0, i16 104, i16 101, i16 108, i16 108, i16 111, i16 0]
|
||||||
|
@nullstring = constant i16 0
|
||||||
|
@a = common global [32 x i16] zeroinitializer, align 1
|
||||||
|
@null_hello_mid = constant [13 x i16] [i16 104, i16 101, i16 108, i16 108, i16 111, i16 32, i16 119, i16 111, i16 114, i16 0, i16 108, i16 100, i16 0]
|
||||||
|
|
||||||
|
define i64 @test_simplify1() {
|
||||||
|
; CHECK-LABEL: @test_simplify1(
|
||||||
|
; CHECK-NEXT: ret i64 5
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr [6 x i16], [6 x i16]* @hello, i64 0, i64 0
|
||||||
|
%hello_l = call i64 @wcslen(i16* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify2() {
|
||||||
|
; CHECK-LABEL: @test_simplify2(
|
||||||
|
; CHECK-NEXT: ret i64 0
|
||||||
|
;
|
||||||
|
%null_p = getelementptr [1 x i16], [1 x i16]* @null, i64 0, i64 0
|
||||||
|
%null_l = call i64 @wcslen(i16* %null_p)
|
||||||
|
ret i64 %null_l
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify3() {
|
||||||
|
; CHECK-LABEL: @test_simplify3(
|
||||||
|
; CHECK-NEXT: ret i64 0
|
||||||
|
;
|
||||||
|
%null_hello_p = getelementptr [7 x i16], [7 x i16]* @null_hello, i64 0, i64 0
|
||||||
|
%null_hello_l = call i64 @wcslen(i16* %null_hello_p)
|
||||||
|
ret i64 %null_hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify4() {
|
||||||
|
; CHECK-LABEL: @test_simplify4(
|
||||||
|
; CHECK-NEXT: ret i64 0
|
||||||
|
;
|
||||||
|
%len = tail call i64 @wcslen(i16* @nullstring) nounwind
|
||||||
|
ret i64 %len
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check wcslen(x) == 0 --> *x == 0.
|
||||||
|
|
||||||
|
define i1 @test_simplify5() {
|
||||||
|
; CHECK-LABEL: @test_simplify5(
|
||||||
|
; CHECK-NEXT: ret i1 false
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr [6 x i16], [6 x i16]* @hello, i64 0, i64 0
|
||||||
|
%hello_l = call i64 @wcslen(i16* %hello_p)
|
||||||
|
%eq_hello = icmp eq i64 %hello_l, 0
|
||||||
|
ret i1 %eq_hello
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @test_simplify6(i16* %str_p) {
|
||||||
|
; CHECK-LABEL: @test_simplify6(
|
||||||
|
; CHECK-NEXT: [[STRLENFIRST:%.*]] = load i16, i16* [[STR_P:%.*]], align 2
|
||||||
|
; CHECK-NEXT: [[EQ_NULL:%.*]] = icmp eq i16 [[STRLENFIRST]], 0
|
||||||
|
; CHECK-NEXT: ret i1 [[EQ_NULL]]
|
||||||
|
;
|
||||||
|
%str_l = call i64 @wcslen(i16* %str_p)
|
||||||
|
%eq_null = icmp eq i64 %str_l, 0
|
||||||
|
ret i1 %eq_null
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check wcslen(x) != 0 --> *x != 0.
|
||||||
|
|
||||||
|
define i1 @test_simplify7() {
|
||||||
|
; CHECK-LABEL: @test_simplify7(
|
||||||
|
; CHECK-NEXT: ret i1 true
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr [6 x i16], [6 x i16]* @hello, i64 0, i64 0
|
||||||
|
%hello_l = call i64 @wcslen(i16* %hello_p)
|
||||||
|
%ne_hello = icmp ne i64 %hello_l, 0
|
||||||
|
ret i1 %ne_hello
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @test_simplify8(i16* %str_p) {
|
||||||
|
; CHECK-LABEL: @test_simplify8(
|
||||||
|
; CHECK-NEXT: [[STRLENFIRST:%.*]] = load i16, i16* [[STR_P:%.*]], align 2
|
||||||
|
; CHECK-NEXT: [[NE_NULL:%.*]] = icmp ne i16 [[STRLENFIRST]], 0
|
||||||
|
; CHECK-NEXT: ret i1 [[NE_NULL]]
|
||||||
|
;
|
||||||
|
%str_l = call i64 @wcslen(i16* %str_p)
|
||||||
|
%ne_null = icmp ne i64 %str_l, 0
|
||||||
|
ret i1 %ne_null
|
||||||
|
}
|
||||||
|
|
||||||
|
define i64 @test_simplify9(i1 %x) {
|
||||||
|
; CHECK-LABEL: @test_simplify9(
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[X:%.*]], i64 5, i64 6
|
||||||
|
; CHECK-NEXT: ret i64 [[TMP1]]
|
||||||
|
;
|
||||||
|
%hello = getelementptr [6 x i16], [6 x i16]* @hello, i64 0, i64 0
|
||||||
|
%longer = getelementptr [7 x i16], [7 x i16]* @longer, i64 0, i64 0
|
||||||
|
%s = select i1 %x, i16* %hello, i16* %longer
|
||||||
|
%l = call i64 @wcslen(i16* %s)
|
||||||
|
ret i64 %l
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check the case that should be simplified to a sub instruction.
|
||||||
|
; wcslen(@hello + x) --> 5 - x
|
||||||
|
|
||||||
|
define i64 @test_simplify10(i16 %x) {
|
||||||
|
; CHECK-LABEL: @test_simplify10(
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = sext i16 [[X:%.*]] to i64
|
||||||
|
; CHECK-NEXT: [[TMP2:%.*]] = sub nsw i64 5, [[TMP1]]
|
||||||
|
; CHECK-NEXT: ret i64 [[TMP2]]
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr inbounds [6 x i16], [6 x i16]* @hello, i16 0, i16 %x
|
||||||
|
%hello_l = call i64 @wcslen(i16* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; wcslen(@null_hello_mid + (x & 7)) --> 9 - (x & 7)
|
||||||
|
|
||||||
|
define i64 @test_simplify11(i16 %x) {
|
||||||
|
; CHECK-LABEL: @test_simplify11(
|
||||||
|
; CHECK-NEXT: [[AND:%.*]] = and i16 [[X:%.*]], 7
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = zext i16 [[AND]] to i64
|
||||||
|
; CHECK-NEXT: [[TMP2:%.*]] = sub nsw i64 9, [[TMP1]]
|
||||||
|
; CHECK-NEXT: ret i64 [[TMP2]]
|
||||||
|
;
|
||||||
|
%and = and i16 %x, 7
|
||||||
|
%hello_p = getelementptr inbounds [13 x i16], [13 x i16]* @null_hello_mid, i16 0, i16 %and
|
||||||
|
%hello_l = call i64 @wcslen(i16* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; Check cases that shouldn't be simplified.
|
||||||
|
|
||||||
|
define i64 @test_no_simplify1() {
|
||||||
|
; CHECK-LABEL: @test_no_simplify1(
|
||||||
|
; CHECK-NEXT: [[A_L:%.*]] = call i64 @wcslen(i16* getelementptr inbounds ([32 x i16], [32 x i16]* @a, i64 0, i64 0))
|
||||||
|
; CHECK-NEXT: ret i64 [[A_L]]
|
||||||
|
;
|
||||||
|
%a_p = getelementptr [32 x i16], [32 x i16]* @a, i64 0, i64 0
|
||||||
|
%a_l = call i64 @wcslen(i16* %a_p)
|
||||||
|
ret i64 %a_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; wcslen(@null_hello + x) should not be simplified to a sub instruction.
|
||||||
|
|
||||||
|
define i64 @test_no_simplify2(i16 %x) {
|
||||||
|
; CHECK-LABEL: @test_no_simplify2(
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = sext i16 [[X:%.*]] to i64
|
||||||
|
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [7 x i16], [7 x i16]* @null_hello, i64 0, i64 [[TMP1]]
|
||||||
|
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(i16* [[HELLO_P]])
|
||||||
|
; CHECK-NEXT: ret i64 [[HELLO_L]]
|
||||||
|
;
|
||||||
|
%hello_p = getelementptr inbounds [7 x i16], [7 x i16]* @null_hello, i16 0, i16 %x
|
||||||
|
%hello_l = call i64 @wcslen(i16* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
; wcslen(@null_hello_mid + (x & 15)) should not be simplified to a sub instruction.
|
||||||
|
|
||||||
|
define i64 @test_no_simplify3(i16 %x) {
|
||||||
|
; CHECK-LABEL: @test_no_simplify3(
|
||||||
|
; CHECK-NEXT: [[AND:%.*]] = and i16 [[X:%.*]], 15
|
||||||
|
; CHECK-NEXT: [[TMP1:%.*]] = zext i16 [[AND]] to i64
|
||||||
|
; CHECK-NEXT: [[HELLO_P:%.*]] = getelementptr inbounds [13 x i16], [13 x i16]* @null_hello_mid, i64 0, i64 [[TMP1]]
|
||||||
|
; CHECK-NEXT: [[HELLO_L:%.*]] = call i64 @wcslen(i16* [[HELLO_P]])
|
||||||
|
; CHECK-NEXT: ret i64 [[HELLO_L]]
|
||||||
|
;
|
||||||
|
%and = and i16 %x, 15
|
||||||
|
%hello_p = getelementptr inbounds [13 x i16], [13 x i16]* @null_hello_mid, i16 0, i16 %and
|
||||||
|
%hello_l = call i64 @wcslen(i16* %hello_p)
|
||||||
|
ret i64 %hello_l
|
||||||
|
}
|
||||||
|
|
||||||
|
@str32 = constant [1 x i32] [i32 0]
|
||||||
|
|
||||||
|
; This could in principle be simplified, but the current implementation bails on
|
||||||
|
; type mismatches.
|
||||||
|
define i64 @test_no_simplify4() {
|
||||||
|
; CHECK-LABEL: @test_no_simplify4(
|
||||||
|
; CHECK-NEXT: [[L:%.*]] = call i64 @wcslen(i16* bitcast ([1 x i32]* @str32 to i16*))
|
||||||
|
; CHECK-NEXT: ret i64 [[L]]
|
||||||
|
;
|
||||||
|
%l = call i64 @wcslen(i16* bitcast ([1 x i32]* @str32 to i16*))
|
||||||
|
ret i64 %l
|
||||||
|
}
|
Loading…
Reference in New Issue