forked from tinygo-org/tinygo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
compiler: move OptimizeStringToBytes to transform package
Unfortunately, while doing this I found that it doesn't actually apply in any real-world programs (tested with `make smoketest`), apparently because nil pointer checking messes with the functionattrs pass. I hope to fix that after moving to LLVM 9, which has an optimization that makes nil pointer checking easier to implement.
- Loading branch information
1 parent
cea0d9f
commit 65bedda
Showing
6 changed files
with
151 additions
and
102 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package transform | ||
|
||
import ( | ||
"tinygo.org/x/go-llvm" | ||
) | ||
|
||
// OptimizeStringToBytes transforms runtime.stringToBytes(...) calls into const | ||
// []byte slices whenever possible. This optimizes the following pattern: | ||
// | ||
// w.Write([]byte("foo")) | ||
// | ||
// where Write does not store to the slice. | ||
func OptimizeStringToBytes(mod llvm.Module) { | ||
stringToBytes := mod.NamedFunction("runtime.stringToBytes") | ||
if stringToBytes.IsNil() { | ||
// nothing to optimize | ||
return | ||
} | ||
|
||
for _, call := range getUses(stringToBytes) { | ||
strptr := call.Operand(0) | ||
strlen := call.Operand(1) | ||
|
||
// strptr is always constant because strings are always constant. | ||
|
||
convertedAllUses := true | ||
for _, use := range getUses(call) { | ||
if use.IsAExtractValueInst().IsNil() { | ||
// Expected an extractvalue, but this is something else. | ||
convertedAllUses = false | ||
continue | ||
} | ||
switch use.Type().TypeKind() { | ||
case llvm.IntegerTypeKind: | ||
// A length (len or cap). Propagate the length value. | ||
use.ReplaceAllUsesWith(strlen) | ||
use.EraseFromParentAsInstruction() | ||
case llvm.PointerTypeKind: | ||
// The string pointer itself. | ||
if !isReadOnly(use) { | ||
convertedAllUses = false | ||
continue | ||
} | ||
use.ReplaceAllUsesWith(strptr) | ||
use.EraseFromParentAsInstruction() | ||
default: | ||
// should not happen | ||
panic("unknown return type of runtime.stringToBytes: " + use.Type().String()) | ||
} | ||
} | ||
if convertedAllUses { | ||
// Call to runtime.stringToBytes can be eliminated: both the input | ||
// and the output is constant. | ||
call.EraseFromParentAsInstruction() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package transform | ||
|
||
import ( | ||
"testing" | ||
|
||
"tinygo.org/x/go-llvm" | ||
) | ||
|
||
func TestOptimizeStringToBytes(t *testing.T) { | ||
t.Parallel() | ||
testTransform(t, "testdata/stringtobytes", func(mod llvm.Module) { | ||
// Run optimization pass. | ||
OptimizeStringToBytes(mod) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
target triple = "x86_64--linux" | ||
|
||
@str = constant [6 x i8] c"foobar" | ||
|
||
declare { i8*, i64, i64 } @runtime.stringToBytes(i8*, i64) | ||
|
||
declare void @printSlice(i8* nocapture readonly, i64, i64) | ||
|
||
declare void @writeToSlice(i8* nocapture, i64, i64) | ||
|
||
; Test that runtime.stringToBytes can be fully optimized away. | ||
define void @testReadOnly() { | ||
entry: | ||
%0 = call fastcc { i8*, i64, i64 } @runtime.stringToBytes(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @str, i32 0, i32 0), i64 6) | ||
%1 = extractvalue { i8*, i64, i64 } %0, 0 | ||
%2 = extractvalue { i8*, i64, i64 } %0, 1 | ||
%3 = extractvalue { i8*, i64, i64 } %0, 2 | ||
call fastcc void @printSlice(i8* %1, i64 %2, i64 %3) | ||
ret void | ||
} | ||
|
||
; Test that even though the slice is written to, some values can be propagated. | ||
define void @testReadWrite() { | ||
entry: | ||
%0 = call fastcc { i8*, i64, i64 } @runtime.stringToBytes(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @str, i32 0, i32 0), i64 6) | ||
%1 = extractvalue { i8*, i64, i64 } %0, 0 | ||
%2 = extractvalue { i8*, i64, i64 } %0, 1 | ||
%3 = extractvalue { i8*, i64, i64 } %0, 2 | ||
call fastcc void @writeToSlice(i8* %1, i64 %2, i64 %3) | ||
ret void | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" | ||
target triple = "x86_64--linux" | ||
|
||
@str = constant [6 x i8] c"foobar" | ||
|
||
declare { i8*, i64, i64 } @runtime.stringToBytes(i8*, i64) | ||
|
||
declare void @printSlice(i8* nocapture readonly, i64, i64) | ||
|
||
declare void @writeToSlice(i8* nocapture, i64, i64) | ||
|
||
define void @testReadOnly() { | ||
entry: | ||
call fastcc void @printSlice(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @str, i32 0, i32 0), i64 6, i64 6) | ||
ret void | ||
} | ||
|
||
define void @testReadWrite() { | ||
entry: | ||
%0 = call fastcc { i8*, i64, i64 } @runtime.stringToBytes(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @str, i32 0, i32 0), i64 6) | ||
%1 = extractvalue { i8*, i64, i64 } %0, 0 | ||
call fastcc void @writeToSlice(i8* %1, i64 6, i64 6) | ||
ret void | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters