Exemplo n.º 1
0
// 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))
}
Exemplo n.º 2
0
func (l langType) DeclareTempVar(v ssa.Value) string {
	typ := l.LangType(v.Type().Underlying(), false, "temp var declaration")
	if typ == "" {
		return ""
	}
	return "var _" + v.Name() + ":" + typ + ";"
}
Exemplo n.º 3
0
Arquivo: api.go Projeto: 4honor/obdi
// 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{}{}
}
Exemplo n.º 4
0
Arquivo: api.go Projeto: 4honor/obdi
// 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{}{}
}
Exemplo n.º 5
0
// 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)
	}
}
Exemplo n.º 6
0
Arquivo: debug.go Projeto: minux/llgo
// declare creates an llvm.dbg.declare call for the specified function
// parameter or local variable.
func (d *debugInfo) declare(b llvm.Builder, v ssa.Value, llv llvm.Value, paramIndex int) {
	tag := tagAutoVariable
	if paramIndex >= 0 {
		tag = tagArgVariable
	}
	ld := debug.NewLocalVariableDescriptor(tag)
	ld.Argument = uint32(paramIndex + 1)
	ld.Name = llv.Name()
	if file := d.Fset.File(v.Pos()); file != nil {
		ld.Line = uint32(file.Position(v.Pos()).Line)
		ld.File = &d.getCompileUnit(file).Path
	}
	ld.Type = d.TypeDebugDescriptor(deref(v.Type()))
	ld.Context = d.context()
	b.InsertDeclare(d.module, llvm.MDNode([]llvm.Value{llv}), d.MDNode(ld))
}
Exemplo n.º 7
0
// 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
}
Exemplo n.º 8
0
// runPTA runs the pointer analysis of the selected SSA value or address.
func runPTA(o *Oracle, v ssa.Value, isAddr bool) (ptrs []pointerResult, err error) {
	buildSSA(o)

	if isAddr {
		o.ptaConfig.AddIndirectQuery(v)
	} else {
		o.ptaConfig.AddQuery(v)
	}
	ptares := ptrAnalysis(o)

	var ptr pointer.Pointer
	if isAddr {
		ptr = ptares.IndirectQueries[v]
	} else {
		ptr = ptares.Queries[v]
	}
	if ptr == (pointer.Pointer{}) {
		return nil, fmt.Errorf("pointer analysis did not find expression (dead code?)")
	}
	pts := ptr.PointsTo()

	if pointer.CanHaveDynamicTypes(v.Type()) {
		// Show concrete types for interface/reflect.Value expression.
		if concs := pts.DynamicTypes(); concs.Len() > 0 {
			concs.Iterate(func(conc types.Type, pta interface{}) {
				labels := pta.(pointer.PointsToSet).Labels()
				sort.Sort(byPosAndString(labels)) // to ensure determinism
				ptrs = append(ptrs, pointerResult{conc, labels})
			})
		}
	} else {
		// Show labels for other expressions.
		labels := pts.Labels()
		sort.Sort(byPosAndString(labels)) // to ensure determinism
		ptrs = append(ptrs, pointerResult{v.Type(), labels})
	}
	sort.Sort(byTypeString(ptrs)) // to ensure determinism
	return ptrs, nil
}
Exemplo n.º 9
0
// 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))
	}
}
Exemplo n.º 10
0
func (ctxt *context) getErrorInfo(v ssa.Value, member int, enclosingPos token.Pos, seen map[ssa.Value]bool) (result *errorInfo) {
	if !enclosingPos.IsValid() {
		panicf("getErrorInfo with invalid pos; %T %s", v, v)
	}
	//	log.Printf("getErrorInfo[%d] %T %v {", member, v, v)
	//	defer func() {
	//		log.Printf("} -> %+v", result)
	//	}()

	if seen[v] {
		return &errorInfo{}
	}
	seen[v] = true
	defer delete(seen, v)
	if pos := v.Pos(); pos.IsValid() {
		enclosingPos = pos
	}
	terminate := func() []errorTermination {
		return []errorTermination{{
			val: v,
			pos: enclosingPos,
		}}
	}
	switch v := v.(type) {
	case *ssa.Call:
		if member > 0 && member != v.Type().(*types.Tuple).Len()-1 {
			log.Printf("error from non-final member of function")
			return &errorInfo{unknown: terminate()}
		}
		return &errorInfo{nested: []*ssa.Call{v}}
	case *ssa.ChangeInterface:
		return ctxt.getErrorInfo(v.X, 0, enclosingPos, seen)
	case *ssa.Extract:
		return ctxt.getErrorInfo(v.Tuple, v.Index, enclosingPos, seen)
	case *ssa.Field:
		return &errorInfo{unknown: terminate()}
	case *ssa.Index:
		return &errorInfo{unknown: terminate()}
	case *ssa.Lookup:
		return &errorInfo{unknown: terminate()}
	case *ssa.Const:
		if v.Value != nil {
			panicf("non-nil constant cannot make error, surely?")
		}
		return &errorInfo{}
	case *ssa.MakeInterface:
		// TODO look into components of v.X
		return &errorInfo{nonNil: terminate()}
	case *ssa.Next:
		return &errorInfo{unknown: terminate()}
	case *ssa.Parameter:
		return &errorInfo{unknown: terminate()}
	case *ssa.Phi:
		var info errorInfo
		for _, edge := range v.Edges {
			info.add(ctxt.getErrorInfo(edge, member, enclosingPos, seen))
		}
		return &info
	case *ssa.Select:
		return &errorInfo{unknown: terminate()}
	case *ssa.TypeAssert:
		if v.CommaOk {
			return &errorInfo{unknown: terminate()}
		}
		return ctxt.getErrorInfo(v.X, 0, enclosingPos, seen)
	case *ssa.UnOp:
		switch v.Op {
		case token.ARROW:
			return &errorInfo{unknown: terminate()}
		case token.MUL:
			if _, isGlobal := v.X.(*ssa.Global); isGlobal {
				// Assume that if we're returning a global variable, it's a
				// global non-nil error, such as os.ErrInvalid.
				return &errorInfo{nonNil: terminate()}
			}
			return &errorInfo{unknown: terminate()}
		default:
			panicf("unexpected unary operator %s at %s", v, ctxt.lprog.Fset.Position(enclosingPos))
		}
	}
	panicf("unexpected value found for error: %T; %v", v, v)
	panic("not reached")
}
Exemplo n.º 11
0
Arquivo: ssa.go Projeto: minux/llgo
func (fr *frame) value(v ssa.Value) (result *LLVMValue) {
	switch v := v.(type) {
	case nil:
		return nil
	case *ssa.Function:
		result, ok := fr.funcvals[v]
		if ok {
			return result
		}
		// fr.globals[v] has the function in raw pointer form;
		// we must convert it to <f,ctx> form. If the function
		// does not have a receiver, then create a wrapper
		// function that has an additional "context" parameter.
		f := fr.resolveFunction(v)
		if v.Signature.Recv() == nil && len(v.FreeVars) == 0 {
			f = contextFunction(fr.compiler, f)
		}
		pair := llvm.ConstNull(fr.llvmtypes.ToLLVM(f.Type()))
		fnptr := llvm.ConstBitCast(f.LLVMValue(), pair.Type().StructElementTypes()[0])
		pair = llvm.ConstInsertValue(pair, fnptr, []uint32{0})
		result = fr.NewValue(pair, f.Type())
		fr.funcvals[v] = result
		return result
	case *ssa.Const:
		return fr.NewConstValue(v.Value, v.Type())
	case *ssa.Global:
		if g, ok := fr.globals[v]; ok {
			return g
		}
		// Create an external global. Globals for this package are defined
		// on entry to translatePackage, and have initialisers.
		llelemtyp := fr.llvmtypes.ToLLVM(deref(v.Type()))
		llglobal := llvm.AddGlobal(fr.module.Module, llelemtyp, v.String())
		global := fr.NewValue(llglobal, v.Type())
		fr.globals[v] = global
		return global
	}
	if value, ok := fr.env[v]; ok {
		return value
	}

	// Instructions are not necessarily visited before they are used (e.g. Phi
	// edges) so we must "backpatch": create a value with the resultant type,
	// and then replace it when we visit the instruction.
	if b, ok := fr.backpatch[v]; ok {
		return b
	}
	if fr.backpatch == nil {
		fr.backpatch = make(map[ssa.Value]*LLVMValue)
	}
	// Note: we must not create a constant here (e.g. Undef/ConstNull), as
	// it is not permissible to replace a constant with a non-constant.
	// We must create the value in its own standalone basic block, so we can
	// dispose of it after replacing.
	currBlock := fr.builder.GetInsertBlock()
	fr.builder.SetInsertPointAtEnd(llvm.AddBasicBlock(currBlock.Parent(), ""))
	placeholder := fr.compiler.builder.CreatePHI(fr.llvmtypes.ToLLVM(v.Type()), "")
	fr.builder.SetInsertPointAtEnd(currBlock)
	value := fr.NewValue(placeholder, v.Type())
	fr.backpatch[v] = value
	return value
}
Exemplo n.º 12
0
// 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
}
Exemplo n.º 13
0
// 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:
				// The only pointer-like Consts are nil.

			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
}