Example #1
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
}
Example #2
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.Enclosing == 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
}
Example #3
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.Enclosing != nil {
		return fmt.Sprintf("%s in %s",
			types.TypeString(this, fn.Signature),
			prettyFunc(this, fn.Enclosing))
	}
	if fn.Synthetic != "" && fn.Name() == "init" {
		// (This is the actual initializer, not a declared 'func init').
		if fn.Pkg.Object == this {
			return "package initializer"
		}
		return fmt.Sprintf("%q package initializer", fn.Pkg.Object.Path())
	}
	return fn.RelString(this)
}
Example #4
0
// Emit the start of a function.
func emitFuncStart(fn *ssa.Function, trackPhi bool, canOptMap map[string]bool) {
	posStr := CodePosition(fn.Pos())
	pName := "unknown" // TODO review why this code appears to duplicate that at the start of emitFunctions()
	if fn.Pkg != nil {
		if fn.Pkg.Object != nil {
			pName = fn.Pkg.Object.Name()
		}
	}
	mName := fn.Name()
	if fn.Signature.Recv() != nil { // we have a method
		pName = fn.Signature.Recv().Type().String() // note no underlying()
	}
	isPublic := unicode.IsUpper(rune(mName[0])) // TODO check rules for non-ASCII 1st characters and fix
	l := TargetLang
	fmt.Fprintln(&LanguageList[l].buffer,
		LanguageList[l].FuncStart(pName, mName, fn, posStr, isPublic, trackPhi, canOptMap))
}
Example #5
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
}
Example #6
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
}
Example #7
0
File: ssa.go Project: minux/llgo
func (u *unit) resolveFunction(f *ssa.Function) *LLVMValue {
	if v, ok := u.globals[f]; ok {
		return v
	}
	name := f.String()
	if f.Enclosing != nil {
		// Anonymous functions are not guaranteed to
		// have unique identifiers at the global scope.
		name = f.Enclosing.String() + ":" + name
	}
	// It's possible that the function already exists in the module;
	// for example, if it's a runtime intrinsic that the compiler
	// has already referenced.
	llvmFunction := u.module.Module.NamedFunction(name)
	if llvmFunction.IsNil() {
		llvmType := u.llvmtypes.ToLLVM(f.Signature)
		llvmType = llvmType.StructElementTypes()[0].ElementType()
		if len(f.FreeVars) > 0 {
			// Add an implicit first argument.
			returnType := llvmType.ReturnType()
			paramTypes := llvmType.ParamTypes()
			vararg := llvmType.IsFunctionVarArg()
			blockElementTypes := make([]llvm.Type, len(f.FreeVars))
			for i, fv := range f.FreeVars {
				blockElementTypes[i] = u.llvmtypes.ToLLVM(fv.Type())
			}
			blockType := llvm.StructType(blockElementTypes, false)
			blockPtrType := llvm.PointerType(blockType, 0)
			paramTypes = append([]llvm.Type{blockPtrType}, paramTypes...)
			llvmType = llvm.FunctionType(returnType, paramTypes, vararg)
		}
		llvmFunction = llvm.AddFunction(u.module.Module, name, llvmType)
		if f.Enclosing != nil {
			llvmFunction.SetLinkage(llvm.PrivateLinkage)
		}
		u.undefinedFuncs[f] = true
	}
	v := u.NewValue(llvmFunction, f.Signature)
	u.globals[f] = v
	return v
}
Example #8
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 = "unknown"
		fn := fnx
		if fn.Enclosing != nil {
			fn = fn.Enclosing
		}
		if fn.Pkg != nil {
			if fn.Pkg.Object != nil {
				pn = fn.Pkg.Object.Name()
			}
		} else {
			if fn.Object() != nil {
				if fn.Object().Pkg() != nil {
					pn = fn.Object().Pkg().Name()
				}
			}
		}
	}
	return l.LangName(pn, fnx.Name())
}
Example #9
0
File: ssa.go Project: minux/llgo
func (u *unit) defineFunction(f *ssa.Function) {
	// Nothing to do for functions without bodies.
	if len(f.Blocks) == 0 {
		return
	}

	// Only define functions from this package.
	if f.Pkg == nil {
		if r := f.Signature.Recv(); r != nil {
			if r.Pkg() != nil && r.Pkg() != u.pkg.Object {
				return
			} else if named, ok := r.Type().(*types.Named); ok && named.Obj().Parent() == types.Universe {
				// This condition is true iff f is error.Error.
				if u.pkg.Object.Path() != "runtime" {
					return
				}
			}
		}
	} else if f.Pkg != u.pkg {
		return
	}

	fr := frame{
		unit:   u,
		blocks: make([]llvm.BasicBlock, len(f.Blocks)),
		env:    make(map[ssa.Value]*LLVMValue),
	}

	fr.logf("Define function: %s", f.String())
	llvmFunction := fr.resolveFunction(f).LLVMValue()
	delete(u.undefinedFuncs, f)

	// Push the function onto the debug context.
	// TODO(axw) create a fake CU for synthetic functions
	if u.GenerateDebug && f.Synthetic == "" {
		u.debug.pushFunctionContext(llvmFunction, f.Signature, f.Pos())
		defer u.debug.popFunctionContext()
		u.debug.setLocation(u.builder, f.Pos())
	}

	// Functions that call recover must not be inlined, or we
	// can't tell whether the recover call is valid at runtime.
	if f.Recover != nil {
		llvmFunction.AddFunctionAttr(llvm.NoInlineAttribute)
	}

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

	var paramOffset int
	if len(f.FreeVars) > 0 {
		// Extract captures from the first implicit parameter.
		arg0 := llvmFunction.Param(0)
		for i, fv := range f.FreeVars {
			addressPtr := fr.builder.CreateStructGEP(arg0, i, "")
			address := fr.builder.CreateLoad(addressPtr, "")
			fr.env[fv] = fr.NewValue(address, fv.Type())
		}
		paramOffset++
	}
	// 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 + paramOffset
		llparam := llvmFunction.Param(i + paramOffset)
		fr.env[param] = fr.NewValue(llparam, param.Type())
	}

	// Allocate stack space for locals in the prologue block.
	prologueBlock := llvm.InsertBasicBlock(fr.blocks[0], "prologue")
	fr.builder.SetInsertPointAtEnd(prologueBlock)
	for _, local := range f.Locals {
		typ := fr.llvmtypes.ToLLVM(deref(local.Type()))
		alloca := fr.builder.CreateAlloca(typ, local.Comment)
		u.memsetZero(alloca, llvm.SizeOf(typ))
		value := fr.NewValue(alloca, 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)
		}
	}

	// Move any allocs relating to named results from the entry block
	// to the prologue block, so they dominate the rundefers and recover
	// blocks.
	//
	// TODO(axw) ask adonovan for a cleaner way of doing this, e.g.
	// have ssa generate an entry block that defines Allocs and related
	// stores, and then a separate block for function body instructions.
	if f.Synthetic == "" {
		if results := f.Signature.Results(); results != nil {
			for i := 0; i < results.Len(); i++ {
				result := results.At(i)
				if result.Name() == "" {
					break
				}
				for i, instr := range f.Blocks[0].Instrs {
					if instr, ok := instr.(*ssa.Alloc); ok && instr.Heap && instr.Pos() == result.Pos() {
						fr.instruction(instr)
						instrs := f.Blocks[0].Instrs
						instrs = append(instrs[:i], instrs[i+1:]...)
						f.Blocks[0].Instrs = instrs
						break
					}
				}
			}
		}
	}

	// If the function contains any defers, we must first call
	// setjmp so we can call rundefers in response to a panic.
	// We can short-circuit the check for defers with
	// f.Recover != nil.
	if f.Recover != nil || hasDefer(f) {
		rdblock := llvm.AddBasicBlock(llvmFunction, "rundefers")
		defers := fr.builder.CreateAlloca(fr.runtime.defers.llvm, "")
		fr.builder.CreateCall(fr.runtime.initdefers.LLVMValue(), []llvm.Value{defers}, "")
		jb := fr.builder.CreateStructGEP(defers, 0, "")
		jb = fr.builder.CreateBitCast(jb, llvm.PointerType(llvm.Int8Type(), 0), "")
		result := fr.builder.CreateCall(fr.runtime.setjmp.LLVMValue(), []llvm.Value{jb}, "")
		result = fr.builder.CreateIsNotNull(result, "")
		fr.builder.CreateCondBr(result, rdblock, fr.blocks[0])
		// We'll only get here via a panic, which must either be
		// recovered or continue panicking up the stack without
		// returning from "rundefers". The recover block may be
		// nil even if we can recover, in which case we just need
		// to return the zero value for each result (if any).
		var recoverBlock llvm.BasicBlock
		if f.Recover != nil {
			recoverBlock = fr.block(f.Recover)
		} else {
			recoverBlock = llvm.AddBasicBlock(llvmFunction, "recover")
			fr.builder.SetInsertPointAtEnd(recoverBlock)
			var nresults int
			results := f.Signature.Results()
			if results != nil {
				nresults = results.Len()
			}
			switch nresults {
			case 0:
				fr.builder.CreateRetVoid()
			case 1:
				fr.builder.CreateRet(llvm.ConstNull(fr.llvmtypes.ToLLVM(results.At(0).Type())))
			default:
				values := make([]llvm.Value, nresults)
				for i := range values {
					values[i] = llvm.ConstNull(fr.llvmtypes.ToLLVM(results.At(i).Type()))
				}
				fr.builder.CreateAggregateRet(values)
			}
		}
		fr.builder.SetInsertPointAtEnd(rdblock)
		fr.builder.CreateCall(fr.runtime.rundefers.LLVMValue(), nil, "")
		fr.builder.CreateBr(recoverBlock)
	} else {
		fr.builder.CreateBr(fr.blocks[0])
	}

	for i, block := range f.Blocks {
		fr.translateBlock(block, fr.blocks[i])
	}
}
Example #10
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
	sw := ssautil.Switches(fn)
	if len(sw) > 0 {
		fmt.Printf("DEBUG Switches: %s = %+v\n", fn, sw)
	}
	*/

	subFnList := make([]subFnInstrs, 0)
	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
		}
		for b := range fn.Blocks { // go though the blocks looking for sub-functions
			instrsEmitted := 0
			inSubFn := false
			for i := range fn.Blocks[b].Instrs {
				canPutInSubFn := true
				in := fn.Blocks[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(fn.Blocks[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[fn.Blocks[subFnList[sf].block].Instrs[ii]] = true
			}

			for i := subFnList[sf].start; i < subFnList[sf].end; i++ {
				instrVal, hasVal := fn.Blocks[subFnList[sf].block].Instrs[i].(ssa.Value)
				if hasVal {
					refs := *fn.Blocks[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() != fn.Blocks[subFnList[sf].block] {
								canOpt = false
								break
							}
							_, inRange := instrMap[user]
							if !inRange {
								canOpt = false
								break
							}
						}
						if canOpt {
							canOptMap[instrVal.Name()] = true
						}
					}
				}
			}
		}

		emitFuncStart(fn, trackPhi, canOptMap)
		thisSubFn := 0
		for b := range fn.Blocks {
			emitBlockStart(fn.Blocks, b)
			emitPhi := trackPhi
			inSubFn := false
			for i := range fn.Blocks[b].Instrs {
				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
							fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].SubFnCall(thisSubFn))
						}
					}
				}
				if !inSubFn {
					emitPhi = emitInstruction(fn.Blocks[b].Instrs[i], fn.Blocks[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(fn.Blocks, b, emitPhi && trackPhi)
		}
		emitRunEnd(fn)
		for sf := range subFnList {
			l := TargetLang
			fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].SubFnStart(sf, mustSplitCode))
			for i := subFnList[sf].start; i < subFnList[sf].end; i++ {
				instrVal, hasVal := fn.Blocks[subFnList[sf].block].Instrs[i].(ssa.Value)
				if hasVal {
					if canOptMap[instrVal.Name()] == true {
						l := TargetLang
						fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].DeclareTempVar(instrVal))
					}
				}
			}
			for i := subFnList[sf].start; i < subFnList[sf].end; i++ {
				emitInstruction(fn.Blocks[subFnList[sf].block].Instrs[i],
					fn.Blocks[subFnList[sf].block].Instrs[i].Operands(make([]*ssa.Value, 0)))
			}
			fmt.Fprintln(&LanguageList[l].buffer, LanguageList[l].SubFnEnd(sf))
		}
		emitFuncEnd(fn)
	}
}