compiler: improve debugging experience

Currently one instruction can correspond to multiple sequence points
because of inlining. This leads to a bad user experience as only
the last one is used. In this commit we create a sequence point for each
inlined call and also make sure that each time a new sequence point is
created the corresponding opcode can easily be seen in code.

The NOPs increase contract size, but not to a large degree. Other
solutions considered:
1. Discard NOPs if a special flag is provided. Still leads to bad
   debugging experience if deployed contract differs from the debugged
   one.
2. Create an issue for a debugger. When multiple sequence points are
   provided for a single instruction they can be used to emulate
   non-inline behaviour with pseudo-NOPs. I believe this is what windows
   debugger does (the last paragraph https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugging-optimized-code-and-inline-functions-external )
3. Emit debug info for inlined functions even without creating a
   function in the NEF itself. This should be done for each called
   instance and would also create overlapping opcode ranges for
   the enclosing function. However this approach can also ensure
   consistent values view for inlined function parameters.

Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
Evgeniy Stratonikov 2022-04-14 13:33:42 +03:00
parent 6ff11baa1b
commit 304900e765
2 changed files with 6 additions and 2 deletions

View file

@ -593,7 +593,6 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.AssignStmt:
multiRet := len(n.Rhs) != len(n.Lhs)
c.saveSequencePoint(n)
// Assign operations are grouped https://github.com/golang/go/blob/master/src/go/types/stmt.go#L160
isAssignOp := token.ADD_ASSIGN <= n.Tok && n.Tok <= token.AND_NOT_ASSIGN
if isAssignOp {
@ -708,8 +707,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.processDefers()
c.saveSequencePoint(n)
if len(c.pkgInfoInline) == 0 {
c.saveSequencePoint(n)
emit.Opcodes(c.prog.BinWriter, opcode.RET)
}
return nil

View file

@ -21,6 +21,11 @@ import (
// <inline body of f directly>
// }
func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
// Save sequence point for the debugger. Not having NOP can result in
// one instruction being used by multiple sequence points.
c.saveSequencePoint(n)
emit.Opcodes(c.prog.BinWriter, opcode.NOP)
labelSz := len(c.labelList)
offSz := len(c.inlineLabelOffsets)
c.inlineLabelOffsets = append(c.inlineLabelOffsets, labelSz)