aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-04-05 19:24:25 +0200
committerRon Evans <[email protected]>2021-04-08 11:40:59 +0200
commit0b7957d61249ce4b745f78405e36f07245eb8da3 (patch)
tree73ba364ae47485ff2c03bc361820d49857d0f4e8
parent61243f6c57b4158c28666ebadc6344e143ca02f1 (diff)
downloadtinygo-0b7957d61249ce4b745f78405e36f07245eb8da3.tar.gz
tinygo-0b7957d61249ce4b745f78405e36f07245eb8da3.zip
compiler: optimize string literals and globals
This commit optimizes string literals and globals by setting the appropriate alignment and using a nil pointer in zero-length strings. - Setting the alignment for string values has a surprisingly large effect, up to around 2% in binary size. I suspect that LLVM will pick some default alignment for larger byte arrays if no alignment has been specified and forcing an alignment of 1 will pack all strings closer together. - Using nil for zero-length strings also has a positive effect, but I'm not sure why. Perhaps it makes some optimizations more trivial. - Always setting the alignment on globals improves code size slightly, probably for the same reasons setting the alignment of string literals improves code size. The effect is much smaller, however. This commit might have an effect on performance, but if it does this should be tested separately and such a large win in binary size should definitely not be ignored for small embedded systems.
-rw-r--r--compiler/compiler.go22
-rw-r--r--compiler/symbol.go12
-rw-r--r--compiler/testdata/string.go8
-rw-r--r--compiler/testdata/string.ll14
4 files changed, 43 insertions, 13 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go
index be15f7890..d652f5404 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -2258,14 +2258,20 @@ func (b *builder) createConst(prefix string, expr *ssa.Const) llvm.Value {
} else if typ.Info()&types.IsString != 0 {
str := constant.StringVal(expr.Value)
strLen := llvm.ConstInt(b.uintptrType, uint64(len(str)), false)
- objname := prefix + "$string"
- global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
- global.SetInitializer(b.ctx.ConstString(str, false))
- global.SetLinkage(llvm.InternalLinkage)
- global.SetGlobalConstant(true)
- global.SetUnnamedAddr(true)
- zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
- strPtr := b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
+ var strPtr llvm.Value
+ if str != "" {
+ objname := prefix + "$string"
+ global := llvm.AddGlobal(b.mod, llvm.ArrayType(b.ctx.Int8Type(), len(str)), objname)
+ global.SetInitializer(b.ctx.ConstString(str, false))
+ global.SetLinkage(llvm.InternalLinkage)
+ global.SetGlobalConstant(true)
+ global.SetUnnamedAddr(true)
+ global.SetAlignment(1)
+ zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
+ strPtr = b.CreateInBoundsGEP(global, []llvm.Value{zero, zero}, "")
+ } else {
+ strPtr = llvm.ConstNull(b.i8ptrType)
+ }
strObj := llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen})
return strObj
} else if typ.Kind() == types.UnsafePointer {
diff --git a/compiler/symbol.go b/compiler/symbol.go
index 5b44c20f9..6455fb8f3 100644
--- a/compiler/symbol.go
+++ b/compiler/symbol.go
@@ -355,16 +355,18 @@ func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value {
// Set alignment from the //go:align comment.
var alignInBits uint32
- if info.align < 0 || info.align&(info.align-1) != 0 {
+ alignment := c.targetData.ABITypeAlignment(llvmType)
+ if info.align > alignment {
+ alignment = info.align
+ }
+ if alignment <= 0 || alignment&(alignment-1) != 0 {
// Check for power-of-two (or 0).
// See: https://stackoverflow.com/a/108360
c.addError(g.Pos(), "global variable alignment must be a positive power of two")
} else {
// Set the alignment only when it is a power of two.
- alignInBits = uint32(info.align) ^ uint32(info.align-1)
- if info.align > c.targetData.ABITypeAlignment(llvmType) {
- llvmGlobal.SetAlignment(info.align)
- }
+ alignInBits = uint32(alignment) ^ uint32(alignment-1)
+ llvmGlobal.SetAlignment(alignment)
}
if c.Debug && !info.extern {
diff --git a/compiler/testdata/string.go b/compiler/testdata/string.go
index 3c574d8fd..2c37df458 100644
--- a/compiler/testdata/string.go
+++ b/compiler/testdata/string.go
@@ -1,5 +1,13 @@
package main
+func someString() string {
+ return "foo"
+}
+
+func zeroLengthString() string {
+ return ""
+}
+
func stringLen(s string) int {
return len(s)
}
diff --git a/compiler/testdata/string.ll b/compiler/testdata/string.ll
index 7344a2e50..1c66e9a67 100644
--- a/compiler/testdata/string.ll
+++ b/compiler/testdata/string.ll
@@ -3,6 +3,10 @@ source_filename = "string.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
+%runtime._string = type { i8*, i32 }
+
+@"main.someString$string" = internal unnamed_addr constant [3 x i8] c"foo", align 1
+
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr {
@@ -10,6 +14,16 @@ entry:
ret void
}
+define hidden %runtime._string @main.someString(i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ ret %runtime._string { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"main.someString$string", i32 0, i32 0), i32 3 }
+}
+
+define hidden %runtime._string @main.zeroLengthString(i8* %context, i8* %parentHandle) unnamed_addr {
+entry:
+ ret %runtime._string zeroinitializer
+}
+
define hidden i32 @main.stringLen(i8* %s.data, i32 %s.len, i8* %context, i8* %parentHandle) unnamed_addr {
entry:
ret i32 %s.len