// genCall generates contraints 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 { fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller) } }
// genCall generates contraints 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) } // The node whose pts(ยท) will contain all targets of the call. var targets nodeid switch { case call.StaticCallee() != nil: targets = a.genStaticCall(call, result) case call.IsInvoke(): targets = a.genInvoke(call, result) default: targets = a.genDynamicCall(call, result) } site := &callsite{ targets: targets, instr: instr, } caller.sites = append(caller.sites, site) if a.log != nil { fmt.Fprintf(a.log, "\t%s to targets %s from %s\n", site, site.targets, caller) } }
// 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).Object().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": // Analytically print is a no-op, but it's a convenient hook // for testing the pts of an expression, so we notify the client. // Existing uses in Go core libraries are few and harmless. if Print := a.config.Print; Print != nil { // Due to context-sensitivity, we may encounter // the same print() call in many contexts, so // we merge them to a canonical node. probe := a.probes[call] t := call.Args[0].Type() // First time? Create the canonical probe node. if probe == 0 { probe = a.addNodes(t, "print") a.probes[call] = probe Print(call, ptr{a, nil, probe}) // notify client } a.copy(probe, a.valueNode(call.Args[0]), a.sizeof(t)) } default: // No-ops: close len cap real imag complex println delete. } }
func findCallees(o *Oracle, site ssa.CallInstruction) ([]*ssa.Function, error) { // Avoid running the pointer analysis for static calls. if callee := site.Common().StaticCallee(); callee != nil { switch callee.String() { case "runtime.SetFinalizer", "(reflect.Value).Call": // The PTA treats calls to these intrinsics as dynamic. // TODO(adonovan): avoid reliance on PTA internals. default: return []*ssa.Function{callee}, nil // singleton } } // Dynamic call: use pointer analysis. o.ptaConfig.BuildCallGraph = true callgraph := ptrAnalysis(o).CallGraph // Find all call edges from the site. calleesMap := make(map[*ssa.Function]bool) var foundCGNode bool for _, n := range callgraph.Nodes() { if n.Func() == site.Parent() { foundCGNode = true for _, edge := range n.Edges() { if edge.Site == site { calleesMap[edge.Callee.Func()] = true } } } } if !foundCGNode { return nil, fmt.Errorf("this call site is unreachable in this analysis") } // Discard context, de-duplicate and sort. funcs := make([]*ssa.Function, 0, len(calleesMap)) for f := range calleesMap { funcs = append(funcs, f) } sort.Sort(byFuncPos(funcs)) return funcs, nil }
// prepareCall returns the evaluated function and arguments. // // For builtins that may not be used in go/defer, prepareCall // will emits inline code. In this case, prepareCall returns // nil for fn and args, and returns a non-nil value for result. func (fr *frame) prepareCall(instr ssa.CallInstruction) (fn *LLVMValue, args []*LLVMValue, result *LLVMValue) { call := instr.Common() args = make([]*LLVMValue, len(call.Args)) for i, arg := range call.Args { args[i] = fr.value(arg) } if call.IsInvoke() { fn := fr.interfaceMethod(fr.value(call.Value), call.Method) return fn, args, nil } switch v := call.Value.(type) { case *ssa.Builtin: // handled below case *ssa.Function: // Function handled specially; value() will convert // a function to one with a context argument. fn = fr.resolveFunction(v) pair := llvm.ConstNull(fr.llvmtypes.ToLLVM(fn.Type())) pair = llvm.ConstInsertValue(pair, fn.LLVMValue(), []uint32{0}) fn = fr.NewValue(pair, fn.Type()) return fn, args, nil default: fn = fr.value(call.Value) return fn, args, nil } // Builtins may only be used in calls (i.e. can't be assigned), // and only print[ln], panic and recover may be used in go/defer. builtin := call.Value.(*ssa.Builtin) switch builtin.Name() { case "print", "println": // print/println generates a call-site specific anonymous // function to print the values. It's not inline because // print/println may be deferred. params := make([]*types.Var, len(call.Args)) for i, arg := range call.Args { // make sure to use args[i].Type(), not call.Args[i].Type(), // as the evaluated expression converts untyped. params[i] = types.NewParam(arg.Pos(), nil, arg.Name(), args[i].Type()) } sig := types.NewSignature(nil, nil, types.NewTuple(params...), nil, false) llfntyp := fr.llvmtypes.ToLLVM(sig) llfnptr := llvm.AddFunction(fr.module.Module, "", llfntyp.StructElementTypes()[0].ElementType()) currBlock := fr.builder.GetInsertBlock() entry := llvm.AddBasicBlock(llfnptr, "entry") fr.builder.SetInsertPointAtEnd(entry) internalArgs := make([]Value, len(args)) for i, arg := range args { internalArgs[i] = fr.NewValue(llfnptr.Param(i), arg.Type()) } fr.printValues(builtin.Name() == "println", internalArgs...) fr.builder.CreateRetVoid() fr.builder.SetInsertPointAtEnd(currBlock) return fr.NewValue(llfnptr, sig), args, nil case "panic": panic("TODO: panic") case "recover": // TODO(axw) determine number of frames to skip in pc check indirect := fr.NewValue(llvm.ConstNull(llvm.Int32Type()), types.Typ[types.Int32]) return fr.runtime.recover_, []*LLVMValue{indirect}, nil case "append": return nil, nil, fr.callAppend(args[0], args[1]) case "close": return fr.runtime.chanclose, args, nil case "cap": return nil, nil, fr.callCap(args[0]) case "len": return nil, nil, fr.callLen(args[0]) case "copy": return nil, nil, fr.callCopy(args[0], args[1]) case "delete": fr.callDelete(args[0], args[1]) return nil, nil, nil case "real": return nil, nil, args[0].extractComplexComponent(0) case "imag": return nil, nil, args[0].extractComplexComponent(1) case "complex": r := args[0].LLVMValue() i := args[1].LLVMValue() typ := instr.Value().Type() cmplx := llvm.Undef(fr.llvmtypes.ToLLVM(typ)) cmplx = fr.builder.CreateInsertValue(cmplx, r, 0, "") cmplx = fr.builder.CreateInsertValue(cmplx, i, 1, "") return nil, nil, fr.NewValue(cmplx, typ) default: panic("unimplemented: " + builtin.Name()) } }