Skip to content

Commit

Permalink
asm: Instruction - omit bpfInstruction and binary.Read in (Un)Marshal
Browse files Browse the repository at this point in the history
This patch introduces a hand-written (un)marshaler for asm.Instruction and
eliminates the struct bpfInstruction wire format. This saves on a few allocs
and shaves off about ~80% of CPU time spent (un)marshaling on my machine.

name                    old time/op    new time/op    delta
Read64bitImmediate-16      472ns ± 4%      69ns ± 3%  -85.28%  (p=0.004 n=5+6)
Write64BitImmediate-16     405ns ± 2%      89ns ± 3%  -78.11%  (p=0.004 n=5+6)

name                    old alloc/op   new alloc/op   delta
Read64bitImmediate-16      32.0B ± 0%      8.0B ± 0%  -75.00%  (p=0.026 n=5+6)
Write64BitImmediate-16     24.0B ± 0%     16.0B ± 0%  -33.33%  (p=0.026 n=5+6)

name                    old allocs/op  new allocs/op  delta
Read64bitImmediate-16       4.00 ± 0%      1.00 ± 0%  -75.00%  (p=0.026 n=5+6)
Write64BitImmediate-16      3.00 ± 0%      2.00 ± 0%  -33.33%  (p=0.026 n=5+6)

Signed-off-by: Timo Beckers <timo@isovalent.com>
  • Loading branch information
ti-mo committed Jan 14, 2022
1 parent 68fe573 commit efc7fc2
Showing 1 changed file with 35 additions and 34 deletions.
69 changes: 35 additions & 34 deletions asm/instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,45 @@ func (ins Instruction) Sym(name string) Instruction {

// Unmarshal decodes a BPF instruction.
func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) {
var bi bpfInstruction
err := binary.Read(r, bo, &bi)
if err != nil {
data := make([]byte, InstructionSize)
if _, err := io.ReadFull(r, data); err != nil {
return 0, err
}

ins.OpCode = bi.OpCode
ins.Offset = bi.Offset
ins.Constant = int64(bi.Constant)
ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo)
if err != nil {
return 0, fmt.Errorf("can't unmarshal registers: %s", err)
ins.OpCode = OpCode(data[0])

regs := data[1]
switch bo {
case binary.LittleEndian:
ins.Dst, ins.Src = Register(regs&0xF), Register(regs>>4)
case binary.BigEndian:
ins.Dst, ins.Src = Register(regs>>4), Register(regs&0xf)
}

if !bi.OpCode.IsDWordLoad() {
ins.Offset = int16(bo.Uint16(data[2:4]))
// Convert to int32 before widening to int64
// to ensure the signed bit is carried over.
ins.Constant = int64(int32(bo.Uint32(data[4:8])))

if !ins.OpCode.IsDWordLoad() {
return InstructionSize, nil
}

var bi2 bpfInstruction
if err := binary.Read(r, bo, &bi2); err != nil {
// Pull another instruction from the stream to retrieve the second
// half of the 64-bit immediate value.
if _, err := io.ReadFull(r, data); err != nil {
// No Wrap, to avoid io.EOF clash
return 0, errors.New("64bit immediate is missing second half")
}
if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 {

// Require that all fields other than the value are zero.
if bo.Uint32(data[0:4]) != 0 {
return 0, errors.New("64bit immediate has non-zero fields")
}
ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant)))

cons1 := uint32(ins.Constant)
cons2 := int32(bo.Uint32(data[4:8]))
ins.Constant = int64(cons2)<<32 | int64(cons1)

return 2 * InstructionSize, nil
}
Expand All @@ -97,26 +109,22 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
return 0, fmt.Errorf("can't marshal registers: %s", err)
}

bpfi := bpfInstruction{
ins.OpCode,
regs,
ins.Offset,
cons,
}

if err := binary.Write(w, bo, &bpfi); err != nil {
data := make([]byte, InstructionSize)
data[0] = byte(ins.OpCode)
data[1] = byte(regs)
bo.PutUint16(data[2:4], uint16(ins.Offset))
bo.PutUint32(data[4:8], uint32(cons))
if _, err := w.Write(data); err != nil {
return 0, err
}

if !isDWordLoad {
return InstructionSize, nil
}

bpfi = bpfInstruction{
Constant: int32(ins.Constant >> 32),
}

if err := binary.Write(w, bo, &bpfi); err != nil {
data = make([]byte, InstructionSize)
bo.PutUint32(data[4:8], uint32(ins.Constant>>32))
if _, err := w.Write(data); err != nil {
return 0, err
}

Expand Down Expand Up @@ -527,13 +535,6 @@ func (iter *InstructionIterator) Next() bool {
return true
}

type bpfInstruction struct {
OpCode OpCode
Registers bpfRegisters
Offset int16
Constant int32
}

type bpfRegisters uint8

func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
Expand Down

0 comments on commit efc7fc2

Please sign in to comment.