aboutsummaryrefslogtreecommitdiffhomepage
path: root/compiler
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-09-21 18:05:23 +0200
committerRon Evans <[email protected]>2019-09-22 08:25:50 +0200
commit65beddafe8e3d37cd60eb24a3d87e9dc3b3150b3 (patch)
tree5b7a3c3b769b400fe21375059de36051c4b6b615 /compiler
parentcea0d9f864f2e1af32b97bd5af078a16f487859d (diff)
downloadtinygo-65beddafe8e3d37cd60eb24a3d87e9dc3b3150b3.tar.gz
tinygo-65beddafe8e3d37cd60eb24a3d87e9dc3b3150b3.zip
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.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/optimizer.go104
1 files changed, 2 insertions, 102 deletions
diff --git a/compiler/optimizer.go b/compiler/optimizer.go
index 669e089c7..221534869 100644
--- a/compiler/optimizer.go
+++ b/compiler/optimizer.go
@@ -50,7 +50,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
// Run Go-specific optimization passes.
transform.OptimizeMaps(c.mod)
- c.OptimizeStringToBytes()
+ transform.OptimizeStringToBytes(c.mod)
transform.OptimizeAllocs(c.mod)
c.LowerInterfaces()
c.LowerFuncValues()
@@ -62,7 +62,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
// Run TinyGo-specific interprocedural optimizations.
transform.OptimizeAllocs(c.mod)
- c.OptimizeStringToBytes()
+ transform.OptimizeStringToBytes(c.mod)
// Lower runtime.isnil calls to regular nil comparisons.
isnil := c.mod.NamedFunction("runtime.isnil")
@@ -158,103 +158,3 @@ func (c *Compiler) replacePanicsWithTrap() {
}
}
}
-
-// Transform 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 (c *Compiler) OptimizeStringToBytes() {
- stringToBytes := c.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) {
- nilValue := llvm.Value{}
- if use.IsAExtractValueInst() == nilValue {
- 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 !c.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()
- }
- }
-}
-
-// Check whether the given value (which is of pointer type) is never stored to.
-func (c *Compiler) isReadOnly(value llvm.Value) bool {
- uses := getUses(value)
- for _, use := range uses {
- nilValue := llvm.Value{}
- if use.IsAGetElementPtrInst() != nilValue {
- if !c.isReadOnly(use) {
- return false
- }
- } else if use.IsACallInst() != nilValue {
- if !c.hasFlag(use, value, "readonly") {
- return false
- }
- } else {
- // Unknown instruction, might not be readonly.
- return false
- }
- }
- return true
-}
-
-// Check whether all uses of this param as parameter to the call have the given
-// flag. In most cases, there will only be one use but a function could take the
-// same parameter twice, in which case both must have the flag.
-// A flag can be any enum flag, like "readonly".
-func (c *Compiler) hasFlag(call, param llvm.Value, kind string) bool {
- fn := call.CalledValue()
- nilValue := llvm.Value{}
- if fn.IsAFunction() == nilValue {
- // This is not a function but something else, like a function pointer.
- return false
- }
- kindID := llvm.AttributeKindID(kind)
- for i := 0; i < fn.ParamsCount(); i++ {
- if call.Operand(i) != param {
- // This is not the parameter we're checking.
- continue
- }
- index := i + 1 // param attributes start at 1
- attr := fn.GetEnumAttributeAtIndex(index, kindID)
- nilAttribute := llvm.Attribute{}
- if attr == nilAttribute {
- // At least one parameter doesn't have the flag (there may be
- // multiple).
- return false
- }
- }
- return true
-}