// 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 }
// 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 }
// 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) }
// 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)) }
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 }
// 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 }
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 }
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()) }
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]) } }
// 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) } }