func insnToC(insn instruction, blk *block) (string, error) {
	switch i := insn.Instruction.(type) {
	case bpf.LoadConstant:
		return stat("%s = %d;", regToCSym[i.Dst], i.Val)
	case bpf.LoadScratch:
		return stat("%s = m[%d];", regToCSym[i.Dst], i.N)
	case bpf.LoadAbsolute:
		return packetLoadToC(i.Size, "data + %d", i.Off)
	case bpf.LoadIndirect:
		return packetLoadToC(i.Size, "data + x + %d", i.Off)
	case bpf.LoadMemShift:
		return stat("x = 4*(*(data + %d) & 0xf);", i.Off)
	case bpf.StoreScratch:
		return stat("m[%d] = %s;", i.N, regToCSym[i.Src])
	case bpf.LoadExtension:
		if i.Num != bpf.ExtLen {
			return "", errors.Errorf("unsupported BPF extension %v", i)
		}
		return stat("a = data_end - data;")
	case bpf.ALUOpConstant:
		return stat("a %s= %d;", aluToCOp[i.Op], i.Val)
	case bpf.ALUOpX:
		return stat("a %s= x;", aluToCOp[i.Op])
	case bpf.NegateA:
		return stat("a = -a;")
	case bpf.Jump:
		return stat("goto %s;", blk.skipToBlock(skip(i.Skip)).Label())
	case bpf.JumpIf:
		return condToC(skip(i.SkipTrue), skip(i.SkipFalse), blk, condToCFmt[i.Cond], i.Val)
	case bpf.JumpIfX:
		return condToC(skip(i.SkipTrue), skip(i.SkipFalse), blk, condToCFmt[i.Cond], "x")
	case bpf.RetA:
		return stat("return a;")
	case bpf.RetConstant:
		return stat("return %d;", i.Val)
	case bpf.TXA:
		return stat("a = x;")
	case bpf.TAX:
		return stat("x = a;")
	case packetGuardAbsolute:
		return stat("if (data + %d > data_end) return 0;", i.guard)
	case packetGuardIndirect:
		return stat("if (data + x + %d > data_end) return 0;", i.guard)
	case checkXNotZero:
		return stat("if (x == 0) return 0;")
	default:
		return "", errors.Errorf("unsupported instruction %v", insn)
	}
}
insnToC compiles an instruction to a single C line / statement.
package cbpfc
import (
	"fmt"
	"regexp"
	"strings"
	"text/template"
	"github.com/pkg/errors"
	"golang.org/x/net/bpf"
)
const funcTemplate = `
static inline
uint32_t {{.Name}}(const uint8_t *const data, const uint8_t *const data_end) {
	__attribute__((unused))
	uint32_t a, x, m[16];
{{range $i, $b := .Blocks}}
{{$b.Label}}:
__attribute__((unused));
{{- range $i, $s := $b.Statements}}
	{{$s}}
{{- end}}
{{end}}
}`
type cFunction struct {
	Name   string
	Blocks []cBlock
}
var regToCSym = map[bpf.Register]string{
	bpf.RegA: "a",
	bpf.RegX: "x",
}
var aluToCOp = map[bpf.ALUOp]string{
	bpf.ALUOpAdd:        "+",
	bpf.ALUOpSub:        "-",
	bpf.ALUOpMul:        "*",
	bpf.ALUOpDiv:        "/",
	bpf.ALUOpOr:         "|",
	bpf.ALUOpAnd:        "&",
	bpf.ALUOpShiftLeft:  "<<",
	bpf.ALUOpShiftRight: ">>",
	bpf.ALUOpMod:        "%",
	bpf.ALUOpXor:        "^",
}
var condToCFmt = map[bpf.JumpTest]string{
	bpf.JumpEqual:          "a == %v",
	bpf.JumpNotEqual:       "a != %v",
	bpf.JumpGreaterThan:    "a > %v",
	bpf.JumpLessThan:       "a < %v",
	bpf.JumpGreaterOrEqual: "a >= %v",
	bpf.JumpLessOrEqual:    "a <= %v",
	bpf.JumpBitsSet:        "a & %v",
	bpf.JumpBitsNotSet:     "!(a & %v)",
}
var funcNameRegex = regexp.MustCompile(`^[A-Za-z_][0-9A-Za-z_]*$`)
type cBlock struct {
	*block
	Statements []string
}
type COpts struct {
	FunctionName string
}
func ToC(filter []bpf.Instruction, opts COpts) (string, error) {
	if !funcNameRegex.MatchString(opts.FunctionName) {
		return "", errors.Errorf("invalid FunctionName %q", opts.FunctionName)
	}
	blocks, err := compile(filter)
	if err != nil {
		return "", err
	}
	fun := cFunction{
		Name:   opts.FunctionName,
		Blocks: make([]cBlock, len(blocks)),
	}
	for i, block := range blocks {
		fun.Blocks[i], err = blockToC(block)
		if err != nil {
			return "", err
		}
	}
	tmpl, err := template.New("cbfp_func").Parse(funcTemplate)
	if err != nil {
		return "", errors.Wrapf(err, "unable to parse func template")
	}
	c := strings.Builder{}
	if err := tmpl.Execute(&c, fun); err != nil {
		return "", errors.Wrapf(err, "unable to execute func template")
	}
	return c.String(), nil
}
func blockToC(blk *block) (cBlock, error) {
	cBlk := cBlock{
		block:      blk,
		Statements: make([]string, len(blk.insns)),
	}
	for i, insn := range blk.insns {
		stat, err := insnToC(insn, blk)
		if err != nil {
			return cBlk, errors.Wrapf(err, "unable to compile %v", insn)
		}
		cBlk.Statements[i] = stat
	}
	return cBlk, nil
}