// 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)) }
func (fr *frame) value(v ssa.Value) (result *govalue) { switch v := v.(type) { case nil: return nil case *ssa.Function: return fr.resolveFunctionDescriptor(v) case *ssa.Const: return fr.newValueFromConst(v.Value, v.Type()) case *ssa.Global: if g, ok := fr.globals[v]; ok { return newValue(g, v.Type()) } // Create an external global. Globals for this package are defined // on entry to translatePackage, and have initialisers. llelemtyp := fr.llvmtypes.ToLLVM(deref(v.Type())) vname := fr.types.mc.mangleGlobalName(v) llglobal := llvm.AddGlobal(fr.module.Module, llelemtyp, vname) llglobal = llvm.ConstBitCast(llglobal, fr.llvmtypes.ToLLVM(v.Type())) fr.globals[v] = llglobal return newValue(llglobal, v.Type()) } if value, ok := fr.env[v]; ok { return value } panic(fmt.Errorf("Instruction %q not visited yet", v.Name())) }
// genOffsetAddr generates constraints for a 'v=ptr.field' (FieldAddr) // or 'v=ptr[*]' (IndexAddr) instruction v. func (a *analysis) genOffsetAddr(cgn *cgnode, v ssa.Value, ptr nodeid, offset uint32) { dst := a.valueNode(v) if obj := a.objectNode(cgn, v); obj != 0 { // Pre-apply offsetAddrConstraint.solve(). a.addressOf(v.Type(), dst, obj) } else { a.offsetAddr(v.Type(), dst, ptr, offset) } }
// AddQuery adds v to Config.IndirectQueries. // Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()). func (c *Config) AddIndirectQuery(v ssa.Value) { if c.IndirectQueries == nil { c.IndirectQueries = make(map[ssa.Value]struct{}) } if !CanPoint(mustDeref(v.Type())) { panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type())) } c.IndirectQueries[v] = struct{}{} }
// AddQuery adds v to Config.Queries. // Precondition: CanPoint(v.Type()). // TODO(adonovan): consider returning a new Pointer for this query, // which will be initialized during analysis. That avoids the needs // for the corresponding ssa.Value-keyed maps in Config and Result. func (c *Config) AddQuery(v ssa.Value) { if !CanPoint(v.Type()) { panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type())) } if c.Queries == nil { c.Queries = make(map[ssa.Value]struct{}) } c.Queries[v] = struct{}{} }
// valueNode returns the id of the value node for v, creating it (and // the association) as needed. It may return zero for uninteresting // values containing no pointers. // func (a *analysis) valueNode(v ssa.Value) nodeid { // Value nodes for locals are created en masse by genFunc. if id, ok := a.localval[v]; ok { return id } // Value nodes for globals are created on demand. 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 }
// Declare creates an llvm.dbg.declare call for the specified function // parameter or local variable. func (d *DIBuilder) Declare(b llvm.Builder, v ssa.Value, llv llvm.Value, paramIndex int) { tag := tagAutoVariable if paramIndex >= 0 { tag = tagArgVariable } var diFile llvm.Metadata var line int if file := d.fset.File(v.Pos()); file != nil { line = file.Line(v.Pos()) diFile = d.getFile(file) } localVar := d.builder.CreateLocalVariable(d.scope(), llvm.DILocalVariable{ Tag: tag, Name: llv.Name(), File: diFile, Line: line, ArgNo: paramIndex + 1, Type: d.DIType(v.Type()), }) expr := d.builder.CreateExpression(nil) d.builder.InsertDeclareAtEnd(llv, localVar, expr, b.GetInsertBlock()) }
// 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) } // Due to context-sensitivity, we may encounter the same Value // in many contexts. We merge them to a canonical node, since // that's what all clients want. // Record the (v, id) relation if the client has queried pts(v). if _, ok := a.config.Queries[v]; ok { t := v.Type() ptr, ok := a.result.Queries[v] if !ok { // First time? Create the canonical query node. 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)) } // Record the (*v, id) relation if the client has queried pts(*v). if _, ok := a.config.IndirectQueries[v]; ok { t := v.Type() ptr, ok := a.result.IndirectQueries[v] if !ok { // First time? Create the canonical indirect query node. ptr = Pointer{a, a.addNodes(v.Type(), "query.indirect")} a.result.IndirectQueries[v] = ptr } a.genLoad(cgn, ptr.n, v, 0, a.sizeof(t)) } }
// 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: // FreeVar, 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 { switch v.(type) { case *ssa.Global, *ssa.Function, *ssa.Const, *ssa.FreeVar: // 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: // not addressable case *ssa.FreeVar: // not addressable } 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") elem := a.addNodes(tmap.Elem(), "makemap.value") // To update the value field, MapUpdate // generates store-with-offset constraints which // the presolver can't model, so we must mark // those nodes indirect. for id, end := elem, elem+nodeid(a.sizeof(tmap.Elem())); id < end; id++ { a.mapValues = append(a.mapValues, id) } 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 }