func (fb *functionBuilder) emitMul(ky bool, x, y, z int8, kind reflect.Kind) {
	var op runtime.Operation
	switch kind {
	case reflect.Int:
		op = runtime.OpMulInt
	case reflect.Float64:
		op = runtime.OpMulFloat64
	default:
		if z != x {
			panic(fmt.Errorf("z must be == x for kind %s", kind))
		}
		x = int8(flattenIntegerKind(kind))
		op = runtime.OpMul
	}
	if ky {
		op = -op
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: x, B: y, C: z})
}
emitMul appends a new "mul" instruction to the function body.

z = x * y
package compiler
import (
	"fmt"
	"reflect"
	"github.com/open2b/scriggo/ast"
	"github.com/open2b/scriggo/internal/runtime"
)
func (fb *functionBuilder) emitNop() {
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpNone})
}
func (fb *functionBuilder) emitAdd(k bool, x, y, z int8, kind reflect.Kind) {
	var op runtime.Operation
	switch kind {
	case reflect.Int:
		op = runtime.OpAddInt
	case reflect.Float64:
		op = runtime.OpAddFloat64
	default:
		if z != x {
			panic(fmt.Errorf("z must be == x for kind %s", kind))
		}
		x = int8(flattenIntegerKind(kind))
		op = runtime.OpAdd
	}
	if k {
		op = -op
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: x, B: y, C: z})
}
func (fb *functionBuilder) emitAddr(expr, index, dest int8, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpAddr, A: expr, B: index, C: dest})
}
func (fb *functionBuilder) emitAnd(k bool, x, y, z int8, kind reflect.Kind) {
	op := runtime.OpAnd
	if k {
		op = -op
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: x, B: y, C: z})
}
func (fb *functionBuilder) emitAndNot(k bool, x, y, z int8, kind reflect.Kind) {
	op := runtime.OpAndNot
	if k {
		op = -op
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: x, B: y, C: z})
}
func (fb *functionBuilder) emitAppend(start, end, s int8, elementsKind reflect.Kind) {
	fb.addOperandKinds(elementsKind, elementsKind, 0)
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpAppend, A: start, B: end, C: s})
}
func (fb *functionBuilder) emitAppendSlice(t, s int8, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpAppendSlice, A: t, C: s})
}
func (fb *functionBuilder) emitAssert(e int8, typ reflect.Type, z int8) {
	t := fb.addType(typ, true)
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpAssert, A: e, B: int8(t), C: z})
}
func (fb *functionBuilder) emitBreak(lab label) {
	addr := fb.labelAddrs[lab-1]
	a, b, c := encodeUint24(uint32(addr))
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpBreak, A: a, B: b, C: c})
}
func (fb *functionBuilder) emitCallFunc(f int8, shift runtime.StackShift, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpCallFunc, A: f})
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.Operation(shift[0]), A: shift[1], B: shift[2], C: shift[3]})
}
func (fb *functionBuilder) emitCallMacro(f int8, shift runtime.StackShift, pos *ast.Position, toFormat ast.Format) {
	fb.addPosAndPath(pos)
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpCallMacro, A: f, B: int8(toFormat)})
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.Operation(shift[0]), A: shift[1], B: shift[2], C: shift[3]})
}
func (fb *functionBuilder) emitCallIndirect(f int8, numVariadic int8, shift runtime.StackShift, pos *ast.Position, funcType reflect.Type, toFormat ast.Format) {
	fb.addPosAndPath(pos)
	fb.addFunctionType(funcType)
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpCallIndirect, A: f, B: int8(toFormat), C: numVariadic})
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.Operation(shift[0]), A: shift[1], B: shift[2], C: shift[3]})
}
func (fb *functionBuilder) emitCallNative(f int8, numVariadic int8, shift runtime.StackShift, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpCallNative, A: f, C: numVariadic})
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.Operation(shift[0]), A: shift[1], B: shift[2], C: shift[3]})
}
func (fb *functionBuilder) emitCap(s, z int8) {
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpCap, A: s, C: z})
}
func (fb *functionBuilder) emitCase(kvalue bool, dir reflect.SelectDir, value, ch int8) {
	in := runtime.Instruction{Op: runtime.OpCase, A: int8(dir)}
	if kvalue {
		in.Op = -in.Op
	}
	switch dir {
	case reflect.SelectSend:
		in.B = value
		in.C = ch
	case reflect.SelectRecv:
		in.B = ch
		in.C = value
	}
	fb.fn.Body = append(fb.fn.Body, in)
}
func (fb *functionBuilder) emitClose(ch int8, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpClose, A: ch})
}
func (fb *functionBuilder) emitComplex(x, y, z int8, kind reflect.Kind) {
	op := runtime.OpComplex128
	if kind == reflect.Complex64 {
		op = runtime.OpComplex64
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: x, B: y, C: z})
}
func (fb *functionBuilder) emitConcat(s, t, z int8) {
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpConcat, A: s, B: t, C: z})
}
func (fb *functionBuilder) emitContinue(lab label) {
	addr := fb.labelAddrs[lab-1]
	a, b, c := encodeUint24(uint32(addr))
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpContinue, A: a, B: b, C: c})
}
func (fb *functionBuilder) emitConvert(src int8, typ reflect.Type, dst int8, srcKind reflect.Kind) {
	fn := fb.fn
	regType := fb.addType(typ, false)
	var op runtime.Operation
	switch kindToType(srcKind) {
	case generalRegister:
		op = runtime.OpConvert
	case intRegister:
		switch srcKind {
		case reflect.Uint,
			reflect.Uint8,
			reflect.Uint16,
			reflect.Uint32,
			reflect.Uint64,
			reflect.Uintptr:
			op = runtime.OpConvertUint
		default:
			op = runtime.OpConvertInt
		}
	case stringRegister:
		op = runtime.OpConvertString
	case floatRegister:
		op = runtime.OpConvertFloat
	}
	fn.Body = append(fn.Body, runtime.Instruction{Op: op, A: src, B: int8(regType), C: dst})
}
func (fb *functionBuilder) emitCopy(dst, src, n int8) {
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpCopy, A: src, B: n, C: dst})
}
func (fb *functionBuilder) emitDefer(f int8, numVariadic int8, off, arg runtime.StackShift, funcType reflect.Type) {
	fb.addFunctionType(funcType)
	fn := fb.fn
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpDefer, A: f, C: numVariadic})
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.Operation(off[0]), A: off[1], B: off[2], C: off[3]})
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.Operation(arg[0]), A: arg[1], B: arg[2], C: arg[3]})
}
func (fb *functionBuilder) emitDelete(m, k int8) {
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpDelete, A: m, B: k})
}
func (fb *functionBuilder) emitDiv(ky bool, x, y, z int8, kind reflect.Kind, pos *ast.Position) {
	var op runtime.Operation
	switch kind {
	case reflect.Int:
		op = runtime.OpDivInt
	case reflect.Float64:
		op = runtime.OpDivFloat64
	default:
		if z != x {
			panic(fmt.Errorf("z must be == x for kind %s", kind))
		}
		x = int8(flattenIntegerKind(kind))
		op = runtime.OpDiv
	}
	fb.addPosAndPath(pos)
	if ky {
		op = -op
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: x, B: y, C: z})
}
func (fb *functionBuilder) emitField(a, field, c int8, dstKind reflect.Kind) {
	fb.addOperandKinds(0, 0, dstKind)
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpField, A: a, B: field, C: c})
}
func (fb *functionBuilder) emitGetVar(v int, r int8, varKind reflect.Kind) {
	a, b := encodeInt16(int16(v))
	fb.addOperandKinds(0, 0, varKind)
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpGetVar, A: a, B: b, C: r})
}
func (fb *functionBuilder) emitGetVarAddr(v int, r int8) {
	a, b := encodeInt16(int16(v))
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpGetVarAddr, A: a, B: b, C: r})
}
func (fb *functionBuilder) emitGo() {
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpGo})
}
func (fb *functionBuilder) emitGoto(lab label) {
	in := runtime.Instruction{Op: runtime.OpGoto}
	if lab > 0 {
		if lab > label(len(fb.labelAddrs)) {
			panic(internalError("invalid label"))
		}
		addr := fb.labelAddrs[lab-1]
		if addr == 0 {
			fb.gotos[fb.currentAddr()] = lab
		} else {
			in.A, in.B, in.C = encodeUint24(uint32(addr))
		}
	}
	fb.fn.Body = append(fb.fn.Body, in)
}
func (fb *functionBuilder) emitIf(ky bool, x int8, o runtime.Condition, y int8, kind reflect.Kind, pos *ast.Position) {
	fb.addPosAndPath(pos)
	var op runtime.Operation
	switch kindToType(kind) {
	case intRegister:
		op = runtime.OpIfInt
	case floatRegister:
		op = runtime.OpIfFloat
	case stringRegister:
		op = runtime.OpIfString
	case generalRegister:
		op = runtime.OpIf
	}
	if ky {
		op = -op
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: x, B: int8(o), C: y})
}
func (fb *functionBuilder) emitIndex(ki bool, expr, i, dst int8, t reflect.Type, pos *ast.Position, ref bool) {
	fb.addPosAndPath(pos)
	fn := fb.fn
	kind := t.Kind()
	var op runtime.Operation
	switch kind {
	case reflect.Array, reflect.Slice:
		if ref {
			op = runtime.OpIndexRef
		} else {
			op = runtime.OpIndex
		}
		fb.addOperandKinds(0, 0, t.Elem().Kind())
	case reflect.Map:
		op = runtime.OpMapIndex
		fb.addOperandKinds(0, t.Key().Kind(), t.Elem().Kind())
	case reflect.String:
		op = runtime.OpIndexString
	default:
		panic(internalError("invalid type %s", t))
	}
	if ki {
		op = -op
	}
	fn.Body = append(fn.Body, runtime.Instruction{Op: op, A: expr, B: i, C: dst})
}
func (fb *functionBuilder) emitLen(s, l int8, t reflect.Type) {
	a := stringRegister
	if t.Kind() != reflect.String {
		a = generalRegister
	}
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpLen, A: int8(a), B: s, C: l})
}
func (fb *functionBuilder) emitLoadFunc(native bool, f int8, z int8) {
	fn := fb.fn
	var a int8
	if native {
		a = 1
	}
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpLoadFunc, A: a, B: f, C: z})
}
func (fb *functionBuilder) emitLoad(index int, dst int8, kind reflect.Kind) {
	a, b := encodeValueIndex(kindToType(kind), index)
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpLoad, A: a, B: b, C: dst})
}
func (fb *functionBuilder) emitMakeArray(typ reflect.Type, dst int8) {
	if typ.Kind() != reflect.Array {
		panic(internalError("%s is not an array type", typ))
	}
	fn := fb.fn
	b := fb.addType(typ, false)
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpMakeArray, B: int8(b), C: dst})
}
func (fb *functionBuilder) emitMakeChan(typ reflect.Type, kCapacity bool, capacity int8, dst int8, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fn := fb.fn
	t := fb.addType(typ, false)
	op := runtime.OpMakeChan
	if kCapacity {
		op = -op
	}
	fn.Body = append(fn.Body, runtime.Instruction{Op: op, A: int8(t), B: capacity, C: dst})
}
func (fb *functionBuilder) emitMakeMap(typ reflect.Type, kSize bool, size int8, dst int8) {
	fn := fb.fn
	t := fb.addType(typ, false)
	op := runtime.OpMakeMap
	if kSize {
		op = -op
	}
	fn.Body = append(fn.Body, runtime.Instruction{Op: op, A: int8(t), B: size, C: dst})
}
func (fb *functionBuilder) emitMakeSlice(kLen, kCap bool, sliceType reflect.Type, len, cap, dst int8, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fn := fb.fn
	t := fb.addType(sliceType, false)
	var k int8
	if len == 0 && cap == 0 {
		k = 0
	} else {
		k = 1
		if kLen {
			k |= 1 << 1
		}
		if kCap {
			k |= 1 << 2
		}
	}
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpMakeSlice, A: int8(t), B: k, C: dst})
	if k > 0 {
		fn.Body = append(fn.Body, runtime.Instruction{A: len, B: cap})
	}
}
func (fb *functionBuilder) emitMakeStruct(typ reflect.Type, dst int8) {
	if typ.Kind() != reflect.Struct {
		panic(internalError("%s is not a struct type", typ.Kind()))
	}
	fn := fb.fn
	b := fb.addType(typ, false)
	fn.Body = append(fn.Body, runtime.Instruction{Op: runtime.OpMakeStruct, B: int8(b), C: dst})
}
func (fb *functionBuilder) emitMethodValue(name int8, receiver int8, dst int8, pos *ast.Position) {
	fb.addPosAndPath(pos)
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: runtime.OpMethodValue, A: receiver, B: name, C: dst})
}
func (fb *functionBuilder) emitMove(k bool, x, z int8, kind reflect.Kind) {
	op := runtime.OpMove
	if k {
		op = -op
	}
	a := int8(kindToType(kind))
	fb.fn.Body = append(fb.fn.Body, runtime.Instruction{Op: op, A: a, B: x, C: z})
}