// genBuiltinCall generates contraints for a call to a built-in. func (a *analysis) genBuiltinCall(instr ssa.CallInstruction, cgn *cgnode) { call := instr.Common() switch call.Value.(*ssa.Builtin).Name() { case "append": // Safe cast: append cannot appear in a go or defer statement. 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": // In the tests, the probe might be the sole reference // to its arg, so make sure we create nodes for it. 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: // No-ops: close len cap real imag complex print println delete. } }
// genCall generates constraints for call instruction instr. func (a *analysis) genCall(caller *cgnode, instr ssa.CallInstruction) { call := instr.Common() // Intrinsic implementations of built-in functions. if _, ok := call.Value.(*ssa.Builtin); ok { a.genBuiltinCall(instr, caller) return } var result nodeid if v := instr.Value(); v != nil { result = a.valueNode(v) } site := &callsite{instr: instr} if call.StaticCallee() != nil { a.genStaticCall(caller, site, call, result) } else if call.IsInvoke() { a.genInvoke(caller, site, call, result) } else { a.genDynamicCall(caller, site, call, result) } caller.sites = append(caller.sites, site) if a.log != nil { // TODO(adonovan): debug: improve log message. fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller) } }
// callInstruction translates function call instructions. func (fr *frame) callInstruction(instr ssa.CallInstruction) []*govalue { call := instr.Common() if builtin, ok := call.Value.(*ssa.Builtin); ok { var typ types.Type if v := instr.Value(); v != nil { typ = v.Type() } return fr.callBuiltin(typ, builtin, call.Args) } args := make([]*govalue, len(call.Args)) for i, arg := range call.Args { args[i] = fr.value(arg) } var fn *govalue var chain llvm.Value if call.IsInvoke() { var recv *govalue fn, recv = fr.interfaceMethod(fr.llvmvalue(call.Value), call.Value.Type(), call.Method) args = append([]*govalue{recv}, args...) } else { if ssafn, ok := call.Value.(*ssa.Function); ok { llfn := fr.resolveFunctionGlobal(ssafn) llfn = llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0)) fn = newValue(llfn, ssafn.Type()) } else { // First-class function values are stored as *{*fnptr}, so // we must extract the function pointer. We must also // set the chain, in case the function is a closure. fn = fr.value(call.Value) chain = fn.value fnptr := fr.builder.CreateBitCast(fn.value, llvm.PointerType(fn.value.Type(), 0), "") fnptr = fr.builder.CreateLoad(fnptr, "") fn = newValue(fnptr, fn.Type()) } if recv := call.Signature().Recv(); recv != nil { if _, ok := recv.Type().Underlying().(*types.Pointer); !ok { recvalloca := fr.allocaBuilder.CreateAlloca(args[0].value.Type(), "") fr.builder.CreateStore(args[0].value, recvalloca) args[0] = newValue(recvalloca, types.NewPointer(args[0].Type())) } } } return fr.createCall(fn, chain, args) }