Skip to content

Commit

Permalink
proc: use stack machine to evaluate expressions (#3508)
Browse files Browse the repository at this point in the history
* proc: use stack machine to evaluate expressions

This commit splits expression evaluation into two parts. The first part (in
pkg/proc/evalop/evalcompile.go) "compiles" as ast.Expr into a list of
instructions (defined in pkg/proc/evalop/ops.go) for a stack machine
(defined by `proc.(*evalStack)`).
The second part is a stack machine (implemented by `proc.(*EvalScope).eval`
and `proc.(*EvalScope).evalOne`) that has two modes of operation: in the
main mode it executes inteructions from the list (by calling `evalOne`), in
the second mode it executes the call injection protocol by calling
`funcCallStep` repeatedly until it either the protocol finishes, needs more
input from the stack machine (to set call arguments) or fails.

This approach has several benefits:

- it is now possible to remove the goroutine we use to evaluate expression
  and the channel used to communicate with the Continue loop.
- every time we resume the target to execute the call injection protocol we
  need to update several local variables to match the changed state of the
  target, this is now done at the top level of the evaluation loop instead of
  being hidden inside a recurisive evaluator
- using runtime.Pin to pin addresses returned by an injected call would
  allow us to use a more natural evaluation order for function calls, which
  would solve some bugs #3310, allow users to inspect values returned by a
  call injection #1599 and allow implementing some other features #1465. Doing
  this with the recursive evaluator, while keeping backwards compatibility
  with versions of Go that do not have runtime.Pin is very hard. However after
  this change we can simply conditionally change how compileFunctionCall works
  and add some opcodes.

* review round 1

* review round 2
  • Loading branch information
aarzilli authored Oct 17, 2023
1 parent 788df88 commit 2c70023
Show file tree
Hide file tree
Showing 11 changed files with 1,874 additions and 814 deletions.
55 changes: 55 additions & 0 deletions pkg/dwarf/godwarf/fakes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package godwarf

import (
"fmt"
"math/bits"
"reflect"
)

// FakeSliceType synthesizes a slice type with the given field type.
func FakeSliceType(fieldType Type) Type {
return &SliceType{
StructType: StructType{
CommonType: CommonType{
ByteSize: 24,
Name: "",
},
StructName: "[]" + fieldType.Common().Name,
Kind: "struct",
Field: nil,
},
ElemType: fieldType,
}
}

// FakeBasicType synthesizes a basic type numeric type (int8, uint16,
// float32, etc)
func FakeBasicType(name string, bitSize int) Type {
byteSize := bitSize / 8
szr := bits.OnesCount64(uint64(byteSize^(byteSize-1))) - 1 // position of rightmost 1 bit, minus 1

basic := func(kind reflect.Kind) BasicType {
return BasicType{
CommonType: CommonType{
ByteSize: int64(byteSize),
Name: fmt.Sprintf("%s%d", name, bitSize),
ReflectKind: kind,
},
BitSize: int64(bitSize),
BitOffset: 0,
}
}

switch name {
case "int":
return &IntType{BasicType: basic(reflect.Int8 + reflect.Kind(szr))}
case "uint":
return &UintType{BasicType: basic(reflect.Uint8 + reflect.Kind(szr))}
case "float":
return &FloatType{BasicType: basic(reflect.Float32 + reflect.Kind(szr-2))}
case "complex":
return &ComplexType{BasicType: basic(reflect.Complex64 + reflect.Kind(szr-3))}
default:
panic("unsupported")
}
}
Loading

0 comments on commit 2c70023

Please sign in to comment.