op,internal/ops: support CallOps without macros

A recorded macro is prefixed with an internal macro op that stores
the end of the macro. The end is used to efficiently skip the macro
when not calling it. The call a macro, the CallOp stores the start
position of the macro.

To support seamless wrapping of Ops lists, this change removes the
dependency on the macro op prefix from CallOp. Internal code can
now call an Ops like this:

    var ops op.Ops
    var wrapper op.Ops

    ops.AddCall(&wrapper.Internal, &ops.Internal, ops.PC{}, ops.PCFor(&ops.Internal))

References: https://todo.sr.ht/~eliasnaur/gio/318
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-01-20 09:50:25 +01:00
parent c0f3ec88e9
commit 65199a2274
3 changed files with 32 additions and 25 deletions
+4 -2
View File
@@ -119,7 +119,7 @@ const (
const ( const (
TypeMacroLen = 1 + 4 + 4 TypeMacroLen = 1 + 4 + 4
TypeCallLen = 1 + 4 + 4 TypeCallLen = 1 + 4 + 4 + 4 + 4
TypeDeferLen = 1 TypeDeferLen = 1
TypePushTransformLen = 1 + 4*6 TypePushTransformLen = 1 + 4*6
TypeTransformLen = 1 + 1 + 4*6 TypeTransformLen = 1 + 1 + 4*6
@@ -240,12 +240,14 @@ func FillMacro(o *Ops, startPC PC) {
bo.PutUint32(data[5:], uint32(pc.refs)) bo.PutUint32(data[5:], uint32(pc.refs))
} }
func AddCall(o *Ops, callOps *Ops, pc PC) { func AddCall(o *Ops, callOps *Ops, pc PC, end PC) {
data := Write1(o, TypeCallLen, callOps) data := Write1(o, TypeCallLen, callOps)
data[0] = byte(TypeCall) data[0] = byte(TypeCall)
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[1:], uint32(pc.data)) bo.PutUint32(data[1:], uint32(pc.data))
bo.PutUint32(data[5:], uint32(pc.refs)) bo.PutUint32(data[5:], uint32(pc.refs))
bo.PutUint32(data[9:], uint32(end.data))
bo.PutUint32(data[13:], uint32(end.refs))
} }
func PushOp(o *Ops, kind StackKind) (StackID, int) { func PushOp(o *Ops, kind StackKind) (StackID, int) {
+21 -19
View File
@@ -32,8 +32,9 @@ type Key struct {
// Shadow of op.MacroOp. // Shadow of op.MacroOp.
type macroOp struct { type macroOp struct {
ops *Ops ops *Ops
pc PC start PC
end PC
} }
// PC is an instruction counter for an operation list. // PC is an instruction counter for an operation list.
@@ -52,6 +53,13 @@ type opMacroDef struct {
endpc PC endpc PC
} }
func (pc PC) Add(op OpType) PC {
return PC{
data: pc.data + op.Size(),
refs: pc.refs + op.NumRefs(),
}
}
// Reset start reading from the beginning of ops. // Reset start reading from the beginning of ops.
func (r *Reader) Reset(ops *Ops) { func (r *Reader) Reset(ops *Ops) {
r.ResetAt(ops, PC{}) r.ResetAt(ops, PC{})
@@ -128,24 +136,16 @@ func (r *Reader) Decode() (EncodedOp, bool) {
} }
var op macroOp var op macroOp
op.decode(data, refs) op.decode(data, refs)
macroData := op.ops.data[op.pc.data:]
if OpType(macroData[0]) != TypeMacro {
panic("invalid macro reference")
}
var opDef opMacroDef
opDef.decode(macroData[:TypeMacro.Size()])
retPC := r.pc retPC := r.pc
retPC.data += n retPC.data += n
retPC.refs += nrefs retPC.refs += nrefs
r.stack = append(r.stack, macro{ r.stack = append(r.stack, macro{
ops: r.ops, ops: r.ops,
retPC: retPC, retPC: retPC,
endPC: opDef.endpc, endPC: op.end,
}) })
r.ops = op.ops r.ops = op.ops
r.pc = op.pc r.pc = op.start
r.pc.data += TypeMacro.Size()
r.pc.refs += TypeMacro.NumRefs()
continue continue
case TypeMacro: case TypeMacro:
var op opMacroDef var op opMacroDef
@@ -164,7 +164,7 @@ func (op *opMacroDef) decode(data []byte) {
panic("invalid op") panic("invalid op")
} }
bo := binary.LittleEndian bo := binary.LittleEndian
data = data[:9] data = data[:TypeMacroLen]
dataIdx := int(int32(bo.Uint32(data[1:]))) dataIdx := int(int32(bo.Uint32(data[1:])))
refsIdx := int(int32(bo.Uint32(data[5:]))) refsIdx := int(int32(bo.Uint32(data[5:])))
*op = opMacroDef{ *op = opMacroDef{
@@ -179,15 +179,17 @@ func (m *macroOp) decode(data []byte, refs []interface{}) {
if OpType(data[0]) != TypeCall { if OpType(data[0]) != TypeCall {
panic("invalid op") panic("invalid op")
} }
data = data[:9] data = data[:TypeCallLen]
bo := binary.LittleEndian bo := binary.LittleEndian
dataIdx := int(int32(bo.Uint32(data[1:])))
refsIdx := int(int32(bo.Uint32(data[5:])))
*m = macroOp{ *m = macroOp{
ops: refs[0].(*Ops), ops: refs[0].(*Ops),
pc: PC{ start: PC{
data: dataIdx, data: int(int32(bo.Uint32(data[1:]))),
refs: refsIdx, refs: int(int32(bo.Uint32(data[5:]))),
},
end: PC{
data: int(int32(bo.Uint32(data[9:]))),
refs: int(int32(bo.Uint32(data[13:]))),
}, },
} }
} }
+7 -4
View File
@@ -92,8 +92,9 @@ type MacroOp struct {
// CallOp invokes the operations recorded by Record. // CallOp invokes the operations recorded by Record.
type CallOp struct { type CallOp struct {
// Ops is the list of operations to invoke. // Ops is the list of operations to invoke.
ops *ops.Ops ops *ops.Ops
pc ops.PC start ops.PC
end ops.PC
} }
// InvalidateOp requests a redraw at the given time. Use // InvalidateOp requests a redraw at the given time. Use
@@ -165,7 +166,9 @@ func (m MacroOp) Stop() CallOp {
ops.FillMacro(m.ops, m.pc) ops.FillMacro(m.ops, m.pc)
return CallOp{ return CallOp{
ops: m.ops, ops: m.ops,
pc: m.pc, // Skip macro header.
start: m.pc.Add(ops.TypeMacro),
end: ops.PCFor(m.ops),
} }
} }
@@ -176,7 +179,7 @@ func (c CallOp) Add(o *Ops) {
if c.ops == nil { if c.ops == nil {
return return
} }
ops.AddCall(&o.Internal, c.ops, c.pc) ops.AddCall(&o.Internal, c.ops, c.start, c.end)
} }
func (r InvalidateOp) Add(o *Ops) { func (r InvalidateOp) Add(o *Ops) {