diff --git a/gnovm/pkg/gnolang/alloc.go b/gnovm/pkg/gnolang/alloc.go index 1d46ec47da3..75f76f3d913 100644 --- a/gnovm/pkg/gnolang/alloc.go +++ b/gnovm/pkg/gnolang/alloc.go @@ -253,9 +253,12 @@ func (alloc *Allocator) Allocate(size int64) { return } + // if alloc on throwaway still exceeds memory, + // means GC does not work, panic. if alloc.throwAway { if alloc.bytes > alloc.maxBytes { debug2.Println2("---exceed memory size............") + panic("exceed memory size") } } diff --git a/gnovm/pkg/gnolang/op_assign.go b/gnovm/pkg/gnolang/op_assign.go index 9828f2b3ed4..512667ca5d2 100644 --- a/gnovm/pkg/gnolang/op_assign.go +++ b/gnovm/pkg/gnolang/op_assign.go @@ -54,6 +54,7 @@ func (m *Machine) doOpAssign() { func (m *Machine) doOpAddAssign() { s := m.PopStmt().(*AssignStmt) + debug2.Println2("doOpAddAssign, s: ", s) rv := m.PopValue() // only one. lv := m.PopAsPointer(s.Lhs[0]) if debug { diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 6d26fa7ce54..14a50bfe988 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -685,11 +685,13 @@ func isGeq(lv, rv *TypedValue) bool { // for doOpAdd and doOpAddAssign. func addAssign(alloc *Allocator, lv, rv *TypedValue) { + debug2.Println2("addAssign, lv: ", lv) // set the result in lv. // NOTE this block is replicated in op_assign.go switch baseOf(lv.T) { case StringType, UntypedStringType: lv.V = alloc.NewString(lv.GetString() + rv.GetString()) + lv.SetNeedsValueAllocation(true) case IntType: lv.SetInt(lv.GetInt() + rv.GetInt()) case Int8Type: diff --git a/gnovm/pkg/gnolang/op_exec.go b/gnovm/pkg/gnolang/op_exec.go index be046cf8eac..eadaa5d2b49 100644 --- a/gnovm/pkg/gnolang/op_exec.go +++ b/gnovm/pkg/gnolang/op_exec.go @@ -433,7 +433,7 @@ EXEC_SWITCH: if debug { debug.Printf("EXEC: %v\n", s) } - debug2.Printf2("EXEC: %v\n", s) + debug2.Printf2("EXEC: %v, type of s: %v \n", s, reflect.TypeOf(s)) switch cs := s.(type) { case *AssignStmt: switch cs.Op { diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index 3996112c63a..db98d5726ad 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -436,7 +436,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { findGotoLoopDefines(ctx, bn) findLoopUses1(ctx, bn) findLoopUses2(ctx, bn) - findBlockAlloc(store, ctx, bn) + findBlockAllocation(store, ctx, bn) } return n } @@ -1019,8 +1019,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { cx := evalConst(store, last, n) // built-in functions must be called. if !cx.IsUndefined() && - cx.T.Kind() == FuncKind && - ftype != TRANS_CALL_FUNC { + cx.T.Kind() == FuncKind && + ftype != TRANS_CALL_FUNC { panic(fmt.Sprintf( "use of builtin %s not in function call", n.Name)) @@ -1837,8 +1837,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // Case 1: If receiver is pointer type but n.X is // not: if rcvr != nil && - rcvr.Kind() == PointerKind && - nxt2.Kind() != PointerKind { + rcvr.Kind() == PointerKind && + nxt2.Kind() != PointerKind { // Go spec: "If x is addressable and &x's // method set contains m, x.m() is shorthand // for (&x).m()" @@ -1870,8 +1870,8 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { )) } } else if len(tr) > 0 && - tr[len(tr)-1].IsDerefType() && - nxt2.Kind() != PointerKind { + tr[len(tr)-1].IsDerefType() && + nxt2.Kind() != PointerKind { // Case 2: If tr[0] is deref type, but xt // is not pointer type, replace n.X with // &RefExpr{X: n.X}. @@ -2385,13 +2385,13 @@ func preprocess1(store Store, ctx BlockNode, n Node) Node { // defineOrDecl merges the code logic from op define (:=) and declare (var/const). func defineOrDecl( - store Store, - bn BlockNode, - n Node, - isConst bool, - nameExprs []NameExpr, - typeExpr Expr, - valueExprs []Expr, + store Store, + bn BlockNode, + n Node, + isConst bool, + nameExprs []NameExpr, + typeExpr Expr, + valueExprs []Expr, ) { numNames := len(nameExprs) numVals := len(valueExprs) @@ -2425,15 +2425,15 @@ func defineOrDecl( // parseAssignFromExprList parses assignment to multiple variables from a list of expressions. // This function will alter the value of sts, tvs. func parseAssignFromExprList( - store Store, - bn BlockNode, - n Node, - sts []Type, - tvs []TypedValue, - isConst bool, - nameExprs []NameExpr, - typeExpr Expr, - valueExprs []Expr, + store Store, + bn BlockNode, + n Node, + sts []Type, + tvs []TypedValue, + isConst bool, + nameExprs []NameExpr, + typeExpr Expr, + valueExprs []Expr, ) { numNames := len(nameExprs) @@ -2484,7 +2484,7 @@ func parseAssignFromExprList( if len(valueExprs) > 0 { vx := valueExprs[i] if cx, ok := vx.(*ConstExpr); ok && - !cx.TypedValue.IsUndefined() { + !cx.TypedValue.IsUndefined() { if isConst { // const _ = : static block should contain value tvs[i] = cx.TypedValue @@ -2512,14 +2512,14 @@ func parseAssignFromExprList( // - a, b := n.(T) // - a, b := n[i], where n is a map func parseMultipleAssignFromOneExpr( - store Store, - bn BlockNode, - n Node, - sts []Type, - tvs []TypedValue, - nameExprs []NameExpr, - typeExpr Expr, - valueExpr Expr, + store Store, + bn BlockNode, + n Node, + sts []Type, + tvs []TypedValue, + nameExprs []NameExpr, + typeExpr Expr, + valueExpr Expr, ) { var tuple *tupleType numNames := len(nameExprs) @@ -2853,7 +2853,7 @@ func findLoopUses1(ctx BlockNode, bn BlockNode) { }) } -func findBlockAlloc(store Store, ctx BlockNode, bn BlockNode) { +func findBlockAllocation(store Store, ctx BlockNode, bn BlockNode) { // create stack of BlockNodes. var stack []BlockNode = make([]BlockNode, 0, 32) var last BlockNode = ctx @@ -2867,6 +2867,9 @@ func findBlockAlloc(store Store, ctx BlockNode, bn BlockNode) { debug.Printf("findBlockAlloc %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) } debug2.Printf2("findBlockAlloc %s (%v) stage:%v\n", n.String(), reflect.TypeOf(n), stage) + debug2.Println2("last: ", last) + debug2.Println2("last...BlockNames: ", last.GetBlockNames()) + debug2.Println2("last...Externs: ", last.GetExternNames()) switch stage { // ---------------------------------------- @@ -2889,7 +2892,7 @@ func findBlockAlloc(store Store, ctx BlockNode, bn BlockNode) { // set alloc annotation for i, rx := range n.Rhs { debug2.Printf2("n.Rhs[%d] is %v, type of rx: %v \n", i, rx, reflect.TypeOf(rx)) - switch rx.(type) { + switch rxx := rx.(type) { case *NameExpr: rt := evalStaticTypeOf(store, last, nx) debug2.Println2("===rt: ", rt, reflect.TypeOf(rt)) @@ -2898,6 +2901,21 @@ func findBlockAlloc(store Store, ctx BlockNode, bn BlockNode) { // value copy, alloc nx.Alloc = true } + case *ConstExpr: + if bx, ok := rxx.Source.(*BinaryExpr); ok { + debug2.Println2("BinaryExpr: ", bx) + if bx.Op == ADD { + lt := evalStaticTypeOf(store, last, bx.Left) + rt := evalStaticTypeOf(store, last, bx.Right) + debug2.Printf2("lt: %v, rt: %v: \n", lt, rt) + _, ok1 := lt.(PrimitiveType) + _, ok2 := rt.(PrimitiveType) + // s := "hello" + "world" + if ok1 && lt.Kind() == StringKind && ok2 && rt.Kind() == StringKind { + nx.Alloc = true + } + } + } case *CompositeLitExpr, *FuncLitExpr: // TODO: more ... nx.Alloc = true } @@ -3449,7 +3467,7 @@ func findContinuableNode(last BlockNode, store Store) { } func findBranchLabel(last BlockNode, label Name) ( - bn BlockNode, depth uint8, bodyIdx int, + bn BlockNode, depth uint8, bodyIdx int, ) { for { switch cbn := last.(type) { @@ -3489,7 +3507,7 @@ func findBranchLabel(last BlockNode, label Name) ( } func findGotoLabel(last BlockNode, label Name) ( - bn BlockNode, depth uint8, bodyIdx int, + bn BlockNode, depth uint8, bodyIdx int, ) { for { switch cbn := last.(type) { @@ -3733,7 +3751,7 @@ func isNamedConversion(xt, t Type) bool { // covert right to the type of left if one side is unnamed type and the other side is not if t.IsNamed() && !xt.IsNamed() || - !t.IsNamed() && xt.IsNamed() { + !t.IsNamed() && xt.IsNamed() { return true } } @@ -4851,6 +4869,7 @@ func isLocallyDefined(bn BlockNode, n Name) bool { func isLocallyDefined2(bn BlockNode, n Name) bool { debug2.Println2("isLocallyDefined2, n: ", n) _, isLocal := bn.GetLocalIndex(n) + debug2.Println2("isLocal: ", isLocal) return isLocal } diff --git a/gnovm/tests/files/alloc1.gno b/gnovm/tests/files/alloc1.gno index 2b0ec1fa9ca..a45c3e4ee60 100644 --- a/gnovm/tests/files/alloc1.gno +++ b/gnovm/tests/files/alloc1.gno @@ -60,13 +60,13 @@ func main() { } // Output: -// MemStats 1: Allocator{maxBytes:10000000000, bytes:1944} +// MemStats 1: Allocator{maxBytes:9223372036854775807, bytes:1944} // &(struct{("foo" string)} main.Foo) -// MemStats 2: Allocator{maxBytes:10000000000, bytes:6019} -// MemStats before GC1: Allocator{maxBytes:10000000000, bytes:7518} -// MemStats after GC1: Allocator{maxBytes:10000000000, bytes:2424} +// MemStats 2: Allocator{maxBytes:9223372036854775807, bytes:6027} +// MemStats before GC1: Allocator{maxBytes:9223372036854775807, bytes:7534} +// MemStats after GC1: Allocator{maxBytes:9223372036854775807, bytes:2720} // func lit -// MemStats before GC2: Allocator{maxBytes:10000000000, bytes:5443} -// MemStats after GC2: Allocator{maxBytes:10000000000, bytes:2424} -// MemStats before GC3: Allocator{maxBytes:10000000000, bytes:3923} -// MemStats after GC3: Allocator{maxBytes:10000000000, bytes:2424} +// MemStats before GC2: Allocator{maxBytes:9223372036854775807, bytes:5747} +// MemStats after GC2: Allocator{maxBytes:9223372036854775807, bytes:2888} +// MemStats before GC3: Allocator{maxBytes:9223372036854775807, bytes:4395} +// MemStats after GC3: Allocator{maxBytes:9223372036854775807, bytes:2888} diff --git a/gnovm/tests/files/alloc_amino.gno b/gnovm/tests/files/alloc_amino.gno index fd0fa5bd9c0..21f43b49abb 100644 --- a/gnovm/tests/files/alloc_amino.gno +++ b/gnovm/tests/files/alloc_amino.gno @@ -9,8 +9,9 @@ import "runtime" type Foo struct{ name string } var f = Foo{name: "foo"} +var m = map[string]Foo{"a": f} func main() { - println(f) // here should alloc amino for f + println(f, m) // here should alloc amino for f runtime.GC() } diff --git a/gnovm/tests/files/alloc_string.gno b/gnovm/tests/files/alloc_string.gno new file mode 100644 index 00000000000..b555bdfb729 --- /dev/null +++ b/gnovm/tests/files/alloc_string.gno @@ -0,0 +1,14 @@ +// MAXALLOC: 25000 +// max total allocation of 100 mb. + +package main + +import "runtime" + +var s = "hello" + +func main() { + s += "world" // new string + s += "!!!" // new string + runtime.GC() +} diff --git a/gnovm/tests/files/alloc_string2.gno b/gnovm/tests/files/alloc_string2.gno new file mode 100644 index 00000000000..1e5b1f07923 --- /dev/null +++ b/gnovm/tests/files/alloc_string2.gno @@ -0,0 +1,12 @@ +// MAXALLOC: 25000 +// max total allocation of 100 mb. + +package main + +import "runtime" + +func main() { + var s = "hello" + s += "world" + runtime.GC() +} diff --git a/gnovm/tests/files/alloc_string3.gno b/gnovm/tests/files/alloc_string3.gno new file mode 100644 index 00000000000..a594c4dc73e --- /dev/null +++ b/gnovm/tests/files/alloc_string3.gno @@ -0,0 +1,11 @@ +// MAXALLOC: 25000 +// max total allocation of 100 mb. + +package main + +import "runtime" + +func main() { + s := "hello" + "world" // const + runtime.GC() +} diff --git a/gnovm/tests/files/alloc_string4.gno b/gnovm/tests/files/alloc_string4.gno new file mode 100644 index 00000000000..4ce8f55c9c1 --- /dev/null +++ b/gnovm/tests/files/alloc_string4.gno @@ -0,0 +1,11 @@ +// MAXALLOC: 25000 +// max total allocation of 100 mb. + +package main + +import "runtime" + +func main() { + s := "hello" + runtime.GC() +}