// Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ssa import ( "cmd/internal/obj" "fmt" "strings" ) type SlotID int32 // A FuncDebug contains all the debug information for the variables in a // function. Variables are identified by their LocalSlot, which may be the // result of decomposing a larger variable. type FuncDebug struct { // Slots is all the slots used in the debug info, indexed by their SlotID. // Use this when getting a LocalSlot from a SlotID. Slots []*LocalSlot // VarSlots is the slots that represent part of user variables. // Use this when iterating over all the slots to generate debug information. VarSlots []*LocalSlot // The blocks in the function, in program text order. Blocks []*BlockDebug // The registers of the current architecture, indexed by Register.num. Registers []Register } func (f *FuncDebug) BlockString(b *BlockDebug) string { var vars []string for slot := range f.VarSlots { if len(b.Variables[slot].Locations) == 0 { continue } vars = append(vars, fmt.Sprintf("%v = %v", f.Slots[slot], b.Variables[slot])) } return fmt.Sprintf("{%v}", strings.Join(vars, ", ")) } func (f *FuncDebug) SlotLocsString(id SlotID) string { var locs []string for _, block := range f.Blocks { for _, loc := range block.Variables[id].Locations { locs = append(locs, block.LocString(loc)) } } return strings.Join(locs, " ") } type BlockDebug struct { // The SSA block that this tracks. For debug logging only. Block *Block // The variables in this block, indexed by their SlotID. Variables []VarLocList } func (b *BlockDebug) LocString(loc *VarLoc) string { registers := b.Block.Func.Config.registers var storage []string if loc.OnStack { storage = append(storage, "stack") } for reg := 0; reg < 64; reg++ { if loc.Registers&(1<= 0; i-- { b := po[i] // Build the starting state for the block from the final // state of its predecessors. locs := state.mergePredecessors(b, blockLocs) if state.loggingEnabled { state.logf("Processing %v, initial locs %v, regs %v\n", b, state.BlockString(locs), state.registerContents) } // Update locs/registers with the effects of each Value. // The location list generated here needs to be slightly adjusted for use by gdb. // These adjustments are applied in genssa. for _, v := range b.Values { slots := valueNames[v.ID] // Loads and stores inherit the names of their sources. var source *Value switch v.Op { case OpStoreReg: source = v.Args[0] case OpLoadReg: switch a := v.Args[0]; a.Op { case OpArg: source = a case OpStoreReg: source = a.Args[0] default: state.unexpected(v, "load with unexpected source op %v", a) } } if source != nil { slots = append(slots, valueNames[source.ID]...) // As of writing, the compiler never uses a load/store as a // source of another load/store, so there's no reason this should // ever be consulted. Update just in case, and so that when // valueNames is cached, we can reuse the memory. valueNames[v.ID] = slots } if len(slots) == 0 { continue } reg, _ := f.getHome(v.ID).(*Register) state.processValue(locs, v, slots, reg) } // The block is done; mark any live locations as ending with the block. for _, locList := range locs.Variables { last := locList.last() if last == nil || last.End != nil { continue } last.End = BlockEnd } if state.loggingEnabled { f.Logf("Block done: locs %v, regs %v\n", state.BlockString(locs), state.registerContents) } blockLocs[b.ID] = locs } info := &FuncDebug{ Slots: state.slots, VarSlots: state.varSlots, Registers: f.Config.registers, } // Consumers want the information in textual order, not by block ID. for _, b := range f.Blocks { info.Blocks = append(info.Blocks, blockLocs[b.ID]) } if state.loggingEnabled { f.Logf("Final result:\n") for slot := range info.VarSlots { f.Logf("\t%v => %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot))) } } return info } // isSynthetic reports whether if slot represents a compiler-inserted variable, // e.g. an autotmp or an anonymous return value that needed a stack slot. func isSynthetic(slot *LocalSlot) bool { c := slot.String()[0] return c == '.' || c == '~' } // mergePredecessors takes the end state of each of b's predecessors and // intersects them to form the starting state for b. // The registers slice (the second return value) will be reused for each call to mergePredecessors. func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *BlockDebug { live := make([]VarLocList, len(state.slots)) // Filter out back branches. var preds []*Block for _, pred := range b.Preds { if blockLocs[pred.b.ID] != nil { preds = append(preds, pred.b) } } if len(preds) > 0 { p := preds[0] for slot, locList := range blockLocs[p.ID].Variables { last := locList.last() if last == nil || last.End != BlockEnd { continue } loc := state.cache.NewVarLoc() loc.Start = BlockStart loc.OnStack = last.OnStack loc.StackLocation = last.StackLocation loc.Registers = last.Registers live[slot].append(loc) } } if state.loggingEnabled && len(b.Preds) > 1 { state.logf("Starting merge with state from %v: %v\n", b.Preds[0].b, state.BlockString(blockLocs[b.Preds[0].b.ID])) } for i := 1; i < len(preds); i++ { p := preds[i] if state.loggingEnabled { state.logf("Merging in state from %v: %v &= %v\n", p, live, state.BlockString(blockLocs[p.ID])) } for slot, liveVar := range live { liveLoc := liveVar.last() if liveLoc == nil { continue } predLoc := blockLocs[p.ID].Variables[SlotID(slot)].last() // Clear out slots missing/dead in p. if predLoc == nil || predLoc.End != BlockEnd { live[slot].Locations = nil continue } // Unify storage locations. if !liveLoc.OnStack || !predLoc.OnStack || liveLoc.StackLocation != predLoc.StackLocation { liveLoc.OnStack = false liveLoc.StackLocation = 0 } liveLoc.Registers &= predLoc.Registers } } // Create final result. locs := &BlockDebug{Variables: live} if state.loggingEnabled { locs.Block = b } for reg := range state.registerContents { state.registerContents[reg] = state.registerContents[reg][:0] } for slot, locList := range live { loc := locList.last() if loc == nil { continue } for reg := 0; reg < state.numRegisters; reg++ { if loc.Registers&(1<