Beispiel #1
0
// chanOps returns a slice of all the channel operations in the instruction.
// Derived from cmd/guru/peers.go.
func chanOps(instr ssa.Instruction) []chanOp {
	fn := instr.Parent()
	var ops []chanOp
	switch instr := instr.(type) {
	case *ssa.UnOp:
		if instr.Op == token.ARROW {
			// TODO(adonovan): don't assume <-ch; could be 'range ch'.
			ops = append(ops, chanOp{instr.X, "received", instr.Pos(), len("<-"), fn})
		}
	case *ssa.Send:
		ops = append(ops, chanOp{instr.Chan, "sent", instr.Pos(), len("<-"), fn})
	case *ssa.Select:
		for _, st := range instr.States {
			mode := "received"
			if st.Dir == types.SendOnly {
				mode = "sent"
			}
			ops = append(ops, chanOp{st.Chan, mode, st.Pos, len("<-"), fn})
		}
	case ssa.CallInstruction:
		call := instr.Common()
		if blt, ok := call.Value.(*ssa.Builtin); ok && blt.Name() == "close" {
			pos := instr.Common().Pos()
			ops = append(ops, chanOp{call.Args[0], "closed", pos - token.Pos(len("close")), len("close("), fn})
		}
	}
	return ops
}
Beispiel #2
0
func (f *Function) instrIdxInBlock(instr ssa.Instruction) int {
	block := instr.Block()
	for i := 0; i < len(block.Instrs); i++ {
		if block.Instrs[i] == instr {
			return i
		}
	}
	panic(ice("couldn't find instruction location in basic block"))
}
Beispiel #3
0
func aliveTest(ident *identifier, loc ssa.Instruction, after bool) bool {
	// assume all identifiers not local to a basic block are alive
	if !ident.isBlockLocal() {
		return true
	}
	if loc == nil {
		ice("invalid SSA instruction")
	}
	basicBlock := loc.Block()
	if basicBlock == nil {
		ice("cant get basic block for SSA instruction")
	}
	value := ident.ssaValue()
	if value == nil {
		// no ssa.Value, assume alive
		return true
	}
	start := false
	for _, i := range basicBlock.Instrs {
		if !after {
			if i == loc {
				start = true
			}
		}
		if start {
			ops := i.Operands(nil)
			for _, op := range ops {
				// instruction at or after loc uses ident as an operand
				if op != nil && *op == value {
					return true
				}
			}
		}
		if after {
			if i == loc {
				start = true
			}
		}
	}
	// no instruction at or after loc uses ident as an operand
	return false
}
Beispiel #4
0
func (f *Function) isAlive(loc ssa.Instruction, val ssa.Value) bool {
	block := loc.Block()
	instrIdx := f.instrIdxInBlock(loc)
	if val.Referrers() == nil {
		return false
	}

	for _, ref := range *val.Referrers() {
		bk := ref.Block()
		if bk.Index != block.Index {
			continue
		}
		for i, _ := range bk.Instrs {
			if i > instrIdx {
				return true
			}
		}
	}
	return false
}
Beispiel #5
0
// chanOps extract all channel operations from an instruction.
func chanOps(instr ssa.Instruction) []ChanOp {
	var ops []ChanOp
	switch instr := instr.(type) {
	case *ssa.Send:
		ops = append(ops, ChanOp{instr.Chan, ChanSend, instr.Pos()})
	case *ssa.UnOp:
		if instr.Op == token.ARROW {
			ops = append(ops, ChanOp{instr.X, ChanRecv, instr.Pos()})
		}
	case *ssa.Select:
		for _, st := range instr.States {
			switch st.Dir {
			case types.SendOnly:
				ops = append(ops, ChanOp{st.Chan, ChanSend, st.Pos})
			case types.RecvOnly:
				ops = append(ops, ChanOp{st.Chan, ChanRecv, st.Pos})
			}
		}
	case ssa.CallInstruction:
		common := instr.Common()
		if b, ok := common.Value.(*ssa.Builtin); ok && b.Name() == "close" {
			ops = append(ops, ChanOp{common.Args[0], ChanClose, common.Pos()})
		}
	}
	return ops
}
Beispiel #6
0
// chanOps returns a slice of all the channel operations in the instruction.
func chanOps(instr ssa.Instruction) []chanOp {
	// TODO(adonovan): handle calls to reflect.{Select,Recv,Send,Close} too.
	var ops []chanOp
	switch instr := instr.(type) {
	case *ssa.UnOp:
		if instr.Op == token.ARROW {
			ops = append(ops, chanOp{instr.X, types.RecvOnly, instr.Pos()})
		}
	case *ssa.Send:
		ops = append(ops, chanOp{instr.Chan, types.SendOnly, instr.Pos()})
	case *ssa.Select:
		for _, st := range instr.States {
			ops = append(ops, chanOp{st.Chan, st.Dir, st.Pos})
		}
	case ssa.CallInstruction:
		cc := instr.Common()
		if b, ok := cc.Value.(*ssa.Builtin); ok && b.Name() == "close" {
			ops = append(ops, chanOp{cc.Args[0], types.SendRecv, cc.Pos()})
		}
	}
	return ops
}
Beispiel #7
0
func ssaType(i ssa.Instruction, out *bytes.Buffer) {
	switch i := i.(type) {
	case *ssa.Alloc:
		out.WriteString("  *ssa.Alloc  ")
		out.WriteString("    Comment: " + i.Comment + " Heap: ")
		if i.Heap {
			out.WriteString("   true \n")
		} else {
			out.WriteString("   false \n")
		}
	case *ssa.BinOp:
		out.WriteString("  *ssa.BinOp   ")
		out.WriteString("    Op: " + i.Op.String() + " X: " + i.X.String() + " Y: " + i.Y.String() + "\n")
		/*				case *ssa.Builtin:
						out.WriteString("*ssa.Builtin  \n") */
	case *ssa.ChangeInterface:
		out.WriteString("  *ssa.ChangeInterface ")
		out.WriteString("   X: " + i.X.String() + "\n")
	case *ssa.ChangeType:
		out.WriteString("  *ssa.ChangeType ")
		out.WriteString("   X: " + i.X.String() + "\n")
		/*				case *ssa.Const:
						out.WriteString("*ssa.Const ")
						out.WriteString("Value: " + i.Value.String() + "\n") */
	case *ssa.Convert:
		out.WriteString("  ssa.Convert ")
		out.WriteString("   X:  " + i.X.String() + "\n")
	case *ssa.Extract:
		out.WriteString("  ssa.Extract")
		out.WriteString("   Tuple: " + i.Tuple.String() + " Index ")
		out.WriteString(strconv.Itoa(i.Index))
	case *ssa.Field:
		out.WriteString("  ssa.Field")
		out.WriteString("   X: " + i.X.String() + " Field " + strconv.Itoa(i.Field) + "\n")
	case *ssa.FieldAddr:
		out.WriteString("  ssa.FieldAddr")
		out.WriteString("    X: " + i.X.String() + " Field " + strconv.Itoa(i.Field) + "\n")
		/*				case *ssa.FreeVar:
						out.WriteString("ssa.FreeVar") */
		/*				case *ssa.Global:
						out.WriteString("*ssa.Global")
						out.WriteString("Pkg " + i.Pkg.String() + "\n") */
	case *ssa.Index:
		out.WriteString("  *ssa.Index ")
		out.WriteString("    X: " + i.X.String() + " Index " + i.Index.String() + " \n")
	case *ssa.IndexAddr:
		out.WriteString("  *ssa.IndexAddr")
		out.WriteString("    X: " + i.X.String() + " Index " + i.Index.String() + "\n")
	case *ssa.Lookup:
		out.WriteString("  *ssa.LookUp")
		out.WriteString("  x: " + i.X.String() + " Index " + i.Index.String() + " CommaOk " + strconv.FormatBool(i.CommaOk) + "\n")
	case *ssa.MakeChan:
		out.WriteString("  *ssa.MakeChan")
		out.WriteString("    Size: " + i.Size.String() + "\n")
	case *ssa.MakeClosure:
		out.WriteString("  *ssa.MakeClosure")
		out.WriteString("    FN: " + i.Fn.String() + " Bindings ")
		for _, i := range i.Bindings {
			out.WriteString("   " + i.String() + " ")
		}
		out.WriteString("\n")
	case *ssa.MakeInterface:
		out.WriteString("  *ssa.MakeInterface")
		out.WriteString("    X: " + i.X.String() + "\n")
	case *ssa.MakeMap:
		out.WriteString("  *ssa.MakeMap")
		if i.Reserve != nil {
			out.WriteString("    Reserve: " + i.Reserve.String() + "\n")
		}
	case *ssa.MakeSlice:
		out.WriteString("  *ssa.MakeSlice")
		out.WriteString("  Len: " + i.Len.String() + " Cap : " + i.Cap.String() + " \n")
	case *ssa.Next:
		out.WriteString("  *ssa.Next")
		out.WriteString("    Iter " + i.Iter.String() + " isString " + strconv.FormatBool(i.IsString) + "\n")
		/*				case *ssa.Parameter:
						out.WriteString("*ssa.Parameter") */
	case *ssa.Phi:
		out.WriteString("  *ssa.Phi")
		out.WriteString("    Comment: " + i.Comment + " Edges ")
		for _, i := range i.Edges {
			out.WriteString("   " + i.String() + " ")
		}
		out.WriteString(" \n")
	case *ssa.Range:
		out.WriteString("  *ssa.Range")
		out.WriteString("    X " + i.X.String() + "\n")
	case *ssa.Select:
		out.WriteString("  *ssa.Select")
		out.WriteString("    States: ")
		for _, i := range i.States {
			out.WriteString("   Channel: " + i.Chan.String() + " Send : " + i.Send.String())
		}
		out.WriteString("   Blocking: " + strconv.FormatBool(i.Blocking) + "\n")
	case *ssa.Send:
		out.WriteString("  *ssa.Send")
		out.WriteString("    Chan: " + i.Chan.String() + " X: " + i.X.String() + "\n")
	case *ssa.Slice:
		out.WriteString("  *ssa.Slice")
		if i.X != nil {
			out.WriteString("    x: " + i.X.String())
		}
		if i.Low != nil {
			out.WriteString("    Low: " + i.Low.String())
		}
		if i.Max != nil {
			out.WriteString("    Max: " + i.Max.String())
		}
		out.WriteString("\n")
	case *ssa.Store:
		out.WriteString("  *ssa.Store")
		out.WriteString("      Addr: " + i.Addr.String() + "  Val " + i.Addr.String() + "\n")
	case *ssa.TypeAssert:
		out.WriteString("  *ssa.TypeAssert")
		out.WriteString("    X: " + i.X.String() + " AssertedType: " + i.AssertedType.String() + " CommaOk " + strconv.FormatBool(i.CommaOk) + "\n")
	case *ssa.UnOp:
		out.WriteString("  *ssa.UnoOp")
		out.WriteString("    Op: " + i.Op.String() + " X: " + i.X.String() + " CommaOk: " + strconv.FormatBool(i.CommaOk) + "\n")
	}
}
Beispiel #8
0
// genInstr generates constraints for instruction instr in context cgn.
func (a *analysis) genInstr(cgn *cgnode, instr ssa.Instruction) {
	if a.log != nil {
		var prefix string
		if val, ok := instr.(ssa.Value); ok {
			prefix = val.Name() + " = "
		}
		fmt.Fprintf(a.log, "; %s%s\n", prefix, instr)
	}

	switch instr := instr.(type) {
	case *ssa.DebugRef:
		// no-op.

	case *ssa.UnOp:
		switch instr.Op {
		case token.ARROW: // <-x
			// We can ignore instr.CommaOk because the node we're
			// altering is always at zero offset relative to instr
			tElem := instr.X.Type().Underlying().(*types.Chan).Elem()
			a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(tElem))

		case token.MUL: // *x
			a.genLoad(cgn, a.valueNode(instr), instr.X, 0, a.sizeof(instr.Type()))

		default:
			// NOT, SUB, XOR: no-op.
		}

	case *ssa.BinOp:
		// All no-ops.

	case ssa.CallInstruction: // *ssa.Call, *ssa.Go, *ssa.Defer
		a.genCall(cgn, instr)

	case *ssa.ChangeType:
		a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)

	case *ssa.Convert:
		a.genConv(instr, cgn)

	case *ssa.Extract:
		a.copy(a.valueNode(instr),
			a.valueOffsetNode(instr.Tuple, instr.Index),
			a.sizeof(instr.Type()))

	case *ssa.FieldAddr:
		a.genOffsetAddr(cgn, instr, a.valueNode(instr.X),
			a.offsetOf(mustDeref(instr.X.Type()), instr.Field))

	case *ssa.IndexAddr:
		a.genOffsetAddr(cgn, instr, a.valueNode(instr.X), 1)

	case *ssa.Field:
		a.copy(a.valueNode(instr),
			a.valueOffsetNode(instr.X, instr.Field),
			a.sizeof(instr.Type()))

	case *ssa.Index:
		a.copy(a.valueNode(instr), 1+a.valueNode(instr.X), a.sizeof(instr.Type()))

	case *ssa.Select:
		recv := a.valueOffsetNode(instr, 2) // instr : (index, recvOk, recv0, ... recv_n-1)
		for _, st := range instr.States {
			elemSize := a.sizeof(st.Chan.Type().Underlying().(*types.Chan).Elem())
			switch st.Dir {
			case types.RecvOnly:
				a.genLoad(cgn, recv, st.Chan, 0, elemSize)
				recv += nodeid(elemSize)

			case types.SendOnly:
				a.genStore(cgn, st.Chan, a.valueNode(st.Send), 0, elemSize)
			}
		}

	case *ssa.Return:
		results := a.funcResults(cgn.obj)
		for _, r := range instr.Results {
			sz := a.sizeof(r.Type())
			a.copy(results, a.valueNode(r), sz)
			results += nodeid(sz)
		}

	case *ssa.Send:
		a.genStore(cgn, instr.Chan, a.valueNode(instr.X), 0, a.sizeof(instr.X.Type()))

	case *ssa.Store:
		a.genStore(cgn, instr.Addr, a.valueNode(instr.Val), 0, a.sizeof(instr.Val.Type()))

	case *ssa.Alloc, *ssa.MakeSlice, *ssa.MakeChan, *ssa.MakeMap, *ssa.MakeInterface:
		v := instr.(ssa.Value)
		a.addressOf(v.Type(), a.valueNode(v), a.objectNode(cgn, v))

	case *ssa.ChangeInterface:
		a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)

	case *ssa.TypeAssert:
		a.typeAssert(instr.AssertedType, a.valueNode(instr), a.valueNode(instr.X), true)

	case *ssa.Slice:
		a.copy(a.valueNode(instr), a.valueNode(instr.X), 1)

	case *ssa.If, *ssa.Jump:
		// no-op.

	case *ssa.Phi:
		sz := a.sizeof(instr.Type())
		for _, e := range instr.Edges {
			a.copy(a.valueNode(instr), a.valueNode(e), sz)
		}

	case *ssa.MakeClosure:
		fn := instr.Fn.(*ssa.Function)
		a.copy(a.valueNode(instr), a.valueNode(fn), 1)
		// Free variables are treated like global variables.
		for i, b := range instr.Bindings {
			a.copy(a.valueNode(fn.FreeVars[i]), a.valueNode(b), a.sizeof(b.Type()))
		}

	case *ssa.RunDefers:
		// The analysis is flow insensitive, so we just "call"
		// defers as we encounter them.

	case *ssa.Range:
		// Do nothing.  Next{Iter: *ssa.Range} handles this case.

	case *ssa.Next:
		if !instr.IsString { // map
			// Assumes that Next is always directly applied to a Range result.
			theMap := instr.Iter.(*ssa.Range).X
			tMap := theMap.Type().Underlying().(*types.Map)

			ksize := a.sizeof(tMap.Key())
			vsize := a.sizeof(tMap.Elem())

			// The k/v components of the Next tuple may each be invalid.
			tTuple := instr.Type().(*types.Tuple)

			// Load from the map's (k,v) into the tuple's (ok, k, v).
			osrc := uint32(0) // offset within map object
			odst := uint32(1) // offset within tuple (initially just after 'ok bool')
			sz := uint32(0)   // amount to copy

			// Is key valid?
			if tTuple.At(1).Type() != tInvalid {
				sz += ksize
			} else {
				odst += ksize
				osrc += ksize
			}

			// Is value valid?
			if tTuple.At(2).Type() != tInvalid {
				sz += vsize
			}

			a.genLoad(cgn, a.valueNode(instr)+nodeid(odst), theMap, osrc, sz)
		}

	case *ssa.Lookup:
		if tMap, ok := instr.X.Type().Underlying().(*types.Map); ok {
			// CommaOk can be ignored: field 0 is a no-op.
			ksize := a.sizeof(tMap.Key())
			vsize := a.sizeof(tMap.Elem())
			a.genLoad(cgn, a.valueNode(instr), instr.X, ksize, vsize)
		}

	case *ssa.MapUpdate:
		tmap := instr.Map.Type().Underlying().(*types.Map)
		ksize := a.sizeof(tmap.Key())
		vsize := a.sizeof(tmap.Elem())
		a.genStore(cgn, instr.Map, a.valueNode(instr.Key), 0, ksize)
		a.genStore(cgn, instr.Map, a.valueNode(instr.Value), ksize, vsize)

	case *ssa.Panic:
		a.copy(a.panicNode, a.valueNode(instr.X), 1)

	default:
		panic(fmt.Sprintf("unimplemented: %T", instr))
	}
}
Beispiel #9
0
func (f *Function) Instr(instr ssa.Instruction) (string, *Error) {

	if instr == nil {
		ice("nil instr")
	}
	asm := ""
	var err *Error

	errormsg := func(msg string) (string, *Error) {
		return "", &Error{Err: fmt.Errorf(msg), Pos: instr.Pos()}
	}
	if f.Trace {
		if _, ok := instr.(*ssa.DebugRef); ok {
			// Nothing to do
		} else {
			if v, ok := instr.(ssa.Value); ok {
				fmt.Printf("TRACE %v = %v\n", v.Name(), v)
			} else {
				fmt.Printf("TRACE %v\n", instr)
			}
		}
	}
	switch instr := instr.(type) {
	default:
		err = &Error{Err: fmt.Errorf("Unknown ssa instruction (type:%v): %v\n", reflect.TypeOf(instr), instr), Pos: instr.Pos()}
	case *ssa.Alloc:
		asm, err = f.AllocInstr(instr)
	case *ssa.BinOp:
		asm, err = f.BinOp(instr)
	case *ssa.Call:
		asm, err = f.Call(instr)
	case *ssa.ChangeInterface:
		asm, err = errormsg("converting interfaces unsupported")
	case *ssa.ChangeType:
		asm, err = errormsg("changing between types unsupported")
	case *ssa.Convert:
		asm, err = f.Convert(instr)
	case *ssa.DebugRef:
		// Nothing to do
	case *ssa.Defer:
		asm, err = errormsg("defer unsupported")
	case *ssa.Extract:
		asm, err = errormsg("extracting tuple values unsupported")
	case *ssa.Field:
		asm, err = errormsg("field access unimplemented")
	case *ssa.FieldAddr:
		asm, err = errormsg("field access unimplemented")
	case *ssa.Go:
		asm, err = errormsg("go keyword unsupported")
	case *ssa.If:
		asm, err = f.If(instr)
	case *ssa.Index:
		asm, err = f.Index(instr)
	case *ssa.IndexAddr:
		asm, err = f.IndexAddr(instr)
	case *ssa.Jump:
		asm, err = f.Jump(instr)
	case *ssa.Lookup:
		asm, err = errormsg("maps unsupported")
	case *ssa.MakeChan:
		asm, err = errormsg("channels unsupported")
	case *ssa.MakeClosure:
		asm, err = errormsg("closures unsupported")
	case *ssa.MakeInterface, *ssa.MakeMap, *ssa.MakeSlice:
		asm, err = errormsg("make slice/map/interface unsupported")
	case *ssa.MapUpdate:
		asm, err = errormsg("map update unsupported")
	case *ssa.Next:
		asm, err = errormsg("map/string iterators unsupported")
	case *ssa.Panic:
		asm, err = errormsg("panic unimplemented")
	case *ssa.Phi:
		asm, err = f.Phi(instr)
	case *ssa.Range:
		asm, err = errormsg("range unsupported")
	case *ssa.Return:
		asm, err = f.Return(instr)
	case *ssa.Select, *ssa.RunDefers, *ssa.Send:
		asm, err = errormsg("select/send/defer unsupported")
	case *ssa.Slice:
		asm, err = f.Slice(instr)
	case *ssa.Store:
		asm, err = f.Store(instr)
	case *ssa.TypeAssert:
		asm, err = errormsg("type assert unsupported")
	case *ssa.UnOp:
		asm, err = f.UnOp(instr)
	}

	if err != nil && !err.Pos.IsValid() {
		err.Pos = instr.Pos()
	}
	return asm, err
}
Beispiel #10
0
// visitInstr interprets a single ssa.Instruction within the activation
// record frame.  It returns a continuation value indicating where to
// read the next instruction from.
func visitInstr(fr *frame, instr ssa.Instruction) continuation {
	switch instr := instr.(type) {
	case *ssa.DebugRef:
		// no-op

	case *ssa.UnOp:
		fr.env[instr] = unop(instr, fr.get(instr.X))

	case *ssa.BinOp:
		fr.env[instr] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y))

	case *ssa.Call:
		fn, args := prepareCall(fr, &instr.Call)
		fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args)

	case *ssa.ChangeInterface:
		fr.env[instr] = fr.get(instr.X)

	case *ssa.ChangeType:
		fr.env[instr] = fr.get(instr.X) // (can't fail)

	case *ssa.Convert:
		fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X))

	case *ssa.MakeInterface:
		fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)}

	case *ssa.Extract:
		fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index]

	case *ssa.Slice:
		fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max))

	case *ssa.Return:
		switch len(instr.Results) {
		case 0:
		case 1:
			fr.result = fr.get(instr.Results[0])
		default:
			var res []value
			for _, r := range instr.Results {
				res = append(res, fr.get(r))
			}
			fr.result = tuple(res)
		}
		fr.block = nil
		return kReturn

	case *ssa.RunDefers:
		fr.runDefers()

	case *ssa.Panic:
		panic(targetPanic{fr.get(instr.X)})

	case *ssa.Send:
		fr.get(instr.Chan).(chan value) <- copyVal(fr.get(instr.X))

	case *ssa.Store:
		*fr.get(instr.Addr).(*value) = copyVal(fr.get(instr.Val))

	case *ssa.If:
		succ := 1
		if fr.get(instr.Cond).(bool) {
			succ = 0
		}
		fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ]
		return kJump

	case *ssa.Jump:
		fr.prevBlock, fr.block = fr.block, fr.block.Succs[0]
		return kJump

	case *ssa.Defer:
		fn, args := prepareCall(fr, &instr.Call)
		fr.defers = &deferred{
			fn:    fn,
			args:  args,
			instr: instr,
			tail:  fr.defers,
		}

	case *ssa.Go:
		fn, args := prepareCall(fr, &instr.Call)
		go call(fr.i, nil, instr.Pos(), fn, args)

	case *ssa.MakeChan:
		fr.env[instr] = make(chan value, asInt(fr.get(instr.Size)))

	case *ssa.Alloc:
		var addr *value
		if instr.Heap {
			// new
			addr = new(value)
			fr.env[instr] = addr
		} else {
			// local
			addr = fr.env[instr].(*value)
		}
		*addr = zero(deref(instr.Type()))

	case *ssa.MakeSlice:
		slice := make([]value, asInt(fr.get(instr.Cap)))
		tElt := instr.Type().Underlying().(*types.Slice).Elem()
		for i := range slice {
			slice[i] = zero(tElt)
		}
		fr.env[instr] = slice[:asInt(fr.get(instr.Len))]

	case *ssa.MakeMap:
		reserve := 0
		if instr.Reserve != nil {
			reserve = asInt(fr.get(instr.Reserve))
		}
		fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve)

	case *ssa.Range:
		fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type())

	case *ssa.Next:
		fr.env[instr] = fr.get(instr.Iter).(iter).next()

	case *ssa.FieldAddr:
		x := fr.get(instr.X)
		fr.env[instr] = &(*x.(*value)).(structure)[instr.Field]

	case *ssa.Field:
		fr.env[instr] = copyVal(fr.get(instr.X).(structure)[instr.Field])

	case *ssa.IndexAddr:
		x := fr.get(instr.X)
		idx := fr.get(instr.Index)
		switch x := x.(type) {
		case []value:
			fr.env[instr] = &x[asInt(idx)]
		case *value: // *array
			fr.env[instr] = &(*x).(array)[asInt(idx)]
		default:
			panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x))
		}

	case *ssa.Index:
		fr.env[instr] = copyVal(fr.get(instr.X).(array)[asInt(fr.get(instr.Index))])

	case *ssa.Lookup:
		fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index))

	case *ssa.MapUpdate:
		m := fr.get(instr.Map)
		key := fr.get(instr.Key)
		v := fr.get(instr.Value)
		switch m := m.(type) {
		case map[value]value:
			m[key] = v
		case *hashmap:
			m.insert(key.(hashable), v)
		default:
			panic(fmt.Sprintf("illegal map type: %T", m))
		}

	case *ssa.TypeAssert:
		fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface))

	case *ssa.MakeClosure:
		var bindings []value
		for _, binding := range instr.Bindings {
			bindings = append(bindings, fr.get(binding))
		}
		fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings}

	case *ssa.Phi:
		for i, pred := range instr.Block().Preds {
			if fr.prevBlock == pred {
				fr.env[instr] = fr.get(instr.Edges[i])
				break
			}
		}

	case *ssa.Select:
		var cases []reflect.SelectCase
		if !instr.Blocking {
			cases = append(cases, reflect.SelectCase{
				Dir: reflect.SelectDefault,
			})
		}
		for _, state := range instr.States {
			var dir reflect.SelectDir
			if state.Dir == types.RecvOnly {
				dir = reflect.SelectRecv
			} else {
				dir = reflect.SelectSend
			}
			var send reflect.Value
			if state.Send != nil {
				send = reflect.ValueOf(fr.get(state.Send))
			}
			cases = append(cases, reflect.SelectCase{
				Dir:  dir,
				Chan: reflect.ValueOf(fr.get(state.Chan)),
				Send: send,
			})
		}
		chosen, recv, recvOk := reflect.Select(cases)
		if !instr.Blocking {
			chosen-- // default case should have index -1.
		}
		r := tuple{chosen, recvOk}
		for i, st := range instr.States {
			if st.Dir == types.RecvOnly {
				var v value
				if i == chosen && recvOk {
					// No need to copy since send makes an unaliased copy.
					v = recv.Interface().(value)
				} else {
					v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem())
				}
				r = append(r, v)
			}
		}
		fr.env[instr] = r

	default:
		panic(fmt.Sprintf("unexpected instruction: %T", instr))
	}

	// if val, ok := instr.(ssa.Value); ok {
	// 	fmt.Println(toString(fr.env[val])) // debugging
	// }

	return kNext
}
Beispiel #11
0
func (fr *frame) instruction(instr ssa.Instruction) {
	fr.logf("[%T] %v @ %s\n", instr, instr, fr.pkg.Prog.Fset.Position(instr.Pos()))
	if fr.GenerateDebug {
		fr.debug.SetLocation(fr.builder, instr.Pos())
	}

	switch instr := instr.(type) {
	case *ssa.Alloc:
		typ := deref(instr.Type())
		llvmtyp := fr.llvmtypes.ToLLVM(typ)
		var value llvm.Value
		if !instr.Heap {
			value = fr.env[instr].value
			fr.memsetZero(value, llvm.SizeOf(llvmtyp))
		} else if fr.isInit && fr.shouldStaticallyAllocate(instr) {
			// If this is the init function and we think it may be beneficial,
			// allocate memory statically in the object file rather than on the
			// heap. This allows us to optimize constant stores into such
			// variables as static initializations.
			global := llvm.AddGlobal(fr.module.Module, llvmtyp, "")
			global.SetLinkage(llvm.InternalLinkage)
			fr.addGlobal(global, typ)
			ptr := llvm.ConstBitCast(global, llvm.PointerType(llvm.Int8Type(), 0))
			fr.env[instr] = newValue(ptr, instr.Type())
		} else {
			value = fr.createTypeMalloc(typ)
			value.SetName(instr.Comment)
			value = fr.builder.CreateBitCast(value, llvm.PointerType(llvm.Int8Type(), 0), "")
			fr.env[instr] = newValue(value, instr.Type())
		}

	case *ssa.BinOp:
		lhs, rhs := fr.value(instr.X), fr.value(instr.Y)
		fr.env[instr] = fr.binaryOp(lhs, instr.Op, rhs)

	case *ssa.Call:
		tuple := fr.callInstruction(instr)
		if len(tuple) == 1 {
			fr.env[instr] = tuple[0]
		} else {
			fr.tuples[instr] = tuple
		}

	case *ssa.ChangeInterface:
		x := fr.value(instr.X)
		// The source type must be a non-empty interface,
		// as ChangeInterface cannot fail (E2I may fail).
		if instr.Type().Underlying().(*types.Interface).NumMethods() > 0 {
			x = fr.changeInterface(x, instr.Type(), false)
		} else {
			x = fr.convertI2E(x)
		}
		fr.env[instr] = x

	case *ssa.ChangeType:
		value := fr.llvmvalue(instr.X)
		if _, ok := instr.Type().Underlying().(*types.Pointer); ok {
			value = fr.builder.CreateBitCast(value, fr.llvmtypes.ToLLVM(instr.Type()), "")
		}
		fr.env[instr] = newValue(value, instr.Type())

	case *ssa.Convert:
		v := fr.value(instr.X)
		fr.env[instr] = fr.convert(v, instr.Type())

	case *ssa.Defer:
		fn, arg := fr.createThunk(instr)
		fr.runtime.Defer.call(fr, fr.frameptr, fn, arg)

	case *ssa.Extract:
		var elem llvm.Value
		if t, ok := fr.tuples[instr.Tuple]; ok {
			elem = t[instr.Index].value
		} else {
			tuple := fr.llvmvalue(instr.Tuple)
			elem = fr.builder.CreateExtractValue(tuple, instr.Index, instr.Name())
		}
		elemtyp := instr.Type()
		fr.env[instr] = newValue(elem, elemtyp)

	case *ssa.Field:
		fieldtyp := instr.Type()
		if p, ok := fr.ptr[instr.X]; ok {
			field := fr.builder.CreateStructGEP(p, instr.Field, instr.Name())
			if fr.canAvoidElementLoad(*instr.Referrers()) {
				fr.ptr[instr] = field
			} else {
				fr.env[instr] = newValue(fr.builder.CreateLoad(field, ""), fieldtyp)
			}
		} else {
			value := fr.llvmvalue(instr.X)
			field := fr.builder.CreateExtractValue(value, instr.Field, instr.Name())
			fr.env[instr] = newValue(field, fieldtyp)
		}

	case *ssa.FieldAddr:
		ptr := fr.llvmvalue(instr.X)
		fr.nilCheck(instr.X, ptr)
		xtyp := instr.X.Type().Underlying().(*types.Pointer).Elem()
		ptrtyp := llvm.PointerType(fr.llvmtypes.ToLLVM(xtyp), 0)
		ptr = fr.builder.CreateBitCast(ptr, ptrtyp, "")
		fieldptr := fr.builder.CreateStructGEP(ptr, instr.Field, instr.Name())
		fieldptr = fr.builder.CreateBitCast(fieldptr, llvm.PointerType(llvm.Int8Type(), 0), "")
		fieldptrtyp := instr.Type()
		fr.env[instr] = newValue(fieldptr, fieldptrtyp)

	case *ssa.Go:
		fn, arg := fr.createThunk(instr)
		fr.runtime.Go.call(fr, fn, arg)

	case *ssa.If:
		cond := fr.llvmvalue(instr.Cond)
		block := instr.Block()
		trueBlock := fr.block(block.Succs[0])
		falseBlock := fr.block(block.Succs[1])
		cond = fr.builder.CreateTrunc(cond, llvm.Int1Type(), "")
		fr.builder.CreateCondBr(cond, trueBlock, falseBlock)

	case *ssa.Index:
		var arrayptr llvm.Value

		if ptr, ok := fr.ptr[instr.X]; ok {
			arrayptr = ptr
		} else {
			array := fr.llvmvalue(instr.X)
			arrayptr = fr.allocaBuilder.CreateAlloca(array.Type(), "")

			fr.builder.CreateStore(array, arrayptr)
		}
		index := fr.llvmvalue(instr.Index)

		arraytyp := instr.X.Type().Underlying().(*types.Array)
		arraylen := llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false)

		// The index may not have been promoted to int (for example, if it
		// came from a composite literal).
		index = fr.createZExtOrTrunc(index, fr.types.inttype, "")

		// Bounds checking: 0 <= index < len
		zero := llvm.ConstNull(fr.types.inttype)
		i0 := fr.builder.CreateICmp(llvm.IntSLT, index, zero, "")
		li := fr.builder.CreateICmp(llvm.IntSLE, arraylen, index, "")

		cond := fr.builder.CreateOr(i0, li, "")

		fr.condBrRuntimeError(cond, gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS)

		addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{zero, index}, "")
		if fr.canAvoidElementLoad(*instr.Referrers()) {
			fr.ptr[instr] = addr
		} else {
			fr.env[instr] = newValue(fr.builder.CreateLoad(addr, ""), instr.Type())
		}

	case *ssa.IndexAddr:
		x := fr.llvmvalue(instr.X)
		index := fr.llvmvalue(instr.Index)
		var arrayptr, arraylen llvm.Value
		var elemtyp types.Type
		var errcode uint64
		switch typ := instr.X.Type().Underlying().(type) {
		case *types.Slice:
			elemtyp = typ.Elem()
			arrayptr = fr.builder.CreateExtractValue(x, 0, "")
			arraylen = fr.builder.CreateExtractValue(x, 1, "")
			errcode = gccgoRuntimeErrorSLICE_INDEX_OUT_OF_BOUNDS
		case *types.Pointer: // *array
			arraytyp := typ.Elem().Underlying().(*types.Array)
			elemtyp = arraytyp.Elem()
			fr.nilCheck(instr.X, x)
			arrayptr = x
			arraylen = llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false)
			errcode = gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS
		}

		// The index may not have been promoted to int (for example, if it
		// came from a composite literal).
		index = fr.createZExtOrTrunc(index, fr.types.inttype, "")

		// Bounds checking: 0 <= index < len
		zero := llvm.ConstNull(fr.types.inttype)
		i0 := fr.builder.CreateICmp(llvm.IntSLT, index, zero, "")
		li := fr.builder.CreateICmp(llvm.IntSLE, arraylen, index, "")

		cond := fr.builder.CreateOr(i0, li, "")

		fr.condBrRuntimeError(cond, errcode)

		ptrtyp := llvm.PointerType(fr.llvmtypes.ToLLVM(elemtyp), 0)
		arrayptr = fr.builder.CreateBitCast(arrayptr, ptrtyp, "")
		addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{index}, "")
		addr = fr.builder.CreateBitCast(addr, llvm.PointerType(llvm.Int8Type(), 0), "")
		fr.env[instr] = newValue(addr, types.NewPointer(elemtyp))

	case *ssa.Jump:
		succ := instr.Block().Succs[0]
		fr.builder.CreateBr(fr.block(succ))

	case *ssa.Lookup:
		x := fr.value(instr.X)
		index := fr.value(instr.Index)
		if isString(x.Type().Underlying()) {
			fr.env[instr] = fr.stringIndex(x, index)
		} else {
			v, ok := fr.mapLookup(x, index)
			if instr.CommaOk {
				fr.tuples[instr] = []*govalue{v, ok}
			} else {
				fr.env[instr] = v
			}
		}

	case *ssa.MakeChan:
		fr.env[instr] = fr.makeChan(instr.Type(), fr.value(instr.Size))

	case *ssa.MakeClosure:
		llfn := fr.resolveFunctionGlobal(instr.Fn.(*ssa.Function))
		llfn = llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0))
		fn := newValue(llfn, instr.Fn.(*ssa.Function).Signature)
		bindings := make([]*govalue, len(instr.Bindings))
		for i, binding := range instr.Bindings {
			bindings[i] = fr.value(binding)
		}
		fr.env[instr] = fr.makeClosure(fn, bindings)

	case *ssa.MakeInterface:
		// fr.ptr[instr.X] will be set if a pointer load was elided by canAvoidLoad
		if ptr, ok := fr.ptr[instr.X]; ok {
			fr.env[instr] = fr.makeInterfaceFromPointer(ptr, instr.X.Type(), instr.Type())
		} else {
			receiver := fr.llvmvalue(instr.X)
			fr.env[instr] = fr.makeInterface(receiver, instr.X.Type(), instr.Type())
		}

	case *ssa.MakeMap:
		fr.env[instr] = fr.makeMap(instr.Type(), fr.value(instr.Reserve))

	case *ssa.MakeSlice:
		length := fr.value(instr.Len)
		capacity := fr.value(instr.Cap)
		fr.env[instr] = fr.makeSlice(instr.Type(), length, capacity)

	case *ssa.MapUpdate:
		m := fr.value(instr.Map)
		k := fr.value(instr.Key)
		v := fr.value(instr.Value)
		fr.mapUpdate(m, k, v)

	case *ssa.Next:
		iter := fr.tuples[instr.Iter]
		if instr.IsString {
			fr.tuples[instr] = fr.stringIterNext(iter)
		} else {
			fr.tuples[instr] = fr.mapIterNext(iter)
		}

	case *ssa.Panic:
		arg := fr.value(instr.X)
		fr.callPanic(arg)

	case *ssa.Phi:
		typ := instr.Type()
		phi := fr.builder.CreatePHI(fr.llvmtypes.ToLLVM(typ), instr.Comment)
		fr.env[instr] = newValue(phi, typ)
		fr.phis = append(fr.phis, pendingPhi{instr, phi})

	case *ssa.Range:
		x := fr.value(instr.X)
		switch x.Type().Underlying().(type) {
		case *types.Map:
			fr.tuples[instr] = fr.mapIterInit(x)
		case *types.Basic: // string
			fr.tuples[instr] = fr.stringIterInit(x)
		default:
			panic(fmt.Sprintf("unhandled range for type %T", x.Type()))
		}

	case *ssa.Return:
		vals := make([]llvm.Value, len(instr.Results))
		for i, res := range instr.Results {
			vals[i] = fr.llvmvalue(res)
		}
		fr.retInf.encode(llvm.GlobalContext(), fr.allocaBuilder, fr.builder, vals)

	case *ssa.RunDefers:
		fr.runDefers()

	case *ssa.Select:
		states := make([]selectState, len(instr.States))
		for i, state := range instr.States {
			states[i] = selectState{
				Dir:  state.Dir,
				Chan: fr.value(state.Chan),
				Send: fr.value(state.Send),
			}
		}
		index, recvOk, recvElems := fr.chanSelect(states, instr.Blocking)
		tuple := append([]*govalue{index, recvOk}, recvElems...)
		fr.tuples[instr] = tuple

	case *ssa.Send:
		fr.chanSend(fr.value(instr.Chan), fr.value(instr.X))

	case *ssa.Slice:
		x := fr.llvmvalue(instr.X)
		low := fr.llvmvalue(instr.Low)
		high := fr.llvmvalue(instr.High)
		max := fr.llvmvalue(instr.Max)
		slice := fr.slice(x, instr.X.Type(), low, high, max)
		fr.env[instr] = newValue(slice, instr.Type())

	case *ssa.Store:
		addr := fr.llvmvalue(instr.Addr)
		value := fr.llvmvalue(instr.Val)
		addr = fr.builder.CreateBitCast(addr, llvm.PointerType(value.Type(), 0), "")
		// If this is the init function, see if we can simulate the effect
		// of the store in a global's initializer, in which case we can avoid
		// generating code for it.
		if !fr.isInit || !fr.maybeStoreInInitializer(value, addr) {
			fr.nilCheck(instr.Addr, addr)
			fr.builder.CreateStore(value, addr)
		}

	case *ssa.TypeAssert:
		x := fr.value(instr.X)
		if instr.CommaOk {
			v, ok := fr.interfaceTypeCheck(x, instr.AssertedType)
			fr.tuples[instr] = []*govalue{v, ok}
		} else {
			fr.env[instr] = fr.interfaceTypeAssert(x, instr.AssertedType)
		}

	case *ssa.UnOp:
		operand := fr.value(instr.X)
		switch instr.Op {
		case token.ARROW:
			x, ok := fr.chanRecv(operand, instr.CommaOk)
			if instr.CommaOk {
				fr.tuples[instr] = []*govalue{x, ok}
			} else {
				fr.env[instr] = x
			}
		case token.MUL:
			fr.nilCheck(instr.X, operand.value)
			if !fr.canAvoidLoad(instr, operand.value) {
				// The bitcast is necessary to handle recursive pointer loads.
				llptr := fr.builder.CreateBitCast(operand.value, llvm.PointerType(fr.llvmtypes.ToLLVM(instr.Type()), 0), "")
				fr.env[instr] = newValue(fr.builder.CreateLoad(llptr, ""), instr.Type())
			}
		default:
			fr.env[instr] = fr.unaryOp(operand, instr.Op)
		}

	default:
		panic(fmt.Sprintf("unhandled: %v", instr))
	}
}
Beispiel #12
0
func visitInst(inst ssa.Instruction, fr *frame) {
	switch inst := inst.(type) {
	case *ssa.MakeChan:
		visitMakeChan(inst, fr)

	case *ssa.Send:
		visitSend(inst, fr)

	case *ssa.UnOp:
		switch inst.Op {
		case token.ARROW:
			visitRecv(inst, fr)
		case token.MUL:
			visitDeref(inst, fr)
		default:
			fmt.Fprintf(os.Stderr, "   # unhandled %s = %s\n", red(inst.Name()), red(inst.String()))
		}

	case *ssa.Call:
		visitCall(inst, fr)

	case *ssa.Extract:
		visitExtract(inst, fr)

	case *ssa.Go:
		fr.callGo(inst)

	case *ssa.Return:
		fr.retvals = visitReturn(inst, fr)

	case *ssa.Store:
		visitStore(inst, fr)

	case *ssa.Alloc:
		visitAlloc(inst, fr)

	case *ssa.MakeClosure:
		visitMakeClosure(inst, fr)

	case *ssa.Select:
		visitSelect(inst, fr)

	case *ssa.ChangeType:
		visitChangeType(inst, fr)

	case *ssa.ChangeInterface:
		visitChangeInterface(inst, fr)

	case *ssa.If:
		visitIf(inst, fr)

	case *ssa.Jump:
		visitJump(inst, fr)

	case *ssa.BinOp:
		visitBinOp(inst, fr)

	case *ssa.Slice:
		visitSlice(inst, fr)

	case *ssa.MakeSlice:
		visitMakeSlice(inst, fr)

	case *ssa.FieldAddr:
		visitFieldAddr(inst, fr)

	case *ssa.Field:
		visitField(inst, fr)

	case *ssa.IndexAddr:
		visitIndexAddr(inst, fr)

	case *ssa.Index:
		visitIndex(inst, fr)

	case *ssa.Defer:
		visitDefer(inst, fr)

	case *ssa.RunDefers:
		visitRunDefers(inst, fr)

	case *ssa.Phi:
		visitPhi(inst, fr)

	case *ssa.TypeAssert:
		visitTypeAssert(inst, fr)

	case *ssa.MakeInterface:
		visitMakeInterface(inst, fr)

	case *ssa.DebugRef:

	default:
		// Everything else not handled yet
		if v, ok := inst.(ssa.Value); ok {
			fmt.Fprintf(os.Stderr, "   # unhandled %s = %s\n", red(v.Name()), red(v.String()))
		} else {
			fmt.Fprintf(os.Stderr, "   # unhandled %s\n", red(inst.String()))
		}
	}
}