Skip to content

Commit ed91ad3

Browse files
committed
transform: avoid byte slice string length allocations
Rewrite len(string(b)) to use the original slice length. This is always valid even when the copied string must remain for other uses.
1 parent a84a954 commit ed91ad3

5 files changed

Lines changed: 32 additions & 30 deletions

File tree

transform/rtcalls.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,23 @@ func OptimizeStringToBytes(mod llvm.Module) {
6969
}
7070
}
7171

72+
func optimizeStringFromBytesLen(stringFromBytes llvm.Value) {
73+
for _, call := range getUses(stringFromBytes) {
74+
for _, use := range getUses(call) {
75+
if use.IsAExtractValueInst().IsNil() {
76+
continue
77+
}
78+
indices := use.Indices()
79+
if len(indices) != 1 || indices[0] != 1 {
80+
continue
81+
}
82+
use.ReplaceAllUsesWith(call.Operand(1))
83+
use.EraseFromParentAsInstruction()
84+
}
85+
removeDeadStringFromBytes(stringFromBytesValue{call: call})
86+
}
87+
}
88+
7289
// OptimizeStringFromBytes transforms temporary strings created from []byte
7390
// slices into direct uses of the slice data when no instruction between the
7491
// conversion and use can mutate the slice.
@@ -96,6 +113,7 @@ func OptimizeStringFromBytes(mod llvm.Module) {
96113
for _, compare := range compareFuncs {
97114
optimizeStringFromBytesCompareUses(stringFromBytes, compare, safeCalls)
98115
}
116+
optimizeStringFromBytesLen(stringFromBytes)
99117
}
100118

101119
func optimizeStringFromBytesCompareUses(stringFromBytes, compare llvm.Value, safeCalls map[llvm.Value]struct{}) {

transform/rtcalls_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ func TestOptimizeStringFromBytesStringLess(t *testing.T) {
4040
func TestOptimizeStringFromBytesLen(t *testing.T) {
4141
t.Parallel()
4242
testTransform(t, "testdata/stringfrombytes-len", func(mod llvm.Module) {
43-
// TODO: optimize the []byte-to-string conversion away.
43+
transform.OptimizeStringFromBytes(mod)
4444
})
4545
}

transform/testdata/stringfrombytes-len.out.ll

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,13 @@ declare void @useString(ptr, i32)
1212
define i32 @main.stringFromBytesLen(ptr %a.data, i32 %a.len, i32 %a.cap, ptr %context) {
1313
entry:
1414
%stackalloc = alloca i8, align 1
15-
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 %a.len, i32 %a.cap, ptr undef)
16-
%1 = extractvalue %runtime._string %0, 0
17-
call void @runtime.trackPointer(ptr %1, ptr %stackalloc, ptr undef)
18-
%2 = extractvalue %runtime._string %0, 1
19-
%len = extractvalue %runtime._string %0, 1
20-
ret i32 %len
15+
ret i32 %a.len
2116
}
2217

2318
define i32 @main.keepStringConversion(ptr %a.data, i32 %a.len, i32 %a.cap, ptr %context) {
2419
entry:
2520
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 %a.len, i32 %a.cap, ptr undef)
2621
%1 = extractvalue %runtime._string %0, 0
27-
%2 = extractvalue %runtime._string %0, 1
28-
call void @useString(ptr %1, i32 %2)
29-
%len = extractvalue %runtime._string %0, 1
30-
ret i32 %len
22+
call void @useString(ptr %1, i32 %a.len)
23+
ret i32 %a.len
3124
}

transform/testdata/stringfrombytes-stringequal.out.ll

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,31 +31,25 @@ define i1 @main.keepStringConversion(ptr %a.data, i32 %a.len, i32 %a.cap, ptr %b
3131
entry:
3232
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 %a.len, i32 %a.cap, ptr undef)
3333
%1 = extractvalue %runtime._string %0, 0
34-
%2 = extractvalue %runtime._string %0, 1
35-
call void @useString(ptr %1, i32 %2)
36-
%3 = call i1 @runtime.stringEqual(ptr %1, i32 %2, ptr %b.data, i32 %b.len, ptr undef)
37-
ret i1 %3
34+
call void @useString(ptr %1, i32 %a.len)
35+
%2 = call i1 @runtime.stringEqual(ptr %1, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef)
36+
ret i1 %2
3837
}
3938

4039
define i32 @main.equalAndLen(ptr %a.data, i32 %a.len, i32 %a.cap, ptr %s.data, i32 %s.len, ptr %context) {
4140
entry:
42-
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 %a.len, i32 %a.cap, ptr undef)
43-
%1 = extractvalue %runtime._string %0, 0
44-
%2 = extractvalue %runtime._string %0, 1
4541
%equal = call i1 @runtime.stringEqual(ptr %a.data, i32 %a.len, ptr %s.data, i32 %s.len, ptr undef)
46-
%len = extractvalue %runtime._string %0, 1
4742
%equal.ext = zext i1 %equal to i32
48-
%result = add i32 %len, %equal.ext
43+
%result = add i32 %a.len, %equal.ext
4944
ret i32 %result
5045
}
5146

5247
define i1 @main.equalBeforeOtherUse(ptr %a.data, i32 %a.len, i32 %a.cap, ptr %s.data, i32 %s.len, ptr %context) {
5348
entry:
5449
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 %a.len, i32 %a.cap, ptr undef)
5550
%1 = extractvalue %runtime._string %0, 0
56-
%2 = extractvalue %runtime._string %0, 1
5751
%equal = call i1 @runtime.stringEqual(ptr %a.data, i32 %a.len, ptr %s.data, i32 %s.len, ptr undef)
58-
call void @useString(ptr %1, i32 %2)
52+
call void @useString(ptr %1, i32 %a.len)
5953
ret i1 %equal
6054
}
6155

@@ -71,10 +65,9 @@ define i1 @main.keepComparisonAfterMutation(ptr %a.data, i32 %a.len, i32 %a.cap,
7165
entry:
7266
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 %a.len, i32 %a.cap, ptr undef)
7367
%1 = extractvalue %runtime._string %0, 0
74-
%2 = extractvalue %runtime._string %0, 1
7568
%equal1 = call i1 @runtime.stringEqual(ptr %a.data, i32 %a.len, ptr %s.data, i32 %s.len, ptr undef)
7669
store i8 1, ptr %a.data, align 1
77-
%equal2 = call i1 @runtime.stringEqual(ptr %1, i32 %2, ptr %s.data, i32 %s.len, ptr undef)
70+
%equal2 = call i1 @runtime.stringEqual(ptr %1, i32 %a.len, ptr %s.data, i32 %s.len, ptr undef)
7871
%result = and i1 %equal1, %equal2
7972
ret i1 %result
8073
}
@@ -84,9 +77,8 @@ entry:
8477
%a.data = alloca [4 x i8], align 1
8578
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 4, i32 4, ptr undef)
8679
%1 = extractvalue %runtime._string %0, 0
87-
%2 = extractvalue %runtime._string %0, 1
8880
call void @llvm.lifetime.end.p0(ptr %a.data)
89-
%equal = call i1 @runtime.stringEqual(ptr %1, i32 %2, ptr %s.data, i32 %s.len, ptr undef)
81+
%equal = call i1 @runtime.stringEqual(ptr %1, i32 4, ptr %s.data, i32 %s.len, ptr undef)
9082
ret i1 %equal
9183
}
9284

transform/testdata/stringfrombytes-stringless.out.ll

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ define i1 @main.keepStringConversion(ptr %a.data, i32 %a.len, i32 %a.cap, ptr %b
3434
entry:
3535
%0 = call %runtime._string @runtime.stringFromBytes(ptr %a.data, i32 %a.len, i32 %a.cap, ptr undef)
3636
%1 = extractvalue %runtime._string %0, 0
37-
%2 = extractvalue %runtime._string %0, 1
38-
call void @useString(ptr %1, i32 %2)
39-
%3 = call i1 @runtime.stringLess(ptr %1, i32 %2, ptr %b.data, i32 %b.len, ptr undef)
40-
ret i1 %3
37+
call void @useString(ptr %1, i32 %a.len)
38+
%2 = call i1 @runtime.stringLess(ptr %1, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef)
39+
ret i1 %2
4140
}

0 commit comments

Comments
 (0)