func (a *analysis) genDynamicCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
	site.targets = a.valueNode(call.Value)
	sig := call.Signature()
	var offset uint32 = 1  
	for i, arg := range call.Args {
		sz := a.sizeof(sig.Params().At(i).Type())
		a.genStore(caller, call.Value, a.valueNode(arg), offset, sz)
		offset += sz
	}
	if result != 0 {
		a.genLoad(caller, result, call.Value, offset, a.sizeof(sig.Results()))
	}
}
genDynamicCall generates constraints for a dynamic function call.
package pointer
import (
	"fmt"
	"go/token"
	"go/types"
	"golang.org/x/tools/go/callgraph"
	"golang.org/x/tools/go/ssa"
)
var (
	tEface     = types.NewInterface(nil, nil).Complete()
	tInvalid   = types.Typ[types.Invalid]
	tUnsafePtr = types.Typ[types.UnsafePointer]
)
func (a *analysis) nextNode() nodeid {
	return nodeid(len(a.nodes))
}
func (a *analysis) addNodes(typ types.Type, comment string) nodeid {
	id := a.nextNode()
	for _, fi := range a.flatten(typ) {
		a.addOneNode(fi.typ, comment, fi)
	}
	if id == a.nextNode() {
		return 0  
	}
	return id
}
func (a *analysis) addOneNode(typ types.Type, comment string, subelement *fieldInfo) nodeid {
	id := a.nextNode()
	a.nodes = append(a.nodes, &node{typ: typ, subelement: subelement, solve: new(solverState)})
	if a.log != nil {
		fmt.Fprintf(a.log, "\tcreate n%d %s for %s%s\n",
			id, typ, comment, subelement.path())
	}
	return id
}
func (a *analysis) setValueNode(v ssa.Value, id nodeid, cgn *cgnode) {
	if cgn != nil {
		a.localval[v] = id
	} else {
		a.globalval[v] = id
	}
	if a.log != nil {
		fmt.Fprintf(a.log, "\tval[%s] = n%d  (%T)\n", v.Name(), id, v)
	}
	if _, ok := a.config.Queries[v]; ok {
		t := v.Type()
		ptr, ok := a.result.Queries[v]
		if !ok {
			ptr = Pointer{a, a.addNodes(t, "query")}
			a.result.Queries[v] = ptr
		}
		a.result.Queries[v] = ptr
		a.copy(ptr.n, id, a.sizeof(t))
	}
	if _, ok := a.config.IndirectQueries[v]; ok {
		t := v.Type()
		ptr, ok := a.result.IndirectQueries[v]
		if !ok {
			ptr = Pointer{a, a.addNodes(v.Type(), "query.indirect")}
			a.result.IndirectQueries[v] = ptr
		}
		a.genLoad(cgn, ptr.n, v, 0, a.sizeof(t))
	}
	for _, query := range a.config.extendedQueries[v] {
		t, nid := a.evalExtendedQuery(v.Type().Underlying(), id, query.ops)
		if query.ptr.a == nil {
			query.ptr.a = a
			query.ptr.n = a.addNodes(t, "query.extended")
		}
		a.copy(query.ptr.n, nid, a.sizeof(t))
	}
}
func (a *analysis) endObject(obj nodeid, cgn *cgnode, data interface{}) *object {
	size := uint32(a.nextNode() - obj)
	if size == 0 {
		a.addOneNode(tInvalid, "padding", nil)
	}
	objNode := a.nodes[obj]
	o := &object{
		size: size,  
		cgn:  cgn,
		data: data,
	}
	objNode.obj = o
	return o
}
func (a *analysis) makeFunctionObject(fn *ssa.Function, callersite *callsite) nodeid {
	if a.log != nil {
		fmt.Fprintf(a.log, "\t---- makeFunctionObject %s\n", fn)
	}
	obj := a.nextNode()
	cgn := a.makeCGNode(fn, obj, callersite)
	sig := fn.Signature
	a.addOneNode(sig, "func.cgnode", nil)  
	if recv := sig.Recv(); recv != nil {
		a.addNodes(recv.Type(), "func.recv")
	}
	a.addNodes(sig.Params(), "func.params")
	a.addNodes(sig.Results(), "func.results")
	a.endObject(obj, cgn, fn).flags |= otFunction
	if a.log != nil {
		fmt.Fprintf(a.log, "\t----\n")
	}
	a.genq = append(a.genq, cgn)
	return obj
}
func (a *analysis) makeTagged(typ types.Type, cgn *cgnode, data interface{}) nodeid {
	obj := a.addOneNode(typ, "tagged.T", nil)  
	a.addNodes(typ, "tagged.v")
	a.endObject(obj, cgn, data).flags |= otTagged
	return obj
}
func (a *analysis) makeRtype(T types.Type) nodeid {
	if v := a.rtypes.At(T); v != nil {
		return v.(nodeid)
	}
	obj := a.nextNode()
	a.addOneNode(T, "reflect.rtype", nil)
	a.endObject(obj, nil, T)
	id := a.makeTagged(a.reflectRtypePtr, nil, T)
	a.nodes[id+1].typ = T  
	a.addressOf(a.reflectRtypePtr, id+1, obj)
	a.rtypes.Set(T, id)
	return id
}
func (a *analysis) rtypeTaggedValue(obj nodeid) types.Type {
	tDyn, t, _ := a.taggedValue(obj)
	if tDyn != a.reflectRtypePtr {
		panic(fmt.Sprintf("not a *reflect.rtype-tagged object: obj=n%d tag=%v payload=n%d", obj, tDyn, t))
	}
	return a.nodes[t].typ
}
func (a *analysis) valueNode(v ssa.Value) nodeid {
	if id, ok := a.localval[v]; ok {
		return id
	}
	id, ok := a.globalval[v]
	if !ok {
		var comment string
		if a.log != nil {
			comment = v.String()
		}
		id = a.addNodes(v.Type(), comment)
		if obj := a.objectNode(nil, v); obj != 0 {
			a.addressOf(v.Type(), id, obj)
		}
		a.setValueNode(v, id, nil)
	}
	return id
}
func (a *analysis) valueOffsetNode(v ssa.Value, index int) nodeid {
	id := a.valueNode(v)
	if id == 0 {
		panic(fmt.Sprintf("cannot offset within n0: %s = %s", v.Name(), v))
	}
	return id + nodeid(a.offsetOf(v.Type(), index))
}
func (a *analysis) isTaggedObject(obj nodeid) bool {
	return a.nodes[obj].obj.flags&otTagged != 0
}
func (a *analysis) taggedValue(obj nodeid) (tDyn types.Type, v nodeid, indirect bool) {
	n := a.nodes[obj]
	flags := n.obj.flags
	if flags&otTagged == 0 {
		panic(fmt.Sprintf("not a tagged object: n%d", obj))
	}
	return n.typ, obj + 1, flags&otIndirect != 0
}
func (a *analysis) funcParams(id nodeid) nodeid {
	n := a.nodes[id]
	if n.obj == nil || n.obj.flags&otFunction == 0 {
		panic(fmt.Sprintf("funcParams(n%d): not a function object block", id))
	}
	return id + 1
}
func (a *analysis) funcResults(id nodeid) nodeid {
	n := a.nodes[id]
	if n.obj == nil || n.obj.flags&otFunction == 0 {
		panic(fmt.Sprintf("funcResults(n%d): not a function object block", id))
	}
	sig := n.typ.(*types.Signature)
	id += 1 + nodeid(a.sizeof(sig.Params()))
	if sig.Recv() != nil {
		id += nodeid(a.sizeof(sig.Recv().Type()))
	}
	return id
}
func (a *analysis) copy(dst, src nodeid, sizeof uint32) {
	if src == dst || sizeof == 0 {
		return  
	}
	if src == 0 || dst == 0 {
		panic(fmt.Sprintf("ill-typed copy dst=n%d src=n%d", dst, src))
	}
	for i := uint32(0); i < sizeof; i++ {
		a.addConstraint(&copyConstraint{dst, src})
		src++
		dst++
	}
}
func (a *analysis) addressOf(T types.Type, id, obj nodeid) {
	if id == 0 {
		panic("addressOf: zero id")
	}
	if obj == 0 {
		panic("addressOf: zero obj")
	}
	if a.shouldTrack(T) {
		a.addConstraint(&addrConstraint{id, obj})
	}
}
func (a *analysis) load(dst, src nodeid, offset, sizeof uint32) {
	if dst == 0 {
		return  
	}
	if src == 0 && dst == 0 {
		return  
	}
	if src == 0 || dst == 0 {
		panic(fmt.Sprintf("ill-typed load dst=n%d src=n%d", dst, src))
	}
	for i := uint32(0); i < sizeof; i++ {
		a.addConstraint(&loadConstraint{offset, dst, src})
		offset++
		dst++
	}
}
func (a *analysis) store(dst, src nodeid, offset uint32, sizeof uint32) {
	if src == 0 {
		return  
	}
	if src == 0 && dst == 0 {
		return  
	}
	if src == 0 || dst == 0 {
		panic(fmt.Sprintf("ill-typed store dst=n%d src=n%d", dst, src))
	}
	for i := uint32(0); i < sizeof; i++ {
		a.addConstraint(&storeConstraint{offset, dst, src})
		offset++
		src++
	}
}
func (a *analysis) offsetAddr(T types.Type, dst, src nodeid, offset uint32) {
	if !a.shouldTrack(T) {
		return
	}
	if offset == 0 {
		a.copy(dst, src, 1)
	} else {
		a.addConstraint(&offsetAddrConstraint{offset, dst, src})
	}
}
func (a *analysis) typeAssert(T types.Type, dst, src nodeid, exact bool) {
	if isInterface(T) {
		a.addConstraint(&typeFilterConstraint{T, dst, src})
	} else {
		a.addConstraint(&untagConstraint{T, dst, src, exact})
	}
}
func (a *analysis) addConstraint(c constraint) {
	a.constraints = append(a.constraints, c)
	if a.log != nil {
		fmt.Fprintf(a.log, "\t%s\n", c)
	}
}
func (a *analysis) copyElems(cgn *cgnode, typ types.Type, dst, src ssa.Value) {
	tmp := a.addNodes(typ, "copy")
	sz := a.sizeof(typ)
	a.genLoad(cgn, tmp, src, 1, sz)
	a.genStore(cgn, dst, tmp, 1, sz)
}
func (a *analysis) genConv(conv *ssa.Convert, cgn *cgnode) {
	res := a.valueNode(conv)
	if res == 0 {
		return  
	}
	tSrc := conv.X.Type()
	tDst := conv.Type()
	switch utSrc := tSrc.Underlying().(type) {
	case *types.Slice:
		return
	case *types.Pointer:
		if tDst.Underlying() == tUnsafePtr {
			return  
		}
	case *types.Basic:
		switch tDst.Underlying().(type) {
		case *types.Pointer:
			if utSrc == tUnsafePtr {
				obj := a.addNodes(mustDeref(tDst), "unsafe.Pointer conversion")
				a.endObject(obj, cgn, conv)
				a.addressOf(tDst, res, obj)
				return
			}
		case *types.Slice:
			if utSrc.Info()&types.IsString != 0 {
				obj := a.addNodes(sliceToArray(tDst), "convert")
				a.endObject(obj, cgn, conv)
				a.addressOf(tDst, res, obj)
				return
			}
		case *types.Basic:
			return
		}
	}
	panic(fmt.Sprintf("illegal *ssa.Convert %s -> %s: %s", tSrc, tDst, conv.Parent()))
}
func (a *analysis) genAppend(instr *ssa.Call, cgn *cgnode) {
	x := instr.Call.Args[0]
	z := instr
	a.copy(a.valueNode(z), a.valueNode(x), 1)  
	if len(instr.Call.Args) == 1 {
		return  
	}
	y := instr.Call.Args[1]
	tArray := sliceToArray(instr.Call.Args[0].Type())
	var w nodeid
	w = a.nextNode()
	a.addNodes(tArray, "append")
	a.endObject(w, cgn, instr)
	a.copyElems(cgn, tArray.Elem(), z, y)         
	a.addressOf(instr.Type(), a.valueNode(z), w)  
}
func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) {
	call := instr.Common()
	switch call.Value.(*ssa.Builtin).Name() {
	case "append":
		a.genAppend(instr.(*ssa.Call), cgn)
	case "copy":
		tElem := call.Args[0].Type().Underlying().(*types.Slice).Elem()
		a.copyElems(cgn, tElem, call.Args[0], call.Args[1])
	case "panic":
		a.copy(a.panicNode, a.valueNode(call.Args[0]), 1)
	case "recover":
		if v := instr.Value(); v != nil {
			a.copy(a.valueNode(v), a.panicNode, 1)
		}
	case "print":
		if len(call.Args) > 0 {
			a.valueNode(call.Args[0])
		}
	case "ssa:wrapnilchk":
		a.copy(a.valueNode(instr.Value()), a.valueNode(call.Args[0]), 1)
	default:
	}
}
func (a *analysis) shouldUseContext(fn *ssa.Function) bool {
	if a.findIntrinsic(fn) != nil {
		return true  
	}
	if len(fn.Blocks) != 1 {
		return false  
	}
	blk := fn.Blocks[0]
	if len(blk.Instrs) > 10 {
		return false  
	}
	if fn.Synthetic != "" && (fn.Pkg == nil || fn != fn.Pkg.Func("init")) {
		return true  
	}
	for _, instr := range blk.Instrs {
		switch instr := instr.(type) {
		case ssa.CallInstruction:
			if _, ok := instr.Common().Value.(*ssa.Builtin); !ok {
				return false
			}
		}
	}
	return true
}
func (a *analysis) genStaticCall(caller *cgnode, site *callsite, call *ssa.CallCommon, result nodeid) {
	fn := call.StaticCallee()
	switch fn {
	case a.runtimeSetFinalizer:
		site.targets = a.addOneNode(tInvalid, "SetFinalizer.targets", nil)
		a.addConstraint(&runtimeSetFinalizerConstraint{
			targets: site.targets,
			x:       a.valueNode(call.Args[0]),
			f:       a.valueNode(call.Args[1]),
		})
		return
	case a.reflectValueCall:
		dotdotdot := false
		ret := reflectCallImpl(a, caller, site, a.valueNode(call.Args[0]), a.valueNode(call.Args[1]), dotdotdot)
		if result != 0 {
			a.addressOf(fn.Signature.Results().At(0).Type(), result, ret)
		}
		return
	}
	var obj nodeid
	if a.shouldUseContext(fn) {
		obj = a.makeFunctionObject(fn, site)  
	} else {
		obj = a.objectNode(nil, fn)  
	}
	a.callEdge(caller, site, obj)
	sig := call.Signature()
	params := a.funcParams(obj)
	args := call.Args
	if sig.Recv() != nil {
		sz := a.sizeof(sig.Recv().Type())
		a.copy(params, a.valueNode(args[0]), sz)
		params += nodeid(sz)
		args = args[1:]
	}
	for i, arg := range args {
		sz := a.sizeof(sig.Params().At(i).Type())
		a.copy(params, a.valueNode(arg), sz)
		params += nodeid(sz)
	}
	if result != 0 {
		a.copy(result, a.funcResults(obj), a.sizeof(sig.Results()))
	}
}