Value *emitStrLen(Value *Ptr, IRBuilder<> &B, const DataLayout &DL,
const TargetLibraryInfo *TLI);
+ /// Emit a call to the strdup function to the builder, for the specified
+ /// pointer. Ptr is required to be some pointer type, and the return value has
+ /// 'i8*' type.
+ Value *emitStrDup(Value *Ptr, IRBuilder<> &B, const TargetLibraryInfo *TLI);
+
/// Emit a call to the strnlen function to the builder, for the specified
/// pointer. Ptr is required to be some pointer type, MaxLen must be of size_t
/// type, and the return value has 'intptr_t' type.
Value *optimizeStrRChr(CallInst *CI, IRBuilder<> &B);
Value *optimizeStrCmp(CallInst *CI, IRBuilder<> &B);
Value *optimizeStrNCmp(CallInst *CI, IRBuilder<> &B);
+ Value *optimizeStrNDup(CallInst *CI, IRBuilder<> &B);
Value *optimizeStrCpy(CallInst *CI, IRBuilder<> &B);
Value *optimizeStpCpy(CallInst *CI, IRBuilder<> &B);
Value *optimizeStrNCpy(CallInst *CI, IRBuilder<> &B);
if (isAllocationFn(&Call, &TLI))
annotateAnyAllocSite(Call, &TLI);
- if (isAllocLikeFn(&Call, &TLI))
- return visitAllocSite(Call);
-
bool Changed = false;
// Mark any parameters that are known to be non-null with the nonnull
if (I) return eraseInstFromFunction(*I);
}
+ if (isAllocLikeFn(&Call, &TLI))
+ return visitAllocSite(Call);
+
return Changed ? &Call : nullptr;
}
B.getInt8PtrTy(), castToCStr(Ptr, B), B, TLI);
}
+Value *llvm::emitStrDup(Value *Ptr, IRBuilder<> &B,
+ const TargetLibraryInfo *TLI) {
+ return emitLibCall(LibFunc_strdup, B.getInt8PtrTy(), B.getInt8PtrTy(),
+ castToCStr(Ptr, B), B, TLI);
+}
+
Value *llvm::emitStrChr(Value *Ptr, char C, IRBuilder<> &B,
const TargetLibraryInfo *TLI) {
Type *I8Ptr = B.getInt8PtrTy();
return nullptr;
}
+Value *LibCallSimplifier::optimizeStrNDup(CallInst *CI, IRBuilder<> &B) {
+ Value *Src = CI->getArgOperand(0);
+ ConstantInt *Size = dyn_cast<ConstantInt>(CI->getArgOperand(1));
+ uint64_t SrcLen = GetStringLength(Src);
+ if (SrcLen && Size) {
+ annotateDereferenceableBytes(CI, 0, SrcLen);
+ if (SrcLen <= Size->getZExtValue() + 1)
+ return emitStrDup(Src, B, TLI);
+ }
+
+ return nullptr;
+}
+
Value *LibCallSimplifier::optimizeStrCpy(CallInst *CI, IRBuilder<> &B) {
Value *Dst = CI->getArgOperand(0), *Src = CI->getArgOperand(1);
if (Dst == Src) // strcpy(x,x) -> x
return optimizeStrLen(CI, Builder);
case LibFunc_strpbrk:
return optimizeStrPBrk(CI, Builder);
+ case LibFunc_strndup:
+ return optimizeStrNDup(CI, Builder);
case LibFunc_strtol:
case LibFunc_strtod:
case LibFunc_strtof:
define i32 @test10(i8** %esc) {
; CHECK-LABEL: @test10(
-; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strndup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 3) #0
+; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strndup(i8* dereferenceable(8) getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 3) #0
; CHECK-NEXT: store i8* [[CALL]], i8** [[ESC:%.*]], align 8
; CHECK-NEXT: ret i32 4
;
define i32 @test11(i8** %esc) {
; CHECK-LABEL: @test11(
-; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strndup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 7) #0
-; CHECK-NEXT: store i8* [[CALL]], i8** [[ESC:%.*]], align 8
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(8) i8* @strdup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0))
+; CHECK-NEXT: store i8* [[STRDUP]], i8** [[ESC:%.*]], align 8
; CHECK-NEXT: ret i32 8
;
%call = tail call i8* @strndup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i64 0, i64 0), i32 7) nounwind
define i32 @test12(i8** %esc) {
; CHECK-LABEL: @test12(
-; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strndup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 8) #0
-; CHECK-NEXT: store i8* [[CALL]], i8** [[ESC:%.*]], align 8
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(8) i8* @strdup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0))
+; CHECK-NEXT: store i8* [[STRDUP]], i8** [[ESC:%.*]], align 8
; CHECK-NEXT: ret i32 8
;
%call = tail call i8* @strndup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i64 0, i64 0), i32 8) nounwind
define i32 @test13(i8** %esc) {
; CHECK-LABEL: @test13(
-; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strndup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 57) #0
-; CHECK-NEXT: store i8* [[CALL]], i8** [[ESC:%.*]], align 8
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(8) i8* @strdup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0))
+; CHECK-NEXT: store i8* [[STRDUP]], i8** [[ESC:%.*]], align 8
; CHECK-NEXT: ret i32 8
;
%call = tail call i8* @strndup(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i64 0, i64 0), i32 57) nounwind
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -instcombine -S | FileCheck %s
+
+@hello = constant [6 x i8] c"hello\00"
+@null = constant [1 x i8] zeroinitializer
+
+declare i8* @strndup(i8*, i32)
+
+define i8* @test1() {
+; CHECK-LABEL: @test1(
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(1) i8* @strdup(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @null, i64 0, i64 0))
+; CHECK-NEXT: ret i8* [[STRDUP]]
+;
+ %src = getelementptr [1 x i8], [1 x i8]* @null, i32 0, i32 0
+ %ret = call i8* @strndup(i8* %src, i32 0)
+ ret i8* %ret
+}
+
+define i8* @test2() {
+; CHECK-LABEL: @test2(
+; CHECK-NEXT: [[RET:%.*]] = call i8* @strndup(i8* dereferenceable(6) getelementptr inbounds ([6 x i8], [6 x i8]* @hello, i64 0, i64 0), i32 4)
+; CHECK-NEXT: ret i8* [[RET]]
+;
+ %src = getelementptr [6 x i8], [6 x i8]* @hello, i32 0, i32 0
+ %ret = call i8* @strndup(i8* %src, i32 4)
+ ret i8* %ret
+}
+
+define i8* @test3() {
+; CHECK-LABEL: @test3(
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(6) i8* @strdup(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @hello, i64 0, i64 0))
+; CHECK-NEXT: ret i8* [[STRDUP]]
+;
+ %src = getelementptr [6 x i8], [6 x i8]* @hello, i32 0, i32 0
+ %ret = call i8* @strndup(i8* %src, i32 5)
+ ret i8* %ret
+}
+
+define i8* @test4() {
+; CHECK-LABEL: @test4(
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(6) i8* @strdup(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @hello, i64 0, i64 0))
+; CHECK-NEXT: ret i8* [[STRDUP]]
+;
+ %src = getelementptr [6 x i8], [6 x i8]* @hello, i32 0, i32 0
+ %ret = call i8* @strndup(i8* %src, i32 6)
+ ret i8* %ret
+}
+
+define i8* @test5() {
+; CHECK-LABEL: @test5(
+; CHECK-NEXT: [[STRDUP:%.*]] = call dereferenceable_or_null(6) i8* @strdup(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @hello, i64 0, i64 0))
+; CHECK-NEXT: ret i8* [[STRDUP]]
+;
+ %src = getelementptr [6 x i8], [6 x i8]* @hello, i32 0, i32 0
+ %ret = call i8* @strndup(i8* %src, i32 7)
+ ret i8* %ret
+}
+
+define i8* @test6(i32 %n) {
+; CHECK-LABEL: @test6(
+; CHECK-NEXT: [[RET:%.*]] = call i8* @strndup(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @hello, i64 0, i64 0), i32 [[N:%.*]])
+; CHECK-NEXT: ret i8* [[RET]]
+;
+ %src = getelementptr [6 x i8], [6 x i8]* @hello, i32 0, i32 0
+ %ret = call i8* @strndup(i8* %src, i32 %n)
+ ret i8* %ret
+}