diff options
-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 +} |