From 0b44a430430813c524bdebc279409d0e88e28818 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Sun, 6 Sep 2020 15:20:15 +0300 Subject: [PATCH 1/3] compiler: do not allocate static slot for constants Their value is known at compile time. --- pkg/compiler/analysis.go | 9 +++++++-- pkg/compiler/global_test.go | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 730a9bd89..190ea7511 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -109,8 +109,13 @@ func countGlobals(f ast.Node) (i int) { return false // After skipping all funcDecls we are sure that each value spec // is a global declared variable or constant. - case *ast.ValueSpec: - i += len(n.Names) + case *ast.GenDecl: + if n.Tok == token.VAR { + for _, s := range n.Specs { + i += len(s.(*ast.ValueSpec).Names) + } + } + return false } return true }) diff --git a/pkg/compiler/global_test.go b/pkg/compiler/global_test.go index e4f29f89b..b8ab1b045 100644 --- a/pkg/compiler/global_test.go +++ b/pkg/compiler/global_test.go @@ -1,6 +1,7 @@ package compiler_test import ( + "bytes" "fmt" "math/big" "strings" @@ -238,3 +239,19 @@ func TestMultipleFuncSameName(t *testing.T) { eval(t, src, big.NewInt(42)) }) } + +func TestConstDontUseSlots(t *testing.T) { + const count = 256 + buf := bytes.NewBufferString("package foo\n") + for i := 0; i < count; i++ { + buf.WriteString(fmt.Sprintf("const n%d = 1\n", i)) + } + buf.WriteString("func Main() int { sum := 0\n") + for i := 0; i < count; i++ { + buf.WriteString(fmt.Sprintf("sum += n%d\n", i)) + } + buf.WriteString("return sum }") + + src := buf.String() + eval(t, src, big.NewInt(count)) +} From 18369c489e9dc797e2f932160d72c2bf016c4591 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Sun, 6 Sep 2020 15:26:03 +0300 Subject: [PATCH 2/3] compiler: do not allocate slotes for unused "_" vars --- pkg/compiler/analysis.go | 6 +++++- pkg/compiler/codegen.go | 14 ++++++++------ pkg/compiler/global_test.go | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 190ea7511..11be72e32 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -112,7 +112,11 @@ func countGlobals(f ast.Node) (i int) { case *ast.GenDecl: if n.Tok == token.VAR { for _, s := range n.Specs { - i += len(s.(*ast.ValueSpec).Names) + for _, id := range s.(*ast.ValueSpec).Names { + if id.Name != "_" { + i++ + } + } } } return false diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 710d58de6..bf01115ad 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -434,13 +434,15 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { switch t := spec.(type) { case *ast.ValueSpec: for _, id := range t.Names { - if c.scope == nil { - // it is a global declaration - c.newGlobal("", id.Name) - } else { - c.scope.newLocal(id.Name) + if id.Name != "_" { + if c.scope == nil { + // it is a global declaration + c.newGlobal("", id.Name) + } else { + c.scope.newLocal(id.Name) + } + c.registerDebugVariable(id.Name, t.Type) } - c.registerDebugVariable(id.Name, t.Type) } for i := range t.Names { if len(t.Values) != 0 { diff --git a/pkg/compiler/global_test.go b/pkg/compiler/global_test.go index b8ab1b045..434c22290 100644 --- a/pkg/compiler/global_test.go +++ b/pkg/compiler/global_test.go @@ -255,3 +255,19 @@ func TestConstDontUseSlots(t *testing.T) { src := buf.String() eval(t, src, big.NewInt(count)) } + +func TestUnderscoreVarsDontUseSlots(t *testing.T) { + const count = 128 + buf := bytes.NewBufferString("package foo\n") + for i := 0; i < count; i++ { + buf.WriteString(fmt.Sprintf("var _, n%d = 1, 1\n", i)) + } + buf.WriteString("func Main() int { sum := 0\n") + for i := 0; i < count; i++ { + buf.WriteString(fmt.Sprintf("sum += n%d\n", i)) + } + buf.WriteString("return sum }") + + src := buf.String() + eval(t, src, big.NewInt(count)) +} From 7483e3b0547c4cd23bfff5407ffe27ea216880ff Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Sun, 6 Sep 2020 15:49:41 +0300 Subject: [PATCH 3/3] compiler: support `delete()` builtin --- pkg/compiler/analysis.go | 2 +- pkg/compiler/codegen.go | 2 ++ pkg/compiler/map_test.go | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 11be72e32..cd9e9148d 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -14,7 +14,7 @@ import ( var ( // Go language builtin functions. - goBuiltins = []string{"len", "append", "panic", "make", "copy", "recover"} + goBuiltins = []string{"len", "append", "panic", "make", "copy", "recover", "delete"} // Custom builtin utility functions. customBuiltins = []string{ "FromAddress", "Equals", diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index bf01115ad..be9e8ec2c 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -1527,6 +1527,8 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) { } emit.Opcode(c.prog.BinWriter, opcode.PUSHNULL) c.emitStoreByIndex(varGlobal, c.exceptionIndex) + case "delete": + emit.Opcode(c.prog.BinWriter, opcode.REMOVE) case "ToInteger", "ToByteArray", "ToBool": typ := stackitem.IntegerT switch name { diff --git a/pkg/compiler/map_test.go b/pkg/compiler/map_test.go index be7453507..30fcf85a2 100644 --- a/pkg/compiler/map_test.go +++ b/pkg/compiler/map_test.go @@ -55,6 +55,26 @@ var mapTestCases = []testCase{ `, []byte("Valera"), }, + { + "delete key", + `package foo + func Main() int { + m := map[int]int{1: 2, 3: 4} + delete(m, 1) + return len(m) + }`, + big.NewInt(1), + }, + { + "delete missing key", + `package foo + func Main() int { + m := map[int]int{3: 4} + delete(m, 1) + return len(m) + }`, + big.NewInt(1), + }, } func TestMaps(t *testing.T) {