aboutsummaryrefslogtreecommitdiffhomepage
path: root/transform/rtcalls.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-03-08 13:39:46 +0100
committerRon Evans <[email protected]>2021-03-18 17:22:00 +0100
commitf9865a08bcc0ec912e189b2a550e6c5124574c7e (patch)
tree43acc2feffb53a1ad60e1d1364645acecabf4333 /transform/rtcalls.go
parent13db2c13e5f980a5b2103387abd075ecb7a1befb (diff)
downloadtinygo-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.go87
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
+ }
+ }
+}