diff options
author | Ayke van Laethem <[email protected]> | 2019-09-21 18:05:23 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2019-09-22 08:25:50 +0200 |
commit | 65beddafe8e3d37cd60eb24a3d87e9dc3b3150b3 (patch) | |
tree | 5b7a3c3b769b400fe21375059de36051c4b6b615 /compiler | |
parent | cea0d9f864f2e1af32b97bd5af078a16f487859d (diff) | |
download | tinygo-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.go | 104 |
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 -} |