// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package verify import ( "fmt" "slices" ) // Block describes one basic block within a function: a maximal sequence of // instructions with a single entry point (a label or the function start) and // a single exit (a jump, conditional jump or RET). type Block struct { Offset int // byte offset within the function Label string // label name ("(entry)" for the function entry block) } // Blocks identifies the basic blocks of a function from its local labels. // Each label is a potential jump target and therefore a block boundary; the // function entry (offset 0) is always a block. The blocks are returned in // ascending offset order. func (k *Kernel) Blocks(name string) ([]Block, error) { idx, ok := k.funcs[name] if !ok { return nil, fmt.Errorf("verify: function %q not found", name) } fl := k.img.Funcs[idx] blocks := []Block{{Offset: 0, Label: "(entry)"}} // Build a reverse map: offset → label name. offToLabel := make(map[int]string, len(fl.Labels)) for label, off := range fl.Labels { if off > 0 && off < fl.Size { offToLabel[off] = label } } // Collect and sort offsets. offsets := make([]int, 0, len(offToLabel)) for off := range offToLabel { offsets = append(offsets, off) } slices.Sort(offsets) for _, off := range offsets { blocks = append(blocks, Block{Offset: off, Label: offToLabel[off]}) } return blocks, nil } // BlockCount returns the number of identified basic blocks for the function. func (k *Kernel) BlockCount(name string) (int, error) { blocks, err := k.Blocks(name) if err != nil { return 0, err } return len(blocks), nil }