-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(gnovm): handle loop variables (#2429)
# Problem Definition The problem originates from the issue described in [#1135](#1135). While the full scope of the issue is broader, it fundamentally relates to the concept of loop variable escapes block where it's defined. e.g. 1: ```go package main import "fmt" var s1 []*int func forLoopRef() { defer func() { for i, e := range s1 { fmt.Printf("s1[%d] is: %d\n", i, *e) } }() for i := 0; i < 3; i++ { z := i + 1 s1 = append(s1, &z) } } func main() { forLoopRef() } ``` e.g. 2: ```go package main type f func() var fs []f func forLoopClosure() { defer func() { for _, f := range fs { f() } }() for i := 0; i < 3; i++ { z := i fs = append(fs, func() { println(z) }) } } func main() { forLoopClosure() } ``` e.g. 3: ```go package main func main() { c := 0 closures := []func(){} loop: i := c closures = append(closures, func() { println(i) }) c += 1 if c < 10 { goto loop } for _, cl := range closures { cl() } } ``` # Solution ideas - **identify escaped vars in preprocess**: Detect situations where a loop variable is defined within a loop block(including `for/range` loops or loops constructed using `goto` statements), and escapes the block where it's defined. - **runtime allocation**: Allocate a new heap item for the loop variable in each iteration to ensure each iteration operates with its unique variable instance. - **NOTE1**: this is consistent with Go's Strategy: "Each iteration has its own separate declared variable (or variables) [Go 1.22]. The variable used by the first iteration is declared by the init statement. The variable used by each subsequent iteration is declared implicitly before executing the post statement and initialized to the value of the previous iteration's variable at that moment." - **NOTE2**: the `loopvar` feature of Go 1.22 is not supported in this version, and will be supported in next version. not supporting capture `i` defined in for/range clause; ```go for i := 0; i < 3; i++ { s1 = append(s1, &i) } ``` # Implementation Details **Preprocess Stage(Multi-Phase Preprocessor)**: - **Phase 1: `initStaticBlocks`**: Establish a cascading scope structure where `predefine` is conducted. In this phase Name expressions are initially marked as `NameExprTypeDefine`, which may later be upgraded to `NameExprTypeHeapDefine` if it is determined that they escape the loop block. This phase also supports other processes as a prerequisite[#2077](#2077). - **Phase 2: `preprocess1`**: This represents the original preprocessing phase(not going into details). - **Phase 3: `findGotoLoopDefines`**: By traversing the AST, any name expression defined in a loop block (for/range, goto) with the attribute `NameExprTypeDefine` is promoted to `NameExprTypeHeapDefine`. This is used in later phase. - **Phase 4: `findLoopUses1`**: Identify the usage of `NameExprTypeHeapDefine` name expressions. If a name expression is used in a function literal or is referrnced(e.g. &a), and it was previously defined as `NameExprTypeHeapDefine`, the `used` name expression is then given the attribute `NameExprTypeHeapUse`. This step finalizes whether a name expression will be allocated on the heap and used from heap. `Closures` represent a particular scenario in this context. Each closure, defined by a funcLitExpr that captures variables, is associated with a HeapCaptures list. This list consists of NameExprs, which are utilized at runtime to obtain the actual variable values for each iteration. Correspondingly, within the funcLitExpr block, a list of placeholder values are defined. These placeholders are populated during the doOpFuncLit phase and subsequently utilized in the `doOpCall` to ensure that each iteration uses the correct data. - **Phase 5: `findLoopUses2`**: Convert non-loop uses of loop-defined names to `NameExprTypeHeapUse`. Also, demote `NameExprTypeHeapDefine` back to `NameExprTypeDefine` if no actual usage is found. Also , as the last phase, attributes no longer needed will be cleaned up after this phase. **Runtime Stage**: 1. **Variable Allocation**: - Modify the runtime so that encountering a `NameExprTypeHeapDefine` triggers the allocation of a new `heapItemValue` for it, which will be used by any `NameExprTypeHeapUse`. 2. **Function Literal Handling**: - During the execution of `doOpFuncLit`, retrieve the `HeapCapture` values (previously allocated heap item values) and fill in the placeholder values within the `funcLitExpr` block. - When invoking the function (`doOpCall`), the `placeHolder` values(fv.Captures) are used to update the execution context, ensuring accurate and consistent results across iterations. --------- Co-authored-by: ltzMaxwell <[email protected]> Co-authored-by: Morgan <[email protected]>
- Loading branch information
1 parent
5c876f3
commit 1a57e81
Showing
116 changed files
with
3,343 additions
and
354 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
1a57e81
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possible performance regression was detected for benchmark 'Go Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold
1.20
.BenchmarkBinary/EmptyStruct:encode
443.9
ns/op 96 B/op 2 allocs/op288.9
ns/op 96 B/op 2 allocs/op1.54
BenchmarkBinary/EmptyStruct:encode - ns/op
443.9
ns/op288.9
ns/op1.54
BenchmarkBinary/EmptyStruct:decode
274.1
ns/op 0 B/op 0 allocs/op133.1
ns/op 0 B/op 0 allocs/op2.06
BenchmarkBinary/EmptyStruct:decode - ns/op
274.1
ns/op133.1
ns/op2.06
BenchmarkBinary/ShortArraysStruct:encode
744.8
ns/op 192 B/op 4 allocs/op619.2
ns/op 192 B/op 4 allocs/op1.20
BenchmarkBinary/ShortArraysStruct:encode - ns/op
744.8
ns/op619.2
ns/op1.20
BenchmarkBinary/ShortArraysStruct:decode
383.5
ns/op 0 B/op 0 allocs/op206.4
ns/op 0 B/op 0 allocs/op1.86
BenchmarkBinary/ShortArraysStruct:decode - ns/op
383.5
ns/op206.4
ns/op1.86
BenchmarkBcryptGenerateFromPassword/benchmark-security-param
63686598
ns/op 5130 B/op 9 allocs/op31853716
ns/op 5125 B/op 9 allocs/op2.00
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - ns/op
63686598
ns/op31853716
ns/op2.00
BenchmarkBcryptGenerateFromPassword/benchmark-security-param
127325290
ns/op 5139 B/op 9 allocs/op31853716
ns/op 5125 B/op 9 allocs/op4.00
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - ns/op
127325290
ns/op31853716
ns/op4.00
BenchmarkBcryptGenerateFromPassword/benchmark-security-param
254372279
ns/op 5158 B/op 9 allocs/op31853716
ns/op 5125 B/op 9 allocs/op7.99
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - ns/op
254372279
ns/op31853716
ns/op7.99
BenchmarkBcryptGenerateFromPassword/benchmark-security-param
508910154
ns/op 5196 B/op 10 allocs/op31853716
ns/op 5125 B/op 9 allocs/op15.98
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - ns/op
508910154
ns/op31853716
ns/op15.98
BenchmarkBcryptGenerateFromPassword/benchmark-security-param
1017472615
ns/op 5528 B/op 13 allocs/op31853716
ns/op 5125 B/op 9 allocs/op31.94
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - ns/op
1017472615
ns/op31853716
ns/op31.94
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - allocs/op
13
allocs/op9
allocs/op1.44
BenchmarkBcryptGenerateFromPassword/benchmark-security-param
2034527724
ns/op 5736 B/op 15 allocs/op31853716
ns/op 5125 B/op 9 allocs/op63.87
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - ns/op
2034527724
ns/op31853716
ns/op63.87
BenchmarkBcryptGenerateFromPassword/benchmark-security-param - allocs/op
15
allocs/op9
allocs/op1.67
BenchmarkSigning
84524
ns/op 1856 B/op 36 allocs/op25711
ns/op 64 B/op 1 allocs/op3.29
BenchmarkSigning - ns/op
84524
ns/op25711
ns/op3.29
BenchmarkSigning - B/op
1856
B/op64
B/op29
BenchmarkSigning - allocs/op
36
allocs/op1
allocs/op36
BenchmarkSigning
84234
ns/op 1856 B/op 36 allocs/op25711
ns/op 64 B/op 1 allocs/op3.28
BenchmarkSigning - ns/op
84234
ns/op25711
ns/op3.28
BenchmarkSigning - B/op
1856
B/op64
B/op29
BenchmarkSigning - allocs/op
36
allocs/op1
allocs/op36
BenchmarkVerification
163432
ns/op 864 B/op 19 allocs/op61512
ns/op 0 B/op 0 allocs/op2.66
BenchmarkVerification - ns/op
163432
ns/op61512
ns/op2.66
BenchmarkVerification - B/op
864
B/op0
B/op+∞
BenchmarkVerification - allocs/op
19
allocs/op0
allocs/op+∞
BenchmarkVerification
172792
ns/op 864 B/op 19 allocs/op61512
ns/op 0 B/op 0 allocs/op2.81
BenchmarkVerification - ns/op
172792
ns/op61512
ns/op2.81
BenchmarkVerification - B/op
864
B/op0
B/op+∞
BenchmarkVerification - allocs/op
19
allocs/op0
allocs/op+∞
BenchmarkImmutableAvlTreeMemDB
3990410
ns/op 1090330 B/op 22196 allocs/op3187161
ns/op 859944 B/op 17472 allocs/op1.25
BenchmarkImmutableAvlTreeMemDB - ns/op
3990410
ns/op3187161
ns/op1.25
BenchmarkImmutableAvlTreeMemDB - B/op
1090330
B/op859944
B/op1.27
BenchmarkImmutableAvlTreeMemDB - allocs/op
22196
allocs/op17472
allocs/op1.27
BenchmarkRandomBytes/random
68.28
ns/op 16 B/op 1 allocs/op33.11
ns/op 4 B/op 1 allocs/op2.06
BenchmarkRandomBytes/random - ns/op
68.28
ns/op33.11
ns/op2.06
BenchmarkRandomBytes/random - B/op
16
B/op4
B/op4
BenchmarkRandomBytes/random
105.4
ns/op 32 B/op 1 allocs/op33.11
ns/op 4 B/op 1 allocs/op3.18
BenchmarkRandomBytes/random - ns/op
105.4
ns/op33.11
ns/op3.18
BenchmarkRandomBytes/random - B/op
32
B/op4
B/op8
BenchmarkRandomBytes/random
266.6
ns/op 112 B/op 1 allocs/op33.11
ns/op 4 B/op 1 allocs/op8.05
BenchmarkRandomBytes/random - ns/op
266.6
ns/op33.11
ns/op8.05
BenchmarkRandomBytes/random - B/op
112
B/op4
B/op28
BenchmarkRandomBytes/random
2309
ns/op 1024 B/op 1 allocs/op33.11
ns/op 4 B/op 1 allocs/op69.74
BenchmarkRandomBytes/random - ns/op
2309
ns/op33.11
ns/op69.74
BenchmarkRandomBytes/random - B/op
1024
B/op4
B/op256
BenchmarkSmall/boltdb-1000-100-16-40/update
1390200
ns/op 41648 B/op 380 allocs/op978004
ns/op 37694 B/op 372 allocs/op1.42
BenchmarkSmall/boltdb-1000-100-16-40/update - ns/op
1390200
ns/op978004
ns/op1.42
BenchmarkSmall/memdb-1000-100-16-40/block
17119908
ns/op 9291445 B/op 169551 allocs/op13062998
ns/op 6580545 B/op 116786 allocs/op1.31
BenchmarkSmall/memdb-1000-100-16-40/block - ns/op
17119908
ns/op13062998
ns/op1.31
BenchmarkSmall/memdb-1000-100-16-40/block - B/op
9291445
B/op6580545
B/op1.41
BenchmarkSmall/memdb-1000-100-16-40/block - allocs/op
169551
allocs/op116786
allocs/op1.45
BenchmarkMedium/boltdb-100000-100-16-40/update
6688930
ns/op 129376 B/op 1004 allocs/op5224894
ns/op 99221 B/op 840 allocs/op1.28
BenchmarkMedium/boltdb-100000-100-16-40/update - ns/op
6688930
ns/op5224894
ns/op1.28
BenchmarkMedium/boltdb-100000-100-16-40/update - B/op
129376
B/op99221
B/op1.30
BenchmarkMedium/memdb-100000-100-16-40/update
1343172
ns/op 392362 B/op 7774 allocs/op1009813
ns/op 253287 B/op 4884 allocs/op1.33
BenchmarkMedium/memdb-100000-100-16-40/update - ns/op
1343172
ns/op1009813
ns/op1.33
BenchmarkMedium/memdb-100000-100-16-40/update - B/op
392362
B/op253287
B/op1.55
BenchmarkMedium/memdb-100000-100-16-40/update - allocs/op
7774
allocs/op4884
allocs/op1.59
BenchmarkLevelDBBatchSizes/goleveldb-100000-400-16-40/update - B/op
49274
B/op38712
B/op1.27
BenchmarkLevelDBBatchSizes/goleveldb-100000-400-16-40/update - allocs/op
588
allocs/op448
allocs/op1.31
BenchmarkLevelDBBatchSizes/goleveldb-100000-2000-16-40/update - allocs/op
412
allocs/op338
allocs/op1.22
BenchmarkHash/ripemd160
2843
ns/op 25 B/op 1 allocs/op697.9
ns/op 25 B/op 1 allocs/op4.07
BenchmarkHash/ripemd160 - ns/op
2843
ns/op697.9
ns/op4.07
BenchmarkHash/sha2-256
521.9
ns/op 33 B/op 1 allocs/op168.6
ns/op 33 B/op 1 allocs/op3.10
BenchmarkHash/sha2-256 - ns/op
521.9
ns/op168.6
ns/op3.10
BenchmarkHash/sha3-256
1841
ns/op 33 B/op 1 allocs/op689
ns/op 33 B/op 1 allocs/op2.67
BenchmarkHash/sha3-256 - ns/op
1841
ns/op689
ns/op2.67
BenchmarkWriteSecretConnection
6136
ns/op 0 B/op 0 allocs/op4024
ns/op 0 B/op 0 allocs/op1.52
BenchmarkWriteSecretConnection - ns/op
6136
ns/op4024
ns/op1.52
BenchmarkReadSecretConnection
3655
ns/op 0 B/op 0 allocs/op2357
ns/op 0 B/op 0 allocs/op1.55
BenchmarkReadSecretConnection - ns/op
3655
ns/op2357
ns/op1.55
BenchmarkCacheStoreIterator100000
34037844
ns/op 6168066 B/op 29326 allocs/op26119292
ns/op 5791769 B/op 22734 allocs/op1.30
BenchmarkCacheStoreIterator100000 - ns/op
34037844
ns/op26119292
ns/op1.30
BenchmarkCacheStoreIterator100000 - allocs/op
29326
allocs/op22734
allocs/op1.29
This comment was automatically generated by workflow using github-action-benchmark.
CC: @ajnavarro @thehowl @zivkovicmilos