Skip to content

Commit f5135b6

Browse files
committed
Add wcslen idiom
1 parent 9027210 commit f5135b6

File tree

6 files changed

+181
-9
lines changed

6 files changed

+181
-9
lines changed

llvm/include/llvm/Transforms/Scalar/LoopIdiomRecognize.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ struct DisableLIRP {
3737

3838
/// When true, Strlen is disabled.
3939
static bool Strlen;
40+
41+
/// When true, Wcslen is disabled.
42+
static bool Wcslen;
4043
};
4144

4245
/// Performs Loop Idiom Recognize Pass.

llvm/include/llvm/Transforms/Utils/BuildLibCalls.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ namespace llvm {
9393
Value *emitStrLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL,
9494
const TargetLibraryInfo *TLI);
9595

96+
/// Emit a call to the wcslen function to the builder, for the specified
97+
/// pointer. Ptr is required to be some pointer type, and the return value has
98+
/// 'size_t' type.
99+
Value *emitWcsLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL,
100+
const TargetLibraryInfo *TLI);
101+
96102
/// Emit a call to the strdup function to the builder, for the specified
97103
/// pointer. Ptr is required to be some pointer type, and the return value has
98104
/// 'i8*' type.

llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ static cl::opt<bool, true>
136136
cl::location(DisableLIRP::Strlen), cl::init(false),
137137
cl::ReallyHidden);
138138

139+
bool DisableLIRP::Wcslen;
140+
static cl::opt<bool, true>
141+
DisableLIRPWcslen("disable-" DEBUG_TYPE "-wcslen",
142+
cl::desc("Proceed with loop idiom recognize pass, but do "
143+
"not convert loop(s) to wcslen."),
144+
cl::location(DisableLIRP::Wcslen), cl::init(false),
145+
cl::ReallyHidden);
146+
139147
static cl::opt<bool> UseLIRCodeSizeHeurs(
140148
"use-lir-code-size-heurs",
141149
cl::desc("Use loop idiom recognition code size heuristics when compiling"
@@ -1606,15 +1614,19 @@ bool LoopIdiomRecognize::recognizeAndInsertStrLen() {
16061614
if (!Step)
16071615
return false;
16081616

1609-
unsigned int ConstIntValue = 0;
1617+
unsigned int StepSize = 0;
16101618
if (ConstantInt *CI = dyn_cast<ConstantInt>(Step->getValue()))
1611-
ConstIntValue = CI->getZExtValue();
1619+
StepSize = CI->getZExtValue();
16121620

16131621
unsigned OpWidth = OperandType->getIntegerBitWidth();
1614-
if (OpWidth != ConstIntValue * 8)
1622+
unsigned WcharSize = TLI->getWCharSize(*LoopLoad->getModule());
1623+
if (OpWidth != StepSize * 8)
16151624
return false;
1616-
if (OpWidth != 8)
1625+
if (OpWidth != 8 && OpWidth != 16 && OpWidth != 32)
16171626
return false;
1627+
if (OpWidth >= 16)
1628+
if (OpWidth != WcharSize * 8 || DisableLIRPWcslen)
1629+
return false;
16181630

16191631
// Scan every instruction in the loop to ensure there are no side effects.
16201632
for (auto &I : *LoopBody)
@@ -1666,12 +1678,11 @@ bool LoopIdiomRecognize::recognizeAndInsertStrLen() {
16661678
if (auto *GEP = dyn_cast<GetElementPtrInst>(LoopLoad->getPointerOperand())) {
16671679
if (GEP->getPointerOperand() != LoopPhi)
16681680
return false;
1669-
GetElementPtrInst *NewGEP =
1670-
GetElementPtrInst::Create(GEP->getSourceElementType(), PreVal,
1671-
SmallVector<Value *, 4>(GEP->indices()),
1672-
"newgep", Preheader->getTerminator());
1681+
GetElementPtrInst *NewGEP = GetElementPtrInst::Create(
1682+
LoopLoad->getType(), PreVal, SmallVector<Value *, 4>(GEP->indices()),
1683+
"newgep", Preheader->getTerminator());
16731684
Expanded = NewGEP;
1674-
ExpandedType = NewGEP->getSourceElementType();
1685+
ExpandedType = LoopLoad->getType();
16751686
} else if (LoopLoad->getPointerOperand() == LoopPhi) {
16761687
Expanded = PreVal;
16771688
ExpandedType = LoopLoad->getType();
@@ -1718,8 +1729,15 @@ bool LoopIdiomRecognize::recognizeAndInsertStrLen() {
17181729
Value *StrLenFunc = nullptr;
17191730
switch (OpWidth) {
17201731
case 8:
1732+
if (!TLI->has(LibFunc_strlen))
1733+
return false;
17211734
StrLenFunc = emitStrLen(Expanded, Builder, *DL, TLI);
17221735
break;
1736+
case 16:
1737+
case 32:
1738+
if (!TLI->has(LibFunc_wcslen))
1739+
return false;
1740+
StrLenFunc = emitWcsLen(Expanded, Builder, *DL, TLI);
17231741
}
17241742

17251743
assert(StrLenFunc && "Failed to emit strlen function.");

llvm/lib/Transforms/Utils/BuildLibCalls.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,15 @@ Value *llvm::emitStrLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL,
15061506
return emitLibCall(LibFunc_strlen, SizeTTy, CharPtrTy, Ptr, B, TLI);
15071507
}
15081508

1509+
Value *llvm::emitWcsLen(Value *Ptr, IRBuilderBase &B, const DataLayout &DL,
1510+
const TargetLibraryInfo *TLI) {
1511+
assert(Ptr && Ptr->getType()->isPointerTy() &&
1512+
"Argument to wcslen intrinsic must be a pointer.");
1513+
Type *PtrTy = B.getPtrTy();
1514+
Type *SizeTTy = getSizeTTy(B, TLI);
1515+
return emitLibCall(LibFunc_wcslen, SizeTTy, PtrTy, Ptr, B, TLI);
1516+
}
1517+
15091518
Value *llvm::emitStrDup(Value *Ptr, IRBuilderBase &B,
15101519
const TargetLibraryInfo *TLI) {
15111520
Type *CharPtrTy = B.getPtrTy();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -passes='loop-idiom' < %s -S | FileCheck %s
3+
4+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
5+
target triple = "x86_64-unknown-linux-gnu"
6+
7+
define i64 @valid_strlen16(ptr %src) {
8+
; CHECK-LABEL: define i64 @valid_strlen16(
9+
; CHECK-SAME: ptr [[SRC:%.*]]) {
10+
; CHECK-NEXT: [[ENTRY:.*]]:
11+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null
12+
; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]]
13+
; CHECK: [[LOR_LHS_FALSE]]:
14+
; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[SRC]], align 2
15+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i16 [[TMP0]], 0
16+
; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]]
17+
; CHECK: [[WHILE_COND_PREHEADER]]:
18+
; CHECK-NEXT: [[NEWGEP:%.*]] = getelementptr i16, ptr [[SRC]], i64 -1
19+
; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[NEWGEP]])
20+
; CHECK-NEXT: [[END:%.*]] = getelementptr i16, ptr [[NEWGEP]], i64 [[WCSLEN]]
21+
; CHECK-NEXT: br label %[[WHILE_COND:.*]]
22+
; CHECK: [[WHILE_COND]]:
23+
; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ poison, %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ]
24+
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i16 poison, 0
25+
; CHECK-NEXT: br i1 true, label %[[WHILE_END:.*]], label %[[WHILE_COND]]
26+
; CHECK: [[WHILE_END]]:
27+
; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[END]] to i64
28+
; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64
29+
; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
30+
; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 1
31+
; CHECK-NEXT: br label %[[RETURN]]
32+
; CHECK: [[RETURN]]:
33+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ]
34+
; CHECK-NEXT: ret i64 [[RETVAL_0]]
35+
;
36+
entry:
37+
%cmp = icmp eq ptr %src, null
38+
br i1 %cmp, label %return, label %lor.lhs.false
39+
40+
lor.lhs.false: ; preds = %entry
41+
%0 = load i16, ptr %src, align 2
42+
%cmp1 = icmp eq i16 %0, 0
43+
br i1 %cmp1, label %return, label %while.cond
44+
45+
while.cond: ; preds = %lor.lhs.false, %while.cond
46+
%src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %lor.lhs.false ]
47+
%curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 2
48+
%1 = load i16, ptr %curr.0, align 2
49+
%tobool.not = icmp eq i16 %1, 0
50+
br i1 %tobool.not, label %while.end, label %while.cond
51+
52+
while.end: ; preds = %while.cond
53+
%sub.ptr.lhs.cast = ptrtoint ptr %curr.0 to i64
54+
%sub.ptr.rhs.cast = ptrtoint ptr %src to i64
55+
%sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
56+
%sub.ptr.div = ashr exact i64 %sub.ptr.sub, 1
57+
br label %return
58+
59+
return: ; preds = %entry, %lor.lhs.false, %while.end
60+
%retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ]
61+
ret i64 %retval.0
62+
}
63+
64+
!llvm.module.flags = !{!0}
65+
!0 = !{i32 1, !"wchar_size", i32 2}
66+
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -passes='loop-idiom' < %s -S | FileCheck %s
3+
4+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
5+
target triple = "x86_64-unknown-linux-gnu"
6+
7+
define i64 @valid_wcslen32(ptr %src) {
8+
; CHECK-LABEL: define i64 @valid_wcslen32(
9+
; CHECK-SAME: ptr [[SRC:%.*]]) {
10+
; CHECK-NEXT: [[ENTRY:.*]]:
11+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[SRC]], null
12+
; CHECK-NEXT: br i1 [[CMP]], label %[[RETURN:.*]], label %[[LOR_LHS_FALSE:.*]]
13+
; CHECK: [[LOR_LHS_FALSE]]:
14+
; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[SRC]], align 4
15+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[TMP0]], 0
16+
; CHECK-NEXT: br i1 [[CMP1]], label %[[RETURN]], label %[[WHILE_COND_PREHEADER:.*]]
17+
; CHECK: [[WHILE_COND_PREHEADER]]:
18+
; CHECK-NEXT: [[NEWGEP:%.*]] = getelementptr i32, ptr [[SRC]], i64 -3
19+
; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[NEWGEP]])
20+
; CHECK-NEXT: [[END:%.*]] = getelementptr i32, ptr [[NEWGEP]], i64 [[WCSLEN]]
21+
; CHECK-NEXT: br label %[[WHILE_COND:.*]]
22+
; CHECK: [[WHILE_COND]]:
23+
; CHECK-NEXT: [[SRC_PN:%.*]] = phi ptr [ poison, %[[WHILE_COND]] ], [ [[SRC]], %[[WHILE_COND_PREHEADER]] ]
24+
; CHECK-NEXT: [[TOBOOL_NOT:%.*]] = icmp eq i32 poison, 0
25+
; CHECK-NEXT: br i1 true, label %[[WHILE_END:.*]], label %[[WHILE_COND]]
26+
; CHECK: [[WHILE_END]]:
27+
; CHECK-NEXT: [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[END]] to i64
28+
; CHECK-NEXT: [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64
29+
; CHECK-NEXT: [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
30+
; CHECK-NEXT: [[SUB_PTR_DIV:%.*]] = ashr exact i64 [[SUB_PTR_SUB]], 2
31+
; CHECK-NEXT: br label %[[RETURN]]
32+
; CHECK: [[RETURN]]:
33+
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i64 [ [[SUB_PTR_DIV]], %[[WHILE_END]] ], [ 0, %[[LOR_LHS_FALSE]] ], [ 0, %[[ENTRY]] ]
34+
; CHECK-NEXT: ret i64 [[RETVAL_0]]
35+
;
36+
entry:
37+
%cmp = icmp eq ptr %src, null
38+
br i1 %cmp, label %return, label %lor.lhs.false
39+
40+
lor.lhs.false: ; preds = %entry
41+
%0 = load i32, ptr %src, align 4
42+
%cmp1 = icmp eq i32 %0, 0
43+
br i1 %cmp1, label %return, label %while.cond.preheader
44+
45+
while.cond.preheader: ; preds = %lor.lhs.false
46+
br label %while.cond
47+
48+
while.cond: ; preds = %while.cond.preheader, %while.cond
49+
%src.pn = phi ptr [ %curr.0, %while.cond ], [ %src, %while.cond.preheader ]
50+
%curr.0 = getelementptr inbounds i8, ptr %src.pn, i64 4
51+
%1 = load i32, ptr %curr.0, align 4
52+
%tobool.not = icmp eq i32 %1, 0
53+
br i1 %tobool.not, label %while.end, label %while.cond
54+
55+
while.end: ; preds = %while.cond
56+
%curr.0.lcssa = phi ptr [ %curr.0, %while.cond ]
57+
%sub.ptr.lhs.cast = ptrtoint ptr %curr.0.lcssa to i64
58+
%sub.ptr.rhs.cast = ptrtoint ptr %src to i64
59+
%sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
60+
%sub.ptr.div = ashr exact i64 %sub.ptr.sub, 2
61+
br label %return
62+
63+
return: ; preds = %entry, %lor.lhs.false, %while.end
64+
%retval.0 = phi i64 [ %sub.ptr.div, %while.end ], [ 0, %lor.lhs.false ], [ 0, %entry ]
65+
ret i64 %retval.0
66+
}
67+
68+
!llvm.module.flags = !{!0}
69+
!0 = !{i32 1, !"wchar_size", i32 4}
70+

0 commit comments

Comments
 (0)