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 | |
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.
-rw-r--r-- | transform/optimizer.go | 1 | ||||
-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.ll | 19 | ||||
-rw-r--r-- | transform/testdata/stringequal.out.ll | 19 |
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 +} |