compiler: count locals number properly

This commit is contained in:
Evgeniy Stratonikov 2021-02-05 17:26:48 +03:00
parent 57a0377c81
commit 339187a56d
3 changed files with 57 additions and 21 deletions

View file

@ -55,14 +55,14 @@ func (c *codegen) traverseGlobals() (int, int, int) {
switch n := node.(type) { switch n := node.(type) {
case *ast.FuncDecl: case *ast.FuncDecl:
if isInitFunc(n) { if isInitFunc(n) {
c, _ := countLocals(n) num, _ := c.countLocals(n)
if c > initLocals { if num > initLocals {
initLocals = c initLocals = num
} }
} else if isDeployFunc(n) { } else if isDeployFunc(n) {
c, _ := countLocals(n) num, _ := c.countLocals(n)
if c > deployLocals { if num > deployLocals {
deployLocals = c deployLocals = num
} }
} }
return !hasDefer return !hasDefer

View file

@ -330,7 +330,7 @@ func (c *codegen) convertInitFuncs(f *ast.File, pkg *types.Package, seenBefore b
case *ast.FuncDecl: case *ast.FuncDecl:
if isInitFunc(n) { if isInitFunc(n) {
if seenBefore { if seenBefore {
cnt, _ := countLocals(n) cnt, _ := c.countLocals(n)
c.clearSlots(cnt) c.clearSlots(cnt)
seenBefore = true seenBefore = true
} }
@ -362,7 +362,7 @@ func (c *codegen) convertDeployFuncs() {
case *ast.FuncDecl: case *ast.FuncDecl:
if isDeployFunc(n) { if isDeployFunc(n) {
if seenBefore { if seenBefore {
cnt, _ := countLocals(n) cnt, _ := c.countLocals(n)
c.clearSlots(cnt) c.clearSlots(cnt)
} }
c.convertFuncDecl(f, n, pkg) c.convertFuncDecl(f, n, pkg)
@ -408,7 +408,7 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
// All globals copied into the scope of the function need to be added // All globals copied into the scope of the function need to be added
// to the stack size of the function. // to the stack size of the function.
if !isInit && !isDeploy { if !isInit && !isDeploy {
sizeLoc := f.countLocals() sizeLoc := c.countLocalsWithDefer(f)
if sizeLoc > 255 { if sizeLoc > 255 {
c.prog.Err = errors.New("maximum of 255 local variables is allowed") c.prog.Err = errors.New("maximum of 255 local variables is allowed")
} }
@ -416,7 +416,6 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
if sizeArg > 255 { if sizeArg > 255 {
c.prog.Err = errors.New("maximum of 255 local variables is allowed") c.prog.Err = errors.New("maximum of 255 local variables is allowed")
} }
sizeLoc = 255 // FIXME count locals including inline variables
if sizeLoc != 0 || sizeArg != 0 { if sizeLoc != 0 || sizeArg != 0 {
emit.Instruction(c.prog.BinWriter, opcode.INITSLOT, []byte{byte(sizeLoc), byte(sizeArg)}) emit.Instruction(c.prog.BinWriter, opcode.INITSLOT, []byte{byte(sizeLoc), byte(sizeArg)})
} }

View file

@ -102,11 +102,47 @@ func (c *funcScope) analyzeVoidCalls(node ast.Node) bool {
return true return true
} }
func countLocals(decl *ast.FuncDecl) (int, bool) { func (c *codegen) countLocals(decl *ast.FuncDecl) (int, bool) {
return c.countLocalsInline(decl, nil, nil)
}
func (c *codegen) countLocalsInline(decl *ast.FuncDecl, pkg *types.Package, f *funcScope) (int, bool) {
oldMap := c.importMap
if pkg != nil {
c.fillImportMap(f.file, pkg)
}
size := 0 size := 0
hasDefer := false hasDefer := false
ast.Inspect(decl, func(n ast.Node) bool { ast.Inspect(decl, func(n ast.Node) bool {
switch n := n.(type) { switch n := n.(type) {
case *ast.CallExpr:
var name string
switch fun := n.Fun.(type) {
case *ast.Ident:
var pkgName string
if pkg != nil {
pkgName = pkg.Path()
}
name = c.getIdentName(pkgName, fun.Name)
case *ast.SelectorExpr:
name, _ = c.getFuncNameFromSelector(fun)
default:
return false
}
if inner, ok := c.funcs[name]; ok && canInline(name) {
for i := range n.Args {
switch n.Args[i].(type) {
case *ast.Ident:
case *ast.BasicLit:
default:
size++
}
}
innerSz, _ := c.countLocalsInline(inner.decl, inner.pkg, inner)
size += innerSz
}
return false
case *ast.FuncType: case *ast.FuncType:
num := n.Results.NumFields() num := n.Results.NumFields()
if num != 0 && len(n.Results.List[0].Names) != 0 { if num != 0 && len(n.Results.List[0].Names) != 0 {
@ -119,7 +155,11 @@ func countLocals(decl *ast.FuncDecl) (int, bool) {
case *ast.DeferStmt: case *ast.DeferStmt:
hasDefer = true hasDefer = true
return false return false
case *ast.ReturnStmt, *ast.IfStmt: case *ast.ReturnStmt:
if pkg == nil {
size++
}
case *ast.IfStmt:
size++ size++
// This handles the inline GenDecl like "var x = 2" // This handles the inline GenDecl like "var x = 2"
case *ast.ValueSpec: case *ast.ValueSpec:
@ -136,13 +176,16 @@ func countLocals(decl *ast.FuncDecl) (int, bool) {
} }
return true return true
}) })
if pkg != nil {
c.importMap = oldMap
}
return size, hasDefer return size, hasDefer
} }
func (c *funcScope) countLocals() int { func (c *codegen) countLocalsWithDefer(f *funcScope) int {
size, hasDefer := countLocals(c.decl) size, hasDefer := c.countLocals(f.decl)
if hasDefer { if hasDefer {
c.finallyProcessedIndex = size f.finallyProcessedIndex = size
size++ size++
} }
return size return size
@ -156,12 +199,6 @@ func (c *funcScope) countArgs() int {
return n return n
} }
func (c *funcScope) stackSize() int64 {
size := c.countLocals()
numArgs := c.countArgs()
return int64(size + numArgs)
}
// newVariable creates a new local variable or argument in the scope of the function. // newVariable creates a new local variable or argument in the scope of the function.
func (c *funcScope) newVariable(t varType, name string) int { func (c *funcScope) newVariable(t varType, name string) int {
return c.vars.newVariable(t, name) return c.vars.newVariable(t, name)