Esempio n. 1
0
// visitFunc analyses function body.
func visitFunc(fn *ssa.Function, infer *TypeInfer, f *Function) {
	infer.Env.MigoProg.AddFunction(f.FuncDef)

	infer.Logger.Printf(f.Sprintf(FuncEnterSymbol+"───── func %s ─────", fn.Name()))
	defer infer.Logger.Printf(f.Sprintf(FuncExitSymbol+"───── func %s ─────", fn.Name()))
	if fn.Name() == "init" {
		if _, ok := f.Prog.InitPkgs[fn.Package()]; !ok {
			f.Prog.InitPkgs[fn.Package()] = true
		}
		f.hasBody = true
		return
	}
	for val, instance := range f.locals {
		infer.Logger.Printf(f.Sprintf(ParamSymbol+"%s = %s", val.Name(), instance))
		f.revlookup[instance.String()] = val.Name() // If it comes from params..
	}

	if fn.Blocks == nil {
		infer.Logger.Print(f.Sprintf(MoreSymbol + "« no function body »"))
		f.hasBody = false // No body
		return
	}
	// When entering function, always visit as block 0
	block0 := NewBlock(f, fn.Blocks[0], 0)
	visitBasicBlock(fn.Blocks[0], infer, f, block0, &Loop{Parent: f})
	f.hasBody = true
}
Esempio n. 2
0
// CheckNames exists because of this comment in the SSA code documentation:
// "Many objects are nonetheless named to aid in debugging, but it is not essential that the names be either accurate or unambiguous. "
func CheckNames(f *ssa.Function) error {
	names := make(map[string]*ssa.Instruction)

	//fmt.Println("DEBUG Check Names for ", f.String())

	for blk := range f.Blocks {
		for ins := range f.Blocks[blk].Instrs {
			instrVal, hasVal := f.Blocks[blk].Instrs[ins].(ssa.Value)
			if hasVal {
				register := instrVal.Name()
				//fmt.Println("DEBUG name ", register)
				val, found := names[register]
				if found {
					if val != &f.Blocks[blk].Instrs[ins] {
						return fmt.Errorf("internal error, ssa register names not unique in function %s var name %s",
							f.String(), register)
					}
				} else {
					names[register] = &f.Blocks[blk].Instrs[ins]
				}
			}
		}
	}
	return nil
}
Esempio n. 3
0
// findIntrinsic returns the constraint generation function for an
// intrinsic function fn, or nil if the function should be handled normally.
//
func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
	// Consult the *Function-keyed cache.
	// A cached nil indicates a normal non-intrinsic function.
	impl, ok := a.intrinsics[fn]
	if !ok {
		impl = intrinsicsByName[fn.String()] // may be nil

		if a.isReflect(fn) {
			if !a.config.Reflection {
				impl = ext۰NoEffect // reflection disabled
			} else if impl == nil {
				// Ensure all "reflect" code is treated intrinsically.
				impl = ext۰NotYetImplemented
			}
		} else if impl == nil && fn.Pkg != nil && fn.Pkg.Pkg.Path() == "runtime" {
			// Ignore "runtime" (except SetFinalizer):
			// it has few interesting effects on aliasing
			// and is full of unsafe code we can't analyze.
			impl = ext۰NoEffect
		}

		a.intrinsics[fn] = impl
	}
	return impl
}
Esempio n. 4
0
// Switches examines the control-flow graph of fn and returns the
// set of inferred value and type switches.  A value switch tests an
// ssa.Value for equality against two or more compile-time constant
// values.  Switches involving link-time constants (addresses) are
// ignored.  A type switch type-asserts an ssa.Value against two or
// more types.
//
// The switches are returned in dominance order.
//
// The resulting switches do not necessarily correspond to uses of the
// 'switch' keyword in the source: for example, a single source-level
// switch statement with non-constant cases may result in zero, one or
// many Switches, one per plural sequence of constant cases.
// Switches may even be inferred from if/else- or goto-based control flow.
// (In general, the control flow constructs of the source program
// cannot be faithfully reproduced from the SSA representation.)
//
func Switches(fn *ssa.Function) []Switch {
	// Traverse the CFG in dominance order, so we don't
	// enter an if/else-chain in the middle.
	var switches []Switch
	seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet
	for _, b := range fn.DomPreorder() {
		if x, k := isComparisonBlock(b); x != nil {
			// Block b starts a switch.
			sw := Switch{Start: b, X: x}
			valueSwitch(&sw, k, seen)
			if len(sw.ConstCases) > 1 {
				switches = append(switches, sw)
			}
		}

		if y, x, T := isTypeAssertBlock(b); y != nil {
			// Block b starts a type switch.
			sw := Switch{Start: b, X: x}
			typeSwitch(&sw, y, T, seen)
			if len(sw.TypeCases) > 1 {
				switches = append(switches, sw)
			}
		}
	}
	return switches
}
Esempio n. 5
0
// Emit the start of a function.
func emitFuncStart(fn *ssa.Function, blks []*ssa.BasicBlock, trackPhi bool, canOptMap map[string]bool, mustSplitCode bool, reconstruct []tgossa.BlockFormat) {
	l := TargetLang
	posStr := CodePosition(fn.Pos())
	pName, mName := GetFnNameParts(fn)
	isPublic := unicode.IsUpper(rune(mName[0])) // TODO check rules for non-ASCII 1st characters and fix
	fmt.Fprintln(&LanguageList[l].buffer,
		LanguageList[l].FuncStart(pName, mName, fn, blks, posStr, isPublic, trackPhi, grMap[fn] || mustSplitCode, canOptMap, reconstruct))
}
Esempio n. 6
0
func findCallSite(fn *ssa.Function, call *ast.CallExpr) (ssa.CallInstruction, error) {
	instr, _ := fn.ValueForExpr(call)
	callInstr, _ := instr.(ssa.CallInstruction)
	if instr == nil {
		return nil, fmt.Errorf("this call site is unreachable in this analysis")
	}
	return callInstr, nil
}
Esempio n. 7
0
func (fa *FairnessAnalysis) Visit(fn *ssa.Function) {
	visitedBlk := make(map[*ssa.BasicBlock]bool)
	fa.logger.Printf("Visiting: %s", fn.String())
	for _, blk := range fn.Blocks {
		if _, visited := visitedBlk[blk]; !visited {
			visitedBlk[blk] = true
			fa.logger.Printf(" block %d %s", blk.Index, blk.Comment)
			// First consider blocks with loop initialisation blocks.
			if blk.Comment == "rangeindex.loop" {
				fa.total++
				fa.logger.Println(color.GreenString("✓ range loops are fair"))
			} else if blk.Comment == "rangechan.loop" {
				fa.total++
				hasClose := false
				for _, ch := range fa.info.FindChan(blk.Instrs[0].(*ssa.UnOp).X) {
					if ch.Type == ssabuilder.ChanClose {
						fa.logger.Println(color.GreenString("✓ found corresponding close() - channel range likely fair"))
						hasClose = true
					}
				}
				if !hasClose {
					fa.logger.Println(color.RedString("❌ range over channel w/o close() likely unfair (%s)", fa.info.FSet.Position(blk.Instrs[0].Pos())))
					fa.unsafe++
				}
			} else if blk.Comment == "for.loop" {
				fa.total++
				if fa.isLikelyUnsafe(blk) {
					fa.logger.Println(color.RedString("❌ for.loop maybe bad"))
					fa.unsafe++
				} else {
					fa.logger.Println(color.GreenString("✓ for.loop is ok"))
				}
			} else { // Normal blocks (or loops without initialisation blocks).
				if len(blk.Instrs) > 1 {
					if ifInst, ok := blk.Instrs[len(blk.Instrs)-1].(*ssa.If); ok {
						_, thenVisited := visitedBlk[ifInst.Block().Succs[0]]
						_, elseVisited := visitedBlk[ifInst.Block().Succs[1]]
						if thenVisited || elseVisited { // there is a loop!
							fa.total++
							if !fa.isCondFair(ifInst.Cond) {
								fa.logger.Println(color.YellowString("Warning: recurring block condition probably unfair"))
								fa.unsafe++
							} else {
								fa.logger.Println(color.GreenString("✓ recurring block is ok"))
							}
						}
					} else if jInst, ok := blk.Instrs[len(blk.Instrs)-1].(*ssa.Jump); ok {
						if _, visited := visitedBlk[jInst.Block().Succs[0]]; visited {
							fa.total++
							fa.unsafe++
							fa.logger.Println(color.RedString("❌ infinite loop or recurring block, probably bad (%s)", fa.info.FSet.Position(blk.Instrs[0].Pos())))
						}
					}
				}
			}
		}
	}
}
Esempio n. 8
0
// prepareCallFn prepares a caller Function to visit performing necessary context switching and returns a new callee Function.
// rcvr is non-nil if invoke call
func (caller *Function) prepareCallFn(common *ssa.CallCommon, fn *ssa.Function, rcvr ssa.Value) *Function {
	callee := NewFunction(caller)
	callee.Fn = fn
	// This function was called before
	if _, ok := callee.Prog.FuncInstance[callee.Fn]; ok {
		callee.Prog.FuncInstance[callee.Fn]++
	} else {
		callee.Prog.FuncInstance[callee.Fn] = 0
	}
	callee.FuncDef.Name = fn.String()
	callee.id = callee.Prog.FuncInstance[callee.Fn]
	for i, param := range callee.Fn.Params {
		var argCaller ssa.Value
		if rcvr != nil {
			if i == 0 {
				argCaller = rcvr
			} else {
				argCaller = common.Args[i-1]
			}
		} else {
			argCaller = common.Args[i]
		}
		if _, ok := argCaller.Type().(*types.Chan); ok {
			callee.FuncDef.AddParams(&migo.Parameter{Caller: argCaller, Callee: param})
		}
		if inst, ok := caller.locals[argCaller]; ok {
			callee.locals[param] = inst
			callee.revlookup[argCaller.Name()] = param.Name()

			// Copy array and struct from parent.
			if elems, ok := caller.arrays[inst]; ok {
				callee.arrays[inst] = elems
			}
			if fields, ok := caller.structs[inst]; ok {
				callee.structs[inst] = fields
			}
			if maps, ok := caller.maps[inst]; ok {
				callee.maps[inst] = maps
			}
		} else if c, ok := argCaller.(*ssa.Const); ok {
			callee.locals[param] = &Const{c}
		}
	}

	if inst, ok := caller.locals[common.Value]; ok {
		if cap, ok := caller.Prog.closures[inst]; ok {
			for i, fv := range callee.Fn.FreeVars {
				callee.locals[fv] = cap[i]
				if _, ok := derefType(fv.Type()).(*types.Chan); ok {
					callee.FuncDef.AddParams(&migo.Parameter{Caller: fv, Callee: fv})
				}
			}
		}
	}
	return callee
}
Esempio n. 9
0
// FunctionName returns a unique function path and name.
// TODO refactor this code and everywhere it is called to remove duplication.
func FuncPathName(fn *ssa.Function) (path, name string) {
	rx := fn.Signature.Recv()
	pf := MakeID(rootProgram.Fset.Position(fn.Pos()).String()) //fmt.Sprintf("fn%d", fn.Pos())
	if rx != nil {                                             // it is not the name of a normal function, but that of a method, so append the method description
		pf = rx.Type().String() // NOTE no underlying()
	} else {
		if fn.Pkg != nil {
			pf = fn.Pkg.Object.Path() // was .Name(), but not unique
		}
	}
	return pf, fn.Name()
}
Esempio n. 10
0
func GetFnNameParts(fn *ssa.Function) (pack, nam string) {
	mName := fn.Name()
	pName, _ := FuncPathName(fn) //fmt.Sprintf("fn%d", fn.Pos()) //uintptr(unsafe.Pointer(fn)))
	if fn.Pkg != nil {
		if fn.Pkg.Object != nil {
			pName = fn.Pkg.Object.Path() // was .Name()
		}
	}
	if fn.Signature.Recv() != nil { // we have a method
		pName = fn.Signature.Recv().Pkg().Name() + ":" + fn.Signature.Recv().Type().String() // note no underlying()
		//pName = LanguageList[l].PackageOverloadReplace(pName)
	}
	return pName, mName
}
Esempio n. 11
0
func getFnPath(fn *ssa.Function) string {
	if fn == nil {
		//println("DEBUG getFnPath nil function")
		return ""
	}
	ob := fn.Object()
	if ob == nil {
		//println("DEBUG getFnPath nil object: name,synthetic=", fn.Name(), ",", fn.Synthetic)
		return ""
	}
	pk := ob.Pkg()
	if pk == nil {
		//println("DEBUG getFnPath nil package: name,synthetic=", fn.Name(), ",", fn.Synthetic)
		return ""
	}
	return pk.Path()
}
Esempio n. 12
0
// FuncPathName returns a unique function path and name.
func (comp *Compilation) FuncPathName(fn *ssa.Function) (path, name string) {
	rx := fn.Signature.Recv()
	pf := tgoutil.MakeID(comp.rootProgram.Fset.Position(fn.Pos()).String()) //fmt.Sprintf("fn%d", fn.Pos())
	if rx != nil {                                                          // it is not the name of a normal function, but that of a method, so append the method description
		pf = rx.Type().String() // NOTE no underlying()
	} else {
		if fn.Pkg != nil {
			pf = fn.Pkg.Object.Path() // was .Name(), but not unique
		} else {
			goroot := tgoutil.MakeID(LanguageList[comp.TargetLang].GOROOT + string(os.PathSeparator))
			pf1 := strings.Split(pf, goroot) // make auto-generated names shorter
			if len(pf1) == 2 {
				pf = pf1[1]
			} // TODO use GOPATH for names not in std pkgs
		}
	}
	return pf, fn.Name()
}
Esempio n. 13
0
func funcToken(fn *ssa.Function) token.Pos {
	switch syntax := fn.Syntax().(type) {
	case *ast.FuncLit:
		return syntax.Type.Func
	case *ast.FuncDecl:
		return syntax.Type.Func
	}
	return token.NoPos
}
Esempio n. 14
0
// findIntrinsic returns the constraint generation function for an
// intrinsic function fn, or nil if the function should be handled normally.
//
func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
	// Consult the *Function-keyed cache.
	// A cached nil indicates a normal non-intrinsic function.
	impl, ok := a.intrinsics[fn]
	if !ok {
		impl = intrinsicsByName[fn.String()] // may be nil

		if a.isReflect(fn) {
			if !a.config.Reflection {
				impl = ext۰NoEffect // reflection disabled
			} else if impl == nil {
				// Ensure all "reflect" code is treated intrinsically.
				impl = ext۰NotYetImplemented
			}
		}

		a.intrinsics[fn] = impl
	}
	return impl
}
Esempio n. 15
0
File: esc.go Progetto: hinike/llgo
func LowerAllocsToStack(f *ssa.Function) {
	pending := make([]ssa.Value, 0, 10)

	for _, b := range f.Blocks {
		for _, instr := range b.Instrs {
			if alloc, ok := instr.(*ssa.Alloc); ok && alloc.Heap && !escapes(alloc, alloc.Block(), pending) {
				alloc.Heap = false
				f.Locals = append(f.Locals, alloc)
			}
		}
	}
}
Esempio n. 16
0
// prettyFunc pretty-prints fn for the user interface.
// TODO(adonovan): return HTML so we have more markup freedom.
func prettyFunc(this *types.Package, fn *ssa.Function) string {
	if fn.Parent() != nil {
		return fmt.Sprintf("%s in %s",
			types.TypeString(fn.Signature, types.RelativeTo(this)),
			prettyFunc(this, fn.Parent()))
	}
	if fn.Synthetic != "" && fn.Name() == "init" {
		// (This is the actual initializer, not a declared 'func init').
		if fn.Pkg.Pkg == this {
			return "package initializer"
		}
		return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path())
	}
	return fn.RelString(this)
}
Esempio n. 17
0
File: ssa.go Progetto: hinike/llgo
func (u *unit) getFunctionLinkage(f *ssa.Function) llvm.Linkage {
	switch {
	case f.Pkg == nil:
		// Synthetic functions outside packages may appear in multiple packages.
		return llvm.LinkOnceODRLinkage

	case f.Parent() != nil:
		// Anonymous.
		return llvm.InternalLinkage

	case f.Signature.Recv() == nil && !ast.IsExported(f.Name()) &&
		!(f.Name() == "main" && f.Pkg.Object.Path() == "main") &&
		f.Name() != "init":
		// Unexported methods may be referenced as part of an interface method
		// table in another package. TODO(pcc): detect when this cannot happen.
		return llvm.InternalLinkage

	default:
		return llvm.ExternalLinkage
	}
}
Esempio n. 18
0
// callSSA interprets a call to function fn with arguments args,
// and lexical environment env, returning its result.
// callpos is the position of the callsite.
//
func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value {
	if i.mode&EnableTracing != 0 {
		fset := fn.Prog.Fset
		// TODO(adonovan): fix: loc() lies for external functions.
		fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos()))
		suffix := ""
		if caller != nil {
			suffix = ", resuming " + caller.fn.String() + loc(fset, callpos)
		}
		defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix)
	}
	fr := &frame{
		i:      i,
		caller: caller, // for panic/recover
		fn:     fn,
	}
	if fn.Parent() == nil {
		name := fn.String()
		if ext := externals[name]; ext != nil {
			if i.mode&EnableTracing != 0 {
				fmt.Fprintln(os.Stderr, "\t(external)")
			}
			return ext(fr, args)
		}
		if fn.Blocks == nil {
			panic("no code for function: " + name)
		}
	}
	fr.env = make(map[ssa.Value]value)
	fr.block = fn.Blocks[0]
	fr.locals = make([]value, len(fn.Locals))
	for i, l := range fn.Locals {
		fr.locals[i] = zero(deref(l.Type()))
		fr.env[l] = &fr.locals[i]
	}
	for i, p := range fn.Params {
		fr.env[p] = args[i]
	}
	for i, fv := range fn.FreeVars {
		fr.env[fv] = env[i]
	}
	for fr.block != nil {
		runFrame(fr)
	}
	// Destroy the locals to avoid accidental use after return.
	for i := range fn.Locals {
		fr.locals[i] = bad{}
	}
	return fr.result
}
Esempio n. 19
0
func checkFn(fn *ssa.Function, prog *ssa.Program, ptrResult *pointer.Result, roots []*ssa.Function) {
	nam := fn.String()
	if strings.HasPrefix(strings.TrimPrefix(nam, "("), *prefix) {
		hasPath := false
		usedExternally := false
		for _, r := range roots {
			if r != nil {
				nod, ok := ptrResult.CallGraph.Nodes[r]
				if ok {
					//fmt.Println("NODE root", r.Name())
					pth := callgraph.PathSearch(nod,
						func(n *callgraph.Node) bool {
							if n == nil {
								return false
							}
							if n.Func == fn {
								for _, ine := range n.In {
									if ine.Caller.Func.Pkg != fn.Pkg {
										//fmt.Println("DEBUG diff? ",
										//	ine.Caller.Func.Pkg, fn.Pkg)
										usedExternally = true
										break
									}
								}
								return true
							}
							return false
						})
					if pth != nil {
						//fmt.Printf("DEBUG path from %v to %v = %v\n",
						//	r, fn, pth)
						hasPath = true
						break
					}
				}
			}
		}
		isUpper := unicode.IsUpper(rune(fn.Name()[0]))
		pos := fn.Pos()
		//if strings.HasPrefix(nam, "(") && (!hasPath || (!usedExternally && isUpper)) {
		//	fmt.Println("bad Pos", pos, prog.Fset.Position(pos).String())
		//}
		loc := strings.TrimPrefix(
			prog.Fset.Position(pos).String(),
			gopath+"/src/"+*prefix+"/")
		showFuncResult(loc, nam, hasPath, usedExternally, isUpper)
	}
	wg.Done()
}
Esempio n. 20
0
func IsOverloaded(f *ssa.Function) bool {
	pn := "unknown" // Defensive, as some synthetic or other edge-case functions may not have a valid package name
	rx := f.Signature.Recv()
	if rx == nil { // ordinary function
		if f.Pkg != nil {
			if f.Pkg.Object != nil {
				pn = f.Pkg.Object.Path() //was .Name()
			}
		} else {
			if f.Object() != nil {
				if f.Object().Pkg() != nil {
					pn = f.Object().Pkg().Path() //was .Name()
				}
			}
		}
	} else { // determine the package information from the type description
		typ := rx.Type()
		ts := typ.String()
		if ts[0] == '*' {
			ts = ts[1:]
		}
		tss := strings.Split(ts, ".")
		if len(tss) >= 2 {
			ts = tss[len(tss)-2] // take the part before the final dot
		} else {
			ts = tss[0] // no dot!
		}
		pn = ts
	}
	tss := strings.Split(pn, "/") // TODO check this also works in Windows
	ts := tss[len(tss)-1]         // take the last part of the path
	pn = ts                       // TODO this is incorrect, but not currently a problem as there is no function overloading
	//println("DEBUG package name: " + pn)
	if LanguageList[TargetLang].FunctionOverloaded(pn, f.Name()) ||
		strings.HasPrefix(pn, "_") { // the package is not in the target language, signaled by a leading underscore and
		return true
	}
	return false
}
Esempio n. 21
0
func (l langType) FuncName(fnx *ssa.Function) string {
	pn := ""
	if fnx.Signature.Recv() != nil {
		pn = fnx.Signature.Recv().Type().String() // NOTE no use of underlying here
	} else {
		pn, _ = pogo.FuncPathName(fnx) //fmt.Sprintf("fn%d", fnx.Pos())
		fn := ssa.EnclosingFunction(fnx.Package(), []ast.Node{fnx.Syntax()})
		if fn == nil {
			fn = fnx
		}
		if fn.Pkg != nil {
			if fn.Pkg.Object != nil {
				pn = fn.Pkg.Object.Path() // was .Name()
			}
		} else {
			if fn.Object() != nil {
				if fn.Object().Pkg() != nil {
					pn = fn.Object().Pkg().Path() // was .Name()
				}
			}
		}
	}
	return l.LangName(pn, fnx.Name())
}
Esempio n. 22
0
File: ssa.go Progetto: hinike/llgo
func (u *unit) defineFunction(f *ssa.Function) {
	// Only define functions from this package, or synthetic
	// wrappers (which do not have a package).
	if f.Pkg != nil && f.Pkg != u.pkg {
		return
	}

	llfn := u.resolveFunctionGlobal(f)
	linkage := u.getFunctionLinkage(f)

	isMethod := f.Signature.Recv() != nil

	// Methods cannot be referred to via a descriptor.
	if !isMethod {
		llfd := u.resolveFunctionDescriptorGlobal(f)
		llfd.SetInitializer(llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0)))
		llfd.SetLinkage(linkage)
	}

	// We only need to emit a descriptor for functions without bodies.
	if len(f.Blocks) == 0 {
		return
	}

	ssaopt.LowerAllocsToStack(f)

	if u.DumpSSA {
		f.WriteTo(os.Stderr)
	}

	fr := newFrame(u, llfn)
	defer fr.dispose()
	fr.addCommonFunctionAttrs(fr.function)
	fr.function.SetLinkage(linkage)

	fr.logf("Define function: %s", f.String())
	fti := u.llvmtypes.getSignatureInfo(f.Signature)
	delete(u.undefinedFuncs, f)
	fr.retInf = fti.retInf

	// Push the compile unit and function onto the debug context.
	if u.GenerateDebug {
		u.debug.PushFunction(fr.function, f.Signature, f.Pos())
		defer u.debug.PopFunction()
		u.debug.SetLocation(fr.builder, f.Pos())
	}

	// If a function calls recover, we create a separate function to
	// hold the real function, and this function calls __go_can_recover
	// and bridges to it.
	if callsRecover(f) {
		fr = fr.bridgeRecoverFunc(fr.function, fti)
	}

	fr.blocks = make([]llvm.BasicBlock, len(f.Blocks))
	fr.lastBlocks = make([]llvm.BasicBlock, len(f.Blocks))
	for i, block := range f.Blocks {
		fr.blocks[i] = llvm.AddBasicBlock(fr.function, fmt.Sprintf(".%d.%s", i, block.Comment))
	}
	fr.builder.SetInsertPointAtEnd(fr.blocks[0])

	prologueBlock := llvm.InsertBasicBlock(fr.blocks[0], "prologue")
	fr.builder.SetInsertPointAtEnd(prologueBlock)

	// Map parameter positions to indices. We use this
	// when processing locals to map back to parameters
	// when generating debug metadata.
	paramPos := make(map[token.Pos]int)
	for i, param := range f.Params {
		paramPos[param.Pos()] = i
		llparam := fti.argInfos[i].decode(llvm.GlobalContext(), fr.builder, fr.builder)
		if isMethod && i == 0 {
			if _, ok := param.Type().Underlying().(*types.Pointer); !ok {
				llparam = fr.builder.CreateBitCast(llparam, llvm.PointerType(fr.types.ToLLVM(param.Type()), 0), "")
				llparam = fr.builder.CreateLoad(llparam, "")
			}
		}
		fr.env[param] = newValue(llparam, param.Type())
	}

	// Load closure, extract free vars.
	if len(f.FreeVars) > 0 {
		for _, fv := range f.FreeVars {
			fr.env[fv] = newValue(llvm.ConstNull(u.llvmtypes.ToLLVM(fv.Type())), fv.Type())
		}
		elemTypes := make([]llvm.Type, len(f.FreeVars)+1)
		elemTypes[0] = llvm.PointerType(llvm.Int8Type(), 0) // function pointer
		for i, fv := range f.FreeVars {
			elemTypes[i+1] = u.llvmtypes.ToLLVM(fv.Type())
		}
		structType := llvm.StructType(elemTypes, false)
		closure := fr.runtime.getClosure.call(fr)[0]
		closure = fr.builder.CreateBitCast(closure, llvm.PointerType(structType, 0), "")
		for i, fv := range f.FreeVars {
			ptr := fr.builder.CreateStructGEP(closure, i+1, "")
			ptr = fr.builder.CreateLoad(ptr, "")
			fr.env[fv] = newValue(ptr, fv.Type())
		}
	}

	// Allocate stack space for locals in the prologue block.
	for _, local := range f.Locals {
		typ := fr.llvmtypes.ToLLVM(deref(local.Type()))
		alloca := fr.builder.CreateAlloca(typ, local.Comment)
		fr.memsetZero(alloca, llvm.SizeOf(typ))
		bcalloca := fr.builder.CreateBitCast(alloca, llvm.PointerType(llvm.Int8Type(), 0), "")
		value := newValue(bcalloca, local.Type())
		fr.env[local] = value
		if fr.GenerateDebug {
			paramIndex, ok := paramPos[local.Pos()]
			if !ok {
				paramIndex = -1
			}
			fr.debug.Declare(fr.builder, local, alloca, paramIndex)
		}
	}

	// If this is the "init" function, enable init-specific optimizations.
	if !isMethod && f.Name() == "init" {
		fr.isInit = true
	}

	// If the function contains any defers, we must first create
	// an unwind block. We can short-circuit the check for defers with
	// f.Recover != nil.
	if f.Recover != nil || hasDefer(f) {
		fr.unwindBlock = llvm.AddBasicBlock(fr.function, "")
		fr.frameptr = fr.builder.CreateAlloca(llvm.Int8Type(), "")
	}

	term := fr.builder.CreateBr(fr.blocks[0])
	fr.allocaBuilder.SetInsertPointBefore(term)

	for _, block := range f.DomPreorder() {
		fr.translateBlock(block, fr.blocks[block.Index])
	}

	fr.fixupPhis()

	if !fr.unwindBlock.IsNil() {
		fr.setupUnwindBlock(f.Recover, f.Signature.Results())
	}

	// The init function needs to register the GC roots first. We do this
	// after generating code for it because allocations may have caused
	// additional GC roots to be created.
	if fr.isInit {
		fr.builder.SetInsertPointBefore(prologueBlock.FirstInstruction())
		fr.registerGcRoots()
	}
}
Esempio n. 23
0
func (visit *visitor) function(fn *ssa.Function, isOvl isOverloaded) {
	if !visit.seen[fn] { // been, exists := visit.seen[fn]; !been || !exists {
		vprintln("DEBUG 1st visit to: ", fn.String())
		visit.seen[fn] = true
		visit.usesGR[fn] = false
		if isOvl(fn) {
			vprintln("DEBUG overloaded: ", fn.String())
			return
		}
		if len(fn.Blocks) == 0 { // exclude functions that reference C/assembler code
			// NOTE: not marked as seen, because we don't want to include in output
			// if used, the symbol will be included in the golibruntime replacement packages
			// TODO review
			vprintln("DEBUG no code for: ", fn.String())
			return // external functions cannot use goroutines
		}
		var buf [10]*ssa.Value // avoid alloc in common case
		for _, b := range fn.Blocks {
			for _, instr := range b.Instrs {
				for _, op := range instr.Operands(buf[:0]) {
					areRecursing := false
					afn, isFn := (*op).(*ssa.Function)
					if isFn {
						if afn == fn {
							areRecursing = true
						}
						visit.function(afn, isOvl)
						if visit.usesGR[afn] {
							vprintln("marked as using GR because referenced func uses GR")
							visit.usesGR[fn] = true
						}
						vprintln(fn.Name(), " calls ", afn.Name())
					}
					// TODO, review if this code should be included
					if !visit.usesGR[fn] {
						if _, ok := (*op).(ssa.Value); ok {
							typ := (*op).Type()
							typ = DeRefUl(typ)
							switch typ.(type) {
							// TODO use oracle techniques to determine which interfaces or functions may require GR
							case *types.Chan, *types.Interface:
								visit.usesGR[fn] = true // may be too conservative
								vprintln("marked as using GR because uses Chan/Interface")
							case *types.Signature:
								if !areRecursing {
									if !isFn {
										visit.usesGR[fn] = true
										vprintln("marked as using GR because uses Signature")
									}
								}
							}
						}
					}
				}
				if _, ok := instr.(*ssa.Call); ok {
					switch instr.(*ssa.Call).Call.Value.(type) {
					case *ssa.Builtin:
						//NoOp
					default:
						cc := instr.(*ssa.Call).Common()
						if cc != nil {
							afn := cc.StaticCallee()
							if afn != nil {
								visit.function(afn, isOvl)
								if visit.usesGR[afn] {
									visit.usesGR[fn] = true
									vprintln("marked as using GR because call target uses GR")
								}
								vprintln(fn.Name(), " calls ", afn.Name())
							}
						}
					}
				}
				if !visit.usesGR[fn] {
					switch instr.(type) {
					case *ssa.Go, *ssa.MakeChan, *ssa.Defer, *ssa.Panic,
						*ssa.Send, *ssa.Select:
						vprintln("usesGR because uses Go...", fn.Name())
						visit.usesGR[fn] = true
					case *ssa.UnOp:
						if instr.(*ssa.UnOp).Op.String() == "<-" {
							vprintln("usesGR because uses <-", fn.Name())
							visit.usesGR[fn] = true
						}
					}
				}
			}
		}
	}
}
Esempio n. 24
0
// Emit a particular function.
func emitFunc(fn *ssa.Function) {

	/* TODO research if the ssautil.Switches() function can be incorporated to provide any run-time improvement to the code
	// it would give a big adavantage, but only to a very small number of functions - so definately TODO
	sw := ssautil.Switches(fn)
		fmt.Printf("DEBUG Switches for : %s \n", fn)
	for num,swi := range sw {
		fmt.Printf("DEBUG Switches[%d]= %+v\n", num, swi)
	}
	*/

	var subFnList []subFnInstrs        // where the sub-functions are
	canOptMap := make(map[string]bool) // TODO review use of this mechanism

	//println("DEBUG processing function: ", fn.Name())
	MakePosHash(fn.Pos()) // mark that we have entered a function
	trackPhi := true
	switch len(fn.Blocks) {
	case 0: // NoOp - only output a function if it has a body... so ignore pure definitions (target language may generate an error, if truely undef)
		//fmt.Printf("DEBUG function has no body, ignored: %v %v \n", fn.Name(), fn.String())
	case 1: // Only one block, so no Phi tracking required
		trackPhi = false
		fallthrough
	default:
		if trackPhi {
			// check that there actually are Phi instructions to track
			trackPhi = false
		phiSearch:
			for b := range fn.Blocks {
				for i := range fn.Blocks[b].Instrs {
					_, trackPhi = fn.Blocks[b].Instrs[i].(*ssa.Phi)
					if trackPhi {
						break phiSearch
					}
				}
			}
		}
		instrCount := 0
		for b := range fn.Blocks {
			instrCount += len(fn.Blocks[b].Instrs)
		}
		mustSplitCode := false
		if instrCount > LanguageList[TargetLang].InstructionLimit {
			//println("DEBUG mustSplitCode => large function length:", instrCount, " in ", fn.Name())
			mustSplitCode = true
		}
		blks := fn.DomPreorder() // was fn.Blocks
		for b := range blks {    // go though the blocks looking for sub-functions
			instrsEmitted := 0
			inSubFn := false
			for i := range blks[b].Instrs {
				canPutInSubFn := true
				in := blks[b].Instrs[i]
				switch in.(type) {
				case *ssa.Phi: // phi uses self-referential temp vars that must be pre-initialised
					canPutInSubFn = false
				case *ssa.Return:
					canPutInSubFn = false
				case *ssa.Call:
					switch in.(*ssa.Call).Call.Value.(type) {
					case *ssa.Builtin:
						//NoOp
					default:
						canPutInSubFn = false
					}
				case *ssa.Select, *ssa.Send, *ssa.Defer, *ssa.RunDefers, *ssa.Panic:
					canPutInSubFn = false
				case *ssa.UnOp:
					if in.(*ssa.UnOp).Op == token.ARROW {
						canPutInSubFn = false
					}
				}
				if canPutInSubFn {
					if inSubFn {
						if instrsEmitted > LanguageList[TargetLang].SubFnInstructionLimit {
							subFnList[len(subFnList)-1].end = i
							subFnList = append(subFnList, subFnInstrs{b, i, 0})
							instrsEmitted = 0
						}
					} else {
						subFnList = append(subFnList, subFnInstrs{b, i, 0})
						inSubFn = true
					}
				} else {
					if inSubFn {
						subFnList[len(subFnList)-1].end = i
						inSubFn = false
					}
				}
				instrsEmitted++
			}
			if inSubFn {
				subFnList[len(subFnList)-1].end = len(blks[b].Instrs)
			}
		}
		for sf := range subFnList { // go though the sub-functions looking for optimisable temp vars
			var instrMap = make(map[ssa.Instruction]bool)
			for ii := subFnList[sf].start; ii < subFnList[sf].end; ii++ {
				instrMap[blks[subFnList[sf].block].Instrs[ii]] = true
			}

			for i := subFnList[sf].start; i < subFnList[sf].end; i++ {
				instrVal, hasVal := blks[subFnList[sf].block].Instrs[i].(ssa.Value)
				if hasVal {
					refs := *blks[subFnList[sf].block].Instrs[i].(ssa.Value).Referrers()
					switch len(refs) {
					case 0: // no other instruction uses the result of this one
					default: //multiple usage of the register
						canOpt := true
						for r := range refs {
							user := refs[r]
							if user.Block() != blks[subFnList[sf].block] {
								canOpt = false
								break
							}
							_, inRange := instrMap[user]
							if !inRange {
								canOpt = false
								break
							}
						}
						if canOpt &&
							!LanguageList[TargetLang].CanInline(blks[subFnList[sf].block].Instrs[i]) {
							canOptMap[instrVal.Name()] = true
						}
					}
				}
			}
		}

		reconstruct := tgossa.Reconstruct(blks, grMap[fn] || mustSplitCode)
		if reconstruct != nil {
			//fmt.Printf("DEBUG reconstruct %s %#v\n",fn.String(),reconstruct)
		}

		emitFuncStart(fn, blks, trackPhi, canOptMap, mustSplitCode, reconstruct)
		thisSubFn := 0
		for b := range blks {
			emitPhi := trackPhi
			emitBlockStart(blks, b, emitPhi)
			inSubFn := false
			for i := 0; i < len(blks[b].Instrs); i++ {
				if thisSubFn >= 0 && thisSubFn < len(subFnList) { // not at the end of the list
					if b == subFnList[thisSubFn].block {
						if i >= subFnList[thisSubFn].end && inSubFn {
							inSubFn = false
							thisSubFn++
							if thisSubFn >= len(subFnList) {
								thisSubFn = -1 // we have come to the end of the list
							}
						}
					}
				}
				if thisSubFn >= 0 && thisSubFn < len(subFnList) { // not at the end of the list
					if b == subFnList[thisSubFn].block {
						if i == subFnList[thisSubFn].start {
							inSubFn = true
							l := TargetLang
							if mustSplitCode {
								fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].SubFnCall(thisSubFn))
							} else {
								emitSubFn(fn, blks, subFnList, thisSubFn, mustSplitCode, canOptMap)
							}
						}
					}
				}
				if !inSubFn {
					// optimize phi case statements
					phiList := 0
				phiLoop:
					switch blks[b].Instrs[i+phiList].(type) {
					case *ssa.Phi:
						if len(*blks[b].Instrs[i+phiList].(*ssa.Phi).Referrers()) > 0 {
							phiList++
							if (i + phiList) < len(blks[b].Instrs) {
								goto phiLoop
							}
						}
					}
					if phiList > 0 {
						peephole(blks[b].Instrs[i : i+phiList])
						i += phiList - 1
					} else {
						emitPhi = emitInstruction(blks[b].Instrs[i],
							blks[b].Instrs[i].Operands(make([]*ssa.Value, 0)))
					}
				}
			}
			if thisSubFn >= 0 && thisSubFn < len(subFnList) { // not at the end of the list
				if b == subFnList[thisSubFn].block {
					if inSubFn {
						thisSubFn++
						if thisSubFn >= len(subFnList) {
							thisSubFn = -1 // we have come to the end of the list
						}
					}
				}
			}
			emitBlockEnd(blks, b, emitPhi && trackPhi)
		}
		emitRunEnd(fn)
		if mustSplitCode {
			for sf := range subFnList {
				emitSubFn(fn, blks, subFnList, sf, mustSplitCode, canOptMap)
			}
		}
		emitFuncEnd(fn)
	}
}