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