Mostly it's about Go 1.22+ syntax with ranging over integers, but it also
prefers ranging over slices where possible (it makes code a little better to
read).
Notice that we have a number of dangerous loops where slices are mutated
during loop execution, many of these can't be converted since we need proper
length evalutation at every iteration.
Signed-off-by: Roman Khimov <roman@nspcc.ru>
Make them behave as locals. We must initialize them at the start
because the default value could also be used inside the function body.
Signed-off-by: Evgenii Stratonikov <fyfyrchik@runbox.com>
Unfortunately, when import cycle happens somewhere deep in the import chain we
dont't get an error from packages.Load(). But it leaves some imports
uninitialized, so at least we can check for them.
Signed-off-by: Roman Khimov <roman@nspcc.ru>
Currently we take int64 value from the Go parser and push it to the
stack. Using uint64 is not a common practice (usually we just use `int`),
but can be a problem while doing bit arithmetic and serializing numbers.
Signed-off-by: Evgenii Stratonikov <fyfyrchik@runbox.com>
So that (*codegen).Visit is able to omit code generation for these
unused global vars. The most tricky part is to detect unused global
variables, it is done in several steps:
1. Collect the set of named used/unused global vars.
2. Collect the set of globally declared expressions that contain
function calls.
3. Pick up global vars from the set made at step 2.
4. Traverse used functions and puck up those global vars that are used
from these functions.
5. Rename all globals that are presented in the set made at step 1
but are not presented in the set made on step 3 or step 4.
If variable is unnamed and does not contain function call then it's
treated as unused and code generation may be omitted for it
initialization/declaration.
An attempt to compile the following code leads to runtime panic:
```
package foo
type CustomInt int
func Main() int {
var i CustomInt
i = 5
return i.Do(2)
}
func (CustomInt) Do(arg int) int {
return arg
}
```
The panic:
```
panic: runtime error: index out of range [0] with length 0 [recovered]
panic: runtime error: index out of range [0] with length 0
goroutine 22 [running]:
testing.tRunner.func1.2({0xa341c0, 0xc0001606d8})
/usr/local/go/src/testing/testing.go:1209 +0x24e
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1212 +0x218
panic({0xa341c0, 0xc0001606d8})
/usr/local/go/src/runtime/panic.go:1038 +0x215
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).convertFuncDecl(0xc00015e3c0, {0xc753b8, 0xc000152c80}, 0xc000266300, 0x30)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:497 +0x10b3
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).compile.func2(0xc000152c80, 0xc00023c410)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2153 +0x3f8
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).ForEachFile.func1(0xc000229b80)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:102 +0x82
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).ForEachPackage(0xc00015e3c0, 0xc000189bb0)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:93 +0xc6
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).ForEachFile(0x999a20, 0xc000130d80)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:99 +0x45
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).compile(0xc00015e3c0, 0xc0002669f0, 0x1)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2140 +0x445
github.com/nspcc-dev/neo-go/pkg/compiler.codeGen(0xc0002669f0)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2191 +0x353
github.com/nspcc-dev/neo-go/pkg/compiler.CompileWithOptions({0xa6f39a, 0x50b6b3}, {0xc6d1a0, 0xc0002421e0}, 0x0)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:218 +0x65
github.com/nspcc-dev/neo-go/pkg/compiler_test.vmAndCompileInterop(0x5648df, {0xa9bf23, 0x94})
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/vm_test.go:75 +0x113
github.com/nspcc-dev/neo-go/pkg/compiler_test.eval(0xc0002421c0, {0xa9bf23, 0x61be8c7}, {0xa68880, 0xc0002421c0})
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/vm_test.go:36 +0x2d
github.com/nspcc-dev/neo-go/pkg/compiler_test.TestUnnamedMethodReceiver(0x4079f9)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/function_call_test.go:400 +0x4f
testing.tRunner(0xc000204b60, 0xbcebb0)
/usr/local/go/src/testing/testing.go:1259 +0x102
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:1306 +0x35a
```
The solution is to use the same approach as for unnamed function
parameters handling introduced in #2204. (c *funcScope).newVariable is
able to properly handle "_" receiver.
In case there are no returns in the inlined function, jumps point to the
next instruction and can be omitted. This optimization can be extended
to handle other cases, here we just make sure that already existing code
stays the same.
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
c.funcs contains function names using base types, while methods can be defined
on pointers and the value returned from c.getFuncNameFromSelector will have an
asterisk. We can't have the same name used for (*T) and (T) methods, so just
stripping the asterisk allows to get the right one.
Regular methods need this, because it'll be packed into parameters, but
inlined ones should deal with it in inlining code itself because method
receiver will be some local (aliased) variable anyway.
When `_` is unused it can be omitted from constant values mapping.
Catched when compiling `netmap` contract from nspcc-dev/neofs-contract.
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
CONVERT call base price is 8192. DUP, ISTYPE and JMPIF all cost 2. So we add
0.07% overhead in the worst case and save 99.93% otherwise.
At the same time, old code was just two bytes and new one is seven, but I
think it's tolerable considering how much GAS it can potentially save.
Fix#2250.