// valueOffsetNode ascertains the node for tuple/struct value v, // then returns the node for its subfield #index. // 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)) }
// setValueNode associates node id with the value v. // TODO(adonovan): disambiguate v by its CallGraphNode, if it's a local. func (a *analysis) setValueNode(v ssa.Value, id nodeid) { a.valNode[v] = id if a.log != nil { fmt.Fprintf(a.log, "\tval[%s] = n%d (%T)\n", v.Name(), id, v) } // Record the (v, id) relation if the client has queried v. if indirect, ok := a.config.Queries[v]; ok { if indirect { tmp := a.addNodes(v.Type(), "query.indirect") a.load(tmp, id, a.sizeof(v.Type())) id = tmp } a.queries[v] = append(a.queries[v], ptr{a, id}) } }
func (fr *frame) get(key ssa.Value) value { switch key := key.(type) { case nil: // Hack; simplifies handling of optional attributes // such as ssa.Slice.{Low,High}. return nil case *ssa.Function, *ssa.Builtin: return key case *ssa.Const: return constValue(key) case *ssa.Global: if r, ok := fr.i.globals[key]; ok { return r } } if r, ok := fr.env[key]; ok { return r } panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name())) }
// setValueNode associates node id with the value v. // cgn identifies the context iff v is a local variable. // 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) } // Record the (v, id) relation if the client has queried v. if indirect, ok := a.config.Queries[v]; ok { if indirect { tmp := a.addNodes(v.Type(), "query.indirect") a.genLoad(cgn, tmp, v, 0, a.sizeof(v.Type())) id = tmp } a.result.Queries[v] = append(a.result.Queries[v], ptr{a, cgn, id}) } }
// objectNode returns the object to which v points, if known. // In other words, if the points-to set of v is a singleton, it // returns the sole label, zero otherwise. // // We exploit this information to make the generated constraints less // dynamic. For example, a complex load constraint can be replaced by // a simple copy constraint when the sole destination is known a priori. // // Some SSA instructions always have singletons points-to sets: // Alloc, Function, Global, MakeChan, MakeClosure, MakeInterface, MakeMap, MakeSlice. // Others may be singletons depending on their operands: // Capture, Const, Convert, FieldAddr, IndexAddr, Slice. // // Idempotent. Objects are created as needed, possibly via recursion // down the SSA value graph, e.g IndexAddr(FieldAddr(Alloc))). // func (a *analysis) objectNode(cgn *cgnode, v ssa.Value) nodeid { if cgn == nil { // Global object. obj, ok := a.globalobj[v] if !ok { switch v := v.(type) { case *ssa.Global: obj = a.nextNode() a.addNodes(mustDeref(v.Type()), "global") a.endObject(obj, nil, v) case *ssa.Function: obj = a.makeFunctionObject(v, nil) case *ssa.Const: if t, ok := v.Type().Underlying().(*types.Slice); ok && !v.IsNil() { // Non-nil []byte or []rune constant. obj = a.nextNode() a.addNodes(sliceToArray(t), "array in slice constant") a.endObject(obj, nil, v) } case *ssa.Capture: // For now, Captures have the same cardinality as globals. // TODO(adonovan): treat captures context-sensitively. } if a.log != nil { fmt.Fprintf(a.log, "\tglobalobj[%s] = n%d\n", v, obj) } a.globalobj[v] = obj } return obj } // Local object. obj, ok := a.localobj[v] if !ok { switch v := v.(type) { case *ssa.Alloc: obj = a.nextNode() a.addNodes(mustDeref(v.Type()), "alloc") a.endObject(obj, cgn, v) case *ssa.MakeSlice: obj = a.nextNode() a.addNodes(sliceToArray(v.Type()), "makeslice") a.endObject(obj, cgn, v) case *ssa.MakeChan: obj = a.nextNode() a.addNodes(v.Type().Underlying().(*types.Chan).Elem(), "makechan") a.endObject(obj, cgn, v) case *ssa.MakeMap: obj = a.nextNode() tmap := v.Type().Underlying().(*types.Map) a.addNodes(tmap.Key(), "makemap.key") a.addNodes(tmap.Elem(), "makemap.value") a.endObject(obj, cgn, v) case *ssa.MakeInterface: tConc := v.X.Type() obj = a.makeTagged(tConc, cgn, v) // Copy the value into it, if nontrivial. if x := a.valueNode(v.X); x != 0 { a.copy(obj+1, x, a.sizeof(tConc)) } case *ssa.FieldAddr: if xobj := a.objectNode(cgn, v.X); xobj != 0 { obj = xobj + nodeid(a.offsetOf(mustDeref(v.X.Type()), v.Field)) } case *ssa.IndexAddr: if xobj := a.objectNode(cgn, v.X); xobj != 0 { obj = xobj + 1 } case *ssa.Slice: obj = a.objectNode(cgn, v.X) case *ssa.Convert: // TODO(adonovan): opt: handle these cases too: // - unsafe.Pointer->*T conversion acts like Alloc // - string->[]byte/[]rune conversion acts like MakeSlice } if a.log != nil { fmt.Fprintf(a.log, "\tlocalobj[%s] = n%d\n", v.Name(), obj) } a.localobj[v] = obj } return obj }