aboutsummaryrefslogtreecommitdiffhomepage
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
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.
-rw-r--r--transform/optimizer.go1
-rw-r--r--transform/rtcalls.go (renamed from transform/stringtobytes.go)30
-rw-r--r--transform/rtcalls_test.go (renamed from transform/stringtobytes_test.go)8
-rw-r--r--transform/testdata/stringequal.ll19
-rw-r--r--transform/testdata/stringequal.out.ll19
5 files changed, 77 insertions, 0 deletions
diff --git a/transform/optimizer.go b/transform/optimizer.go
index e330d1afa..7ff7874f1 100644
--- a/transform/optimizer.go
+++ b/transform/optimizer.go
@@ -97,6 +97,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
// Run TinyGo-specific interprocedural optimizations.
OptimizeAllocs(mod)
OptimizeStringToBytes(mod)
+ OptimizeStringEqual(mod)
} else {
// Must be run at any optimization level.
diff --git a/transform/stringtobytes.go b/transform/rtcalls.go
index b3cfe1158..aac00d157 100644
--- a/transform/stringtobytes.go
+++ b/transform/rtcalls.go
@@ -1,5 +1,7 @@
package transform
+// This file implements several small optimizations of runtime calls.
+
import (
"tinygo.org/x/go-llvm"
)
@@ -55,3 +57,31 @@ func OptimizeStringToBytes(mod llvm.Module) {
}
}
}
+
+// 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
+ }
+ }
+}
diff --git a/transform/stringtobytes_test.go b/transform/rtcalls_test.go
index 91553840d..b414fc10a 100644
--- a/transform/stringtobytes_test.go
+++ b/transform/rtcalls_test.go
@@ -13,3 +13,11 @@ func TestOptimizeStringToBytes(t *testing.T) {
OptimizeStringToBytes(mod)
})
}
+
+func TestOptimizeStringEqual(t *testing.T) {
+ t.Parallel()
+ testTransform(t, "testdata/stringequal", func(mod llvm.Module) {
+ // Run optimization pass.
+ OptimizeStringEqual(mod)
+ })
+}
diff --git a/transform/testdata/stringequal.ll b/transform/testdata/stringequal.ll
new file mode 100644
index 000000000..94a7d2000
--- /dev/null
+++ b/transform/testdata/stringequal.ll
@@ -0,0 +1,19 @@
+target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "armv7m-none-eabi"
+
+@zeroString = constant [0 x i8] zeroinitializer
+
+declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*, i8*)
+
+define i1 @main.stringCompareEqualConstantZero(i8* %s1.data, i32 %s1.len, i8* %context, i8* %parentHandle) {
+entry:
+ %0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* getelementptr inbounds ([0 x i8], [0 x i8]* @zeroString, i32 0, i32 0), i32 0, i8* undef, i8* null)
+ ret i1 %0
+}
+
+define i1 @main.stringCompareUnequalConstantZero(i8* %s1.data, i32 %s1.len, i8* %context, i8* %parentHandle) {
+entry:
+ %0 = call i1 @runtime.stringEqual(i8* %s1.data, i32 %s1.len, i8* getelementptr inbounds ([0 x i8], [0 x i8]* @zeroString, i32 0, i32 0), i32 0, i8* undef, i8* null)
+ %1 = xor i1 %0, true
+ ret i1 %1
+}
diff --git a/transform/testdata/stringequal.out.ll b/transform/testdata/stringequal.out.ll
new file mode 100644
index 000000000..cde0092b2
--- /dev/null
+++ b/transform/testdata/stringequal.out.ll
@@ -0,0 +1,19 @@
+target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "armv7m-none-eabi"
+
+@zeroString = constant [0 x i8] zeroinitializer
+
+declare i1 @runtime.stringEqual(i8*, i32, i8*, i32, i8*, i8*)
+
+define i1 @main.stringCompareEqualConstantZero(i8* %s1.data, i32 %s1.len, i8* %context, i8* %parentHandle) {
+entry:
+ %0 = icmp eq i32 %s1.len, 0
+ ret i1 %0
+}
+
+define i1 @main.stringCompareUnequalConstantZero(i8* %s1.data, i32 %s1.len, i8* %context, i8* %parentHandle) {
+entry:
+ %0 = icmp eq i32 %s1.len, 0
+ %1 = xor i1 %0, true
+ ret i1 %1
+}