tinygo/transform/rtcalls.go
Ayke van Laethem f9865a08bc 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.
2021-03-18 17:22:00 +01:00

87 строки
2,4 КиБ
Go

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
}
}
}