// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package reflectdata

import (
	"fmt"
	"go/constant"
	"strconv"
	"strings"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/objw"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
)

// genhash returns a symbol which is the closure used to compute
// the hash of a value of type t.
func genhash(t *types.Type) *obj.LSym {
	return genhashSig(eqSignature(t))
}

func genhashSig(sig string) *obj.LSym {
	if len(sig) > 0 && sig[0] == sigAlign {
		_, sig = parseNum(sig[1:])
	}
	switch sig {
	case "":
		return sysClosure("memhash0")
	case string(sigMemory) + "1":
		return sysClosure("memhash8")
	case string(sigMemory) + "2":
		return sysClosure("memhash16")
	case string(sigMemory) + "4":
		return sysClosure("memhash32")
	case string(sigMemory) + "8":
		return sysClosure("memhash64")
	case string(sigMemory) + "16":
		return sysClosure("memhash128")
	case string(sigString):
		return sysClosure("strhash")
	case string(sigIface):
		return sysClosure("interhash")
	case string(sigEface):
		return sysClosure("nilinterhash")
	case string(sigFloat32):
		return sysClosure("f32hash")
	case string(sigFloat64):
		return sysClosure("f64hash")
	case string(sigFloat32) + string(sigFloat32):
		return sysClosure("c64hash")
	case string(sigFloat64) + string(sigFloat64):
		return sysClosure("c128hash")
	}

	closure := TypeLinksymLookup(".hashfunc." + sig)
	if len(closure.P) > 0 { // already generated
		return closure
	}

	if sig[0] == sigMemory {
		n, rest := parseNum(sig[1:])
		if rest == "" {
			// Just M%d. We can make a memhash_varlen closure.
			// The size of the memory region to hash is encoded in the closure.
			if memhashvarlen == nil {
				memhashvarlen = typecheck.LookupRuntimeFunc("memhash_varlen")
			}
			ot := 0
			ot = objw.SymPtr(closure, ot, memhashvarlen, 0)
			ot = objw.Uintptr(closure, ot, uint64(n)) // size encoded in closue
			objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
			return closure
		}
	}

	if base.Flag.LowerR != 0 {
		fmt.Printf("genhash %s\n", sig)
	}

	fn := hashFunc(sig)

	// Build closure. It doesn't close over any variables, so
	// it contains just the function pointer.
	objw.SymPtr(closure, 0, fn.Linksym(), 0)
	objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
	return closure
}

func hashFunc(sig string) *ir.Func {
	sym := types.TypeSymLookup(".hash." + sig)
	if sym.Def != nil {
		return sym.Def.(*ir.Name).Func
	}

	pos := base.AutogeneratedPos // less confusing than end of input
	base.Pos = pos

	// func sym(p unsafe.Pointer, h uintptr) uintptr
	fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil,
		[]*types.Field{
			types.NewField(pos, typecheck.Lookup("p"), types.Types[types.TUNSAFEPTR]),
			types.NewField(pos, typecheck.Lookup("h"), types.Types[types.TUINTPTR]),
		},
		[]*types.Field{
			types.NewField(pos, nil, types.Types[types.TUINTPTR]),
		},
	))
	sym.Def = fn.Nname
	fn.Pragma |= ir.Noinline // TODO(mdempsky): We need to emit this during the unified frontend instead, to allow inlining.
	typecheck.DeclFunc(fn)
	np := fn.Dcl[0]
	nh := fn.Dcl[1]

	// Skip alignment, hash functions can handle unaligned data.
	if len(sig) > 0 && sig[0] == sigAlign {
		_, sig = parseNum(sig[1:])
	}

	// offset from np that we're currently working on
	var off int64

	// Return np+off (as an unsafe.Pointer).
	ptr := func() ir.Node {
		c := ir.NewBasicLit(pos, types.Types[types.TUINTPTR], constant.MakeInt64(off))
		return ir.NewBinaryExpr(pos, ir.OUNSAFEADD, np, c)
	}
	// hash data using function name at np+off.
	// Increment off by size.
	hash := func(name string, size int64) {
		p := ptr()
		hashFn := typecheck.LookupRuntime(name)
		call := ir.NewCallExpr(pos, ir.OCALL, hashFn, []ir.Node{p, nh})
		fn.Body.Append(ir.NewAssignStmt(pos, nh, call))
		off += size
	}

	for len(sig) > 0 {
		kind := sig[0]
		sig = sig[1:]
		switch kind {
		case sigMemory:
			var n int64
			n, sig = parseNum(sig)
			switch {
			case n == 4:
				p := ptr()
				memhash := typecheck.LookupRuntime("memhash32")
				call := ir.NewCallExpr(pos, ir.OCALL, memhash, []ir.Node{p, nh})
				fn.Body.Append(ir.NewAssignStmt(pos, nh, call))
			case n == 8:
				p := ptr()
				memhash := typecheck.LookupRuntime("memhash64")
				call := ir.NewCallExpr(pos, ir.OCALL, memhash, []ir.Node{p, nh})
				fn.Body.Append(ir.NewAssignStmt(pos, nh, call))
			default:
				p := ptr()
				memhash := typecheck.LookupRuntime("memhash")
				size := ir.NewBasicLit(pos, types.Types[types.TUINTPTR], constant.MakeInt64(n))
				call := ir.NewCallExpr(pos, ir.OCALL, memhash, []ir.Node{p, nh, size})
				fn.Body.Append(ir.NewAssignStmt(pos, nh, call))
			}
			off += n
		case sigFloat32:
			hash("f32hash", 4)
		case sigFloat64:
			hash("f64hash", 8)
		case sigString:
			hash("strhash", 2*int64(types.PtrSize))
		case sigEface:
			hash("nilinterhash", 2*int64(types.PtrSize))
		case sigIface:
			hash("interhash", 2*int64(types.PtrSize))
		case sigSkip:
			var n int64
			n, sig = parseNum(sig)
			off += n
		case sigArrayStart:
			var n int64
			var elemSig string
			n, elemSig, sig = parseArray(sig)
			elemSize := sigSize(elemSig)

			// Loop N times, calling hash function for the element.
			//     for i := off; i < off + N*elemSize; i += elemSize {
			//         h = elemfn(p+i, h)
			//     }
			elemFn := hashFunc(elemSig).Nname
			idx := typecheck.TempAt(pos, ir.CurFunc, types.Types[types.TUINTPTR])
			init := ir.NewAssignStmt(pos, idx, ir.NewInt(pos, off))
			cond := ir.NewBinaryExpr(pos, ir.OLT, idx, ir.NewInt(pos, off+n*elemSize))
			post := ir.NewAssignStmt(pos, idx, ir.NewBinaryExpr(pos, ir.OADD, idx, ir.NewInt(pos, elemSize)))

			p := ir.NewBinaryExpr(pos, ir.OUNSAFEADD, np, idx)
			call := typecheck.Call(pos, elemFn, []ir.Node{p, nh}, false)
			as := ir.NewAssignStmt(pos, nh, call)
			loop := ir.NewForStmt(pos, init, cond, post, []ir.Node{as}, false)
			fn.Body.Append(loop)
			off += n * elemSize
		}
	}

	fn.Body.Append(ir.NewReturnStmt(pos, []ir.Node{nh}))

	if base.Flag.LowerR != 0 {
		ir.DumpList("genhash body", fn.Body)
	}

	typecheck.FinishFuncBody()

	fn.SetDupok(true)

	ir.WithFunc(fn, func() {
		typecheck.Stmts(fn.Body)
	})

	fn.SetNilCheckDisabled(true)
	return fn
}

// sysClosure returns a closure which will call the
// given runtime function (with no closed-over variables).
func sysClosure(name string) *obj.LSym {
	s := typecheck.LookupRuntimeVar(name + "·f")
	if len(s.P) == 0 {
		f := typecheck.LookupRuntimeFunc(name)
		objw.SymPtr(s, 0, f, 0)
		objw.Global(s, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
	}
	return s
}

// geneq returns a symbol which is the closure used to compute
// equality for two objects of type t.
func geneq(t *types.Type) *obj.LSym {
	switch types.AlgType(t) {
	case types.ANOEQ, types.ANOALG:
		// The runtime will panic if it tries to compare
		// a type with a nil equality function.
		return nil
	}
	return geneqSig(eqSignature(t))
}

// geneqSig returns a symbol which is the closure used to compute
// equality for two objects with equality signature sig.
func geneqSig(sig string) *obj.LSym {
	align := int64(types.PtrSize)
	if len(sig) > 0 && sig[0] == sigAlign {
		align, sig = parseNum(sig[1:])
	}
	if base.Ctxt.Arch.CanMergeLoads {
		align = 8
	}
	switch sig {
	case "":
		return sysClosure("memequal0")
	case string(sigMemory) + "1":
		return sysClosure("memequal8")
	case string(sigMemory) + "2":
		if align >= 2 {
			return sysClosure("memequal16")
		}
	case string(sigMemory) + "4":
		if align >= 4 {
			return sysClosure("memequal32")
		}
	case string(sigMemory) + "8":
		if align >= 8 {
			return sysClosure("memequal64")
		}
	case string(sigMemory) + "16":
		if align >= 8 {
			return sysClosure("memequal128")
		}
	case string(sigString):
		return sysClosure("strequal")
	case string(sigIface):
		return sysClosure("interequal")
	case string(sigEface):
		return sysClosure("nilinterequal")
	case string(sigFloat32):
		return sysClosure("f32equal")
	case string(sigFloat64):
		return sysClosure("f64equal")
	case string(sigFloat32) + string(sigFloat32):
		return sysClosure("c64equal")
	case string(sigFloat64) + string(sigFloat64):
		return sysClosure("c128equal")
	}

	closure := TypeLinksymLookup(".eqfunc." + sig)
	if len(closure.P) > 0 { // already generated
		return closure
	}

	if sig[0] == sigMemory {
		n, rest := parseNum(sig[1:])
		if rest == "" {
			// Just M%d. We can make a memequal_varlen closure.
			// The size of the memory region to compare is encoded in the closure.
			if memequalvarlen == nil {
				memequalvarlen = typecheck.LookupRuntimeFunc("memequal_varlen")
			}
			ot := 0
			ot = objw.SymPtr(closure, ot, memequalvarlen, 0)
			ot = objw.Uintptr(closure, ot, uint64(n))
			objw.Global(closure, int32(ot), obj.DUPOK|obj.RODATA)
			return closure
		}
	}

	if base.Flag.LowerR != 0 {
		fmt.Printf("geneqSig %s\n", sig)
	}

	fn := eqFunc(sig)

	// Generate a closure which points at the function we just generated.
	objw.SymPtr(closure, 0, fn.Linksym(), 0)
	objw.Global(closure, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
	return closure
}

func eqFunc(sig string) *ir.Func {
	sym := types.TypeSymLookup(".eq." + sig)
	if sym.Def != nil {
		return sym.Def.(*ir.Name).Func
	}

	pos := base.AutogeneratedPos // less confusing than end of input
	base.Pos = pos

	// func sym(p, q unsafe.Pointer) bool
	fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil,
		[]*types.Field{
			types.NewField(pos, typecheck.Lookup("p"), types.Types[types.TUNSAFEPTR]),
			types.NewField(pos, typecheck.Lookup("q"), types.Types[types.TUNSAFEPTR]),
		},
		[]*types.Field{
			types.NewField(pos, typecheck.Lookup("r"), types.Types[types.TBOOL]),
		},
	))
	sym.Def = fn.Nname
	fn.Pragma |= ir.Noinline // TODO(mdempsky): We need to emit this during the unified frontend instead, to allow inlining.
	typecheck.DeclFunc(fn)
	np := fn.Dcl[0]
	nq := fn.Dcl[1]
	nr := fn.Dcl[2]

	// Label to jump to if an equality test fails.
	neq := typecheck.AutoLabel(".neq")

	// Grab known alignment of argument pointers. (ptrSize is the default.)
	align := int64(types.PtrSize)
	if len(sig) > 0 && sig[0] == sigAlign {
		sig = sig[1:]
		align, sig = parseNum(sig)
	}
	unalignedOk := base.Ctxt.Arch.CanMergeLoads

	// offset from np/nq that we're currently working on
	var off int64
	var hasCall bool

	// test takes a boolean. If it evaluates to false, short circuit
	// and return false immediately. Otherwise, keep checking.
	var lastTest ir.Node
	test := func(eq ir.Node) {
		// Buffer one test in lastTest so we can use the
		// last one as the return value.
		if lastTest != nil {
			nif := ir.NewIfStmt(pos, lastTest, nil, []ir.Node{ir.NewBranchStmt(pos, ir.OGOTO, neq)})
			fn.Body.Append(nif)
		}
		lastTest = eq
	}
	// load loads data of type t from np+off and nq+off.
	// Increments off by the size of t.
	load := func(t *types.Type) (ir.Node, ir.Node) {
		c := ir.NewBasicLit(pos, types.Types[types.TUINTPTR], constant.MakeInt64(off))
		p := ir.NewBinaryExpr(pos, ir.OUNSAFEADD, np, c)
		q := ir.NewBinaryExpr(pos, ir.OUNSAFEADD, nq, c)
		x := ir.NewStarExpr(pos, ir.NewConvExpr(pos, ir.OCONVNOP, t.PtrTo(), p))
		y := ir.NewStarExpr(pos, ir.NewConvExpr(pos, ir.OCONVNOP, t.PtrTo(), q))
		off += t.Size()
		return x, y
	}
	// compare compares x and y and jumps to neq if they are not equal.
	compare := func(x, y ir.Node) {
		test(ir.NewBinaryExpr(pos, ir.OEQ, x, y))
	}

	// We keep track of string contents that we don't compare immediately.
	// We delay comparing string contents because they might be large and
	// we'd rather compare scalars farther along in the signature first.
	var pendingStrings []int64
	flushStrings := func() {
		defer func(saveOff int64) {
			off = saveOff
		}(off)
		for _, x := range pendingStrings {
			off = x
			ptrA, ptrB := load(types.Types[types.TUNSAFEPTR])
			len, _ := load(types.Types[types.TUINTPTR])
			// Note: we already checked that the lengths are equal.
			memeq := typecheck.LookupRuntime("memequal")
			test(typecheck.Call(pos, memeq, []ir.Node{ptrA, ptrB, len}, false))
			hasCall = true
		}
		pendingStrings = pendingStrings[:0]
	}

	for len(sig) > 0 {
		kind := sig[0]
		sig = sig[1:]
		switch kind {
		case sigMemory:
			var n int64
			n, sig = parseNum(sig)
			if n > 64 { // TODO: why 64?
				// For big regions, call memequal.
				c := ir.NewBasicLit(pos, types.Types[types.TUINTPTR], constant.MakeInt64(off))
				p := ir.NewBinaryExpr(pos, ir.OUNSAFEADD, np, c)
				q := ir.NewBinaryExpr(pos, ir.OUNSAFEADD, nq, c)
				len := ir.NewBasicLit(pos, types.Types[types.TUINTPTR], constant.MakeInt64(n))
				memeq := typecheck.LookupRuntime("memequal")
				test(typecheck.Call(pos, memeq, []ir.Node{p, q, len}, false))
				hasCall = true
				off += n
				n = 0
			}
			n0 := n
			for n != 0 {
				switch {
				case n >= 8 && (unalignedOk || align >= 8 && off%8 == 0):
					compare(load(types.Types[types.TUINT64]))
					n -= 8
				case (n == 5 || n == 6 || n == 7) && unalignedOk && n0 >= 8:
					off -= 8 - n
					compare(load(types.Types[types.TUINT64]))
					n = 0
				case n >= 4 && (unalignedOk || align >= 4 && off%4 == 0):
					compare(load(types.Types[types.TUINT32]))
					n -= 4
				case n == 3 && unalignedOk && n0 >= 4:
					off--
					compare(load(types.Types[types.TUINT32]))
					n = 0
				case n >= 2 && (unalignedOk || align >= 2 && off%2 == 0):
					compare(load(types.Types[types.TUINT16]))
					n -= 2
				default:
					compare(load(types.Types[types.TUINT8]))
					n--
				}
			}
		case sigFloat32:
			compare(load(types.Types[types.TFLOAT32]))
		case sigFloat64:
			compare(load(types.Types[types.TFLOAT64]))
		case sigString:
			// Compare just the lengths right now.
			// Save the contents for later.
			pendingStrings = append(pendingStrings, off)
			off += int64(types.PtrSize)
			compare(load(types.Types[types.TUINTPTR]))
		case sigEface, sigIface:
			// flushStrings here to ensure that we only get a panic from
			// this interface test if all previous equality checks pass.
			flushStrings()
			typeX, typeY := load(types.Types[types.TUINTPTR].PtrTo())
			compare(typeX, typeY)
			dataX, dataY := load(types.Types[types.TUNSAFEPTR])
			var eqFn *ir.Name
			if kind == sigEface {
				eqFn = typecheck.LookupRuntime("efaceeq")
			} else {
				eqFn = typecheck.LookupRuntime("ifaceeq")
			}
			test(typecheck.Call(pos, eqFn, []ir.Node{typeX, dataX, dataY}, false))
			hasCall = true
		case sigSkip:
			var n int64
			n, sig = parseNum(sig)
			off += n
		case sigArrayStart:
			// Flush any pending test.
			flushStrings()
			// TODO: if the element comparison can't panic (no E or I), then
			// maybe we don't need to do this flushStrings?
			// On the other hand, maybe the unflushed string is not equal, but
			// a big following array is all equal.
			if lastTest != nil {
				nif := ir.NewIfStmt(pos, lastTest, nil, []ir.Node{ir.NewBranchStmt(pos, ir.OGOTO, neq)})
				fn.Body.Append(nif)
				lastTest = nil
			}

			var n int64
			var elemSig string
			n, elemSig, sig = parseArray(sig)
			elemSize := sigSize(elemSig)

			// Loop N times, calling comparison function for the element.
			//     for i := off; i < off + N*elemSize; i += elemSize {
			//         if !eqfn(p+i, q+i) { goto neq }
			//     }
			elemFn := eqFunc(sigTrimSkip(elemSig)).Nname
			idx := typecheck.TempAt(pos, ir.CurFunc, types.Types[types.TUINTPTR])
			init := ir.NewAssignStmt(pos, idx, ir.NewInt(pos, off))
			cond := ir.NewBinaryExpr(pos, ir.OLT, idx, ir.NewInt(pos, off+n*elemSize))
			post := ir.NewAssignStmt(pos, idx, ir.NewBinaryExpr(pos, ir.OADD, idx, ir.NewInt(pos, elemSize)))

			p := ir.NewBinaryExpr(pos, ir.OUNSAFEADD, np, idx)
			q := ir.NewBinaryExpr(pos, ir.OUNSAFEADD, nq, idx)
			call := typecheck.Call(pos, elemFn, []ir.Node{p, q}, false)
			nif := ir.NewIfStmt(pos, call, nil, []ir.Node{ir.NewBranchStmt(pos, ir.OGOTO, neq)})
			loop := ir.NewForStmt(pos, init, cond, post, []ir.Node{nif}, false)
			fn.Body.Append(loop)
			off += n * elemSize

			// TODO: if the element comparison can't panic, but has strings
			// in it, maybe we do a loop first without string contents and a
			// second loop with string contents. There is no way to accomplish
			// this now they way this code works (to call the equality
			// function of the sub-signature).
		}
	}
	// Flush any pending tests.
	// The last test is used directly as a result (instead of branching using it).
	flushStrings()
	if lastTest == nil {
		lastTest = ir.NewBool(pos, true)
	}
	as := ir.NewAssignStmt(pos, nr, lastTest)
	fn.Body.Append(as)

	// ret:
	//   return
	ret := typecheck.AutoLabel(".ret")
	fn.Body.Append(ir.NewLabelStmt(pos, ret))
	fn.Body.Append(ir.NewReturnStmt(pos, nil))

	// neq:
	//   r = false
	//   return (or goto ret)
	fn.Body.Append(ir.NewLabelStmt(pos, neq))
	fn.Body.Append(ir.NewAssignStmt(pos, nr, ir.NewBool(pos, false)))
	if hasCall {
		// Epilogue is large, so share it with the equal case.
		fn.Body.Append(ir.NewBranchStmt(pos, ir.OGOTO, ret))
	} else {
		// Epilogue is small, so don't bother sharing.
		fn.Body.Append(ir.NewReturnStmt(pos, nil))
	}
	// TODO(khr): the epilogue size detection condition above isn't perfect.
	// We should really do a generic CL that shares epilogues across
	// the board. See #24936.

	if base.Flag.LowerR != 0 {
		ir.DumpList("geneq body", fn.Body)
	}

	typecheck.FinishFuncBody()

	fn.SetDupok(true)

	ir.WithFunc(fn, func() {
		typecheck.Stmts(fn.Body)
	})

	// Disable checknils while compiling this code.
	// We are comparing a struct or an array,
	// neither of which can be nil, and our comparisons
	// are shallow.
	fn.SetNilCheckDisabled(true)
	return fn
}

// EqFor returns ONAME node represents type t's equal function, and a boolean
// to indicates whether a length needs to be passed when calling the function.
func EqFor(t *types.Type) (ir.Node, bool) {
	switch types.AlgType(t) {
	case types.AMEM:
		return typecheck.LookupRuntime("memequal"), true
	case types.ASPECIAL:
		fn := eqFunc(eqSignature(t))
		return fn.Nname, false
	}
	base.Fatalf("EqFor %v", t)
	return nil, false
}

// eqSignature returns a signature of the equality function for type t.
// If two types have the same signature, they can use the same equality function.
// The signature lists the comparisons that the equality function needs
// to make, in order. So for instance, a type like:
//
//	type S struct {
//	    i int32
//	    j uint32
//	    s string
//	    e error
//	}
//
// Will have the signature "M8SI".
//
//	M8 = 8 bytes of regular memory
//	S = string
//	I = nonempty interface
//
// The content of the signature is not intended for users. It is an
// internal condensation of the comparison operations that need to be
// performed.
// (Although, note that these names might be seen in tracebacks where
// the equality test panics due to incomparable interfaces.)
//
// Full signature spec:
//
//	M%d    = %d bytes of memory that should be compared directly
//	K%d    = %d bytes of memory that should not be compared (sKip)
//	F      = float32
//	G      = float64
//	S      = string
//	I      = non-empty interface
//	E      = empty interface
//	[%d%s] = array: repeat signature %s %d times.
//	A%d    = known alignment of type pointers (defaults to ptrSize)
//
// An alignment directive is only needed on platforms that can't do
// unaligned loads.
// If an alignment directive is present, it must be first.
// Signatures can end early; a K%d is not required at the end.
func eqSignature(t *types.Type) string {
	var e eqSigBuilder
	if !base.Ctxt.Arch.CanMergeLoads { // alignment only matters if we can't use unaligned loads
		if a := t.Alignment(); a != int64(types.PtrSize) {
			e.r.WriteString(fmt.Sprintf("%c%d", sigAlign, a))
		}
	}
	e.build(t)
	e.flush(true)
	return e.r.String()
}

const (
	sigMemory     = 'M' // followed by an integer number of bytes
	sigSkip       = 'K' // followed by an integer number of bytes
	sigFloat32    = 'F'
	sigFloat64    = 'G'
	sigString     = 'S'
	sigIface      = 'I' // non-empty interface
	sigEface      = 'E' // empty interface
	sigArrayStart = '[' // followed by an iteration count, element signature, and sigArrayEnd
	sigArrayEnd   = ']'
	sigAlign      = 'A' // followed by an integer byte alignment
)

type eqSigBuilder struct {
	r       strings.Builder
	regMem  int64 // queued up region of regular memory
	skipMem int64 // queued up region of memory to skip
}

func (e *eqSigBuilder) flush(atEnd bool) {
	if e.regMem > 0 {
		e.r.WriteString(fmt.Sprintf("%c%d", sigMemory, e.regMem))
		e.regMem = 0
	}
	if e.skipMem > 0 {
		if !atEnd {
			e.r.WriteString(fmt.Sprintf("%c%d", sigSkip, e.skipMem))
		}
		e.skipMem = 0
	}
}
func (e *eqSigBuilder) regular(n int64) {
	if e.regMem == 0 {
		e.flush(false)
	}
	e.regMem += n
}
func (e *eqSigBuilder) skip(n int64) {
	if e.skipMem == 0 {
		e.flush(false)
	}
	e.skipMem += n
}
func (e *eqSigBuilder) float32() {
	e.flush(false)
	e.r.WriteByte(sigFloat32)
}
func (e *eqSigBuilder) float64() {
	e.flush(false)
	e.r.WriteByte(sigFloat64)
}
func (e *eqSigBuilder) string() {
	e.flush(false)
	e.r.WriteByte(sigString)
}
func (e *eqSigBuilder) eface() {
	e.flush(false)
	e.r.WriteByte(sigEface)
}
func (e *eqSigBuilder) iface() {
	e.flush(false)
	e.r.WriteByte(sigIface)
}

func (e *eqSigBuilder) build(t *types.Type) {
	switch t.Kind() {
	case types.TINT8, types.TUINT8, types.TBOOL:
		e.regular(1)
	case types.TINT16, types.TUINT16:
		e.regular(2)
	case types.TINT32, types.TUINT32:
		e.regular(4)
	case types.TINT64, types.TUINT64:
		e.regular(8)
	case types.TINT, types.TUINT, types.TUINTPTR, types.TPTR, types.TUNSAFEPTR, types.TCHAN:
		e.regular(int64(types.PtrSize))
	case types.TFLOAT32:
		e.float32()
	case types.TFLOAT64:
		e.float64()
	case types.TCOMPLEX64:
		e.float32()
		e.float32()
	case types.TCOMPLEX128:
		e.float64()
		e.float64()
	case types.TSTRING:
		e.string()
	case types.TINTER:
		if t.IsEmptyInterface() {
			e.eface()
		} else {
			e.iface()
		}
	case types.TSTRUCT:
		var off int64
		for _, f := range t.Fields() {
			if f.Sym.IsBlank() {
				continue
			}
			if off < f.Offset {
				e.skip(f.Offset - off)
			}
			e.build(f.Type)
			off = f.Offset + f.Type.Size()
		}
		if off < t.Size() {
			e.skip(t.Size() - off)
		}
	case types.TARRAY:
		if types.AlgType(t) == types.AMEM {
			// TODO: some "regular equality" types don't hit here,
			// like [8]sync/atomic.Pointer. Figure out how to
			// handle the subtle difference between "AMEM" and
			// "can be compared byte-by-byte for equality".
			e.regular(t.Size())
			break
		}
		et := t.Elem()
		n := t.NumElem()
		switch n {
		case 0:
		case 1:
			e.build(et)
		default:
			// To keep signatures small, we can't just repeat
			// the element signature N times. Instead, we issue
			// an array into the signature. Note that this can
			// lead to a situation where two types which could
			// share an equality function do not, like
			//   struct { a, b, c, d string }   sig: SSSS
			//   [4]string                      sig: [4S]
			// That's ok, just a tad inefficient.
			//
			// The generated loops are kind of inefficient as well,
			// so unroll the loop a bit.
			const unrollSize = 32 // make loop body compare around this many bytes
			if et.Size() == 0 {
				break // zero-size elements need no comparison
			}
			unroll := max(1, unrollSize/et.Size())
			// Do partial loops directly.
			for n%unroll != 0 {
				e.build(et)
				n--
			}
			if n == 0 {
				break
			}
			// If we only have one loop left, do it directly.
			if n == unroll {
				for range n {
					e.build(et)
				}
				break
			}
			e.flush(false)
			e.r.WriteString(fmt.Sprintf("%c%d", sigArrayStart, n/unroll))
			for range unroll {
				e.build(et)
			}
			e.flush(false)
			e.r.WriteByte(sigArrayEnd)
		}
	default:
		base.Fatalf("eqSigBuilder %v", t)
	}
}

// Parse and remove the number at the start of s.
func parseNum(s string) (int64, string) {
	n := 0
	for n < len(s) && s[n] >= '0' && s[n] <= '9' {
		n++
	}
	x, err := strconv.ParseInt(s[:n], 10, 64)
	if err != nil {
		base.Fatalf("bad integer: %s", s[:n])
	}
	return x, s[n:]
}

// parseArray parses "%d%s]" from the front of a signature.
// Returns the repeat count (the %d), the element signature
// (the %s), and any remaining signature after the closing ']'.
func parseArray(sig string) (int64, string, string) {
	var n int64
	n, sig = parseNum(sig)
	// Find matching closing brace.
	i := 0
	depth := 1
	for {
		if i == len(sig) {
			base.Fatalf("mismatched brackets in %s", sig)
		}
		switch sig[i] {
		case sigArrayStart:
			depth++
		case sigArrayEnd:
			depth--
			if depth == 0 {
				return n, sig[:i], sig[i+1:]
			}
		}
		i++
	}
}

// sigSize returns the size of the type described by the signature.
func sigSize(sig string) int64 {
	var size int64
	for len(sig) > 0 {
		kind := sig[0]
		sig = sig[1:]
		switch kind {
		case sigMemory, sigSkip:
			var n int64
			n, sig = parseNum(sig)
			size += n
		case sigFloat32:
			size += 4
		case sigFloat64:
			size += 8
		case sigString, sigIface, sigEface:
			size += 2 * int64(types.PtrSize)
		case sigArrayStart:
			var n int64
			var elemSig string
			n, elemSig, sig = parseArray(sig)
			size += n * sigSize(elemSig)
		}
	}
	return size
}

func sigTrimSkip(s string) string {
	i := strings.LastIndexByte(s, sigSkip)
	if i < 0 {
		return s
	}
	for j := i + 1; j < len(s); j++ {
		if s[j] < '0' || s[j] > '9' {
			return s
		}
	}
	return s[:i]
}
