diff options
author | Ayke van Laethem <[email protected]> | 2021-03-08 13:39:46 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-03-18 17:22:00 +0100 |
commit | f9865a08bcc0ec912e189b2a550e6c5124574c7e (patch) | |
tree | 43acc2feffb53a1ad60e1d1364645acecabf4333 /transform/rtcalls.go | |
parent | 13db2c13e5f980a5b2103387abd075ecb7a1befb (diff) | |
download | tinygo-f9865a08bcc0ec912e189b2a550e6c5124574c7e.tar.gz tinygo-f9865a08bcc0ec912e189b2a550e6c5124574c7e.zip |
transform: optimize string comparisons against ""
This optimizes a common pattern like:
if s != "" {
...
}
to:
if len(s) != 0 {
...
}
This avoids a runtime call and thus produces slightly better code.
Diffstat (limited to 'transform/rtcalls.go')
-rw-r--r-- | transform/rtcalls.go | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/transform/rtcalls.go b/transform/rtcalls.go new file mode 100644 index 000000000..aac00d157 --- /dev/null +++ b/transform/rtcalls.go @@ -0,0 +1,87 @@ +package transform + +// This file implements several small optimizations of runtime calls. + +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() + } + } +} + +// OptimizeStringEqual transforms runtime.stringEqual(...) calls into simple +// integer comparisons if at least one of the sides of the comparison is zero. +// Ths converts str == "" into len(str) == 0 and "" == "" into false. +func OptimizeStringEqual(mod llvm.Module) { + stringEqual := mod.NamedFunction("runtime.stringEqual") + if stringEqual.IsNil() { + // nothing to optimize + return + } + + builder := mod.Context().NewBuilder() + defer builder.Dispose() + + for _, call := range getUses(stringEqual) { + str1len := call.Operand(1) + str2len := call.Operand(3) + + zero := llvm.ConstInt(str1len.Type(), 0, false) + if str1len == zero || str2len == zero { + builder.SetInsertPointBefore(call) + icmp := builder.CreateICmp(llvm.IntEQ, str1len, str2len, "") + call.ReplaceAllUsesWith(icmp) + call.EraseFromParentAsInstruction() + continue + } + } +} |