+++ /dev/null
-// Copyright 2015 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 main
-
-// Stub package for testing ssa compiler backend. Will eventually
-// be deleted when ssa is called directly from the main compiler.
-
-import (
- "bufio"
- "flag"
- "fmt"
- "io"
- "os"
- "strconv"
- "strings"
-
- "cmd/internal/ssa"
-)
-
-// testing harness which runs the compiler using an IR read from a file
-func main() {
- flag.Parse()
- file := flag.Arg(0)
- r, err := os.Open(file)
- if err != nil {
- panic(err)
- }
- f := buildFunc(readFunc(r))
- ssa.Compile(f)
- // TODO: output f
-}
-
-// readFunc reads the intermediate representation generated by the
-// compiler frontend and returns it as a list of sexpressions.
-func readFunc(r io.Reader) []sexpr {
- var lines []sexpr
- s := bufio.NewScanner(r)
- for s.Scan() {
- line := s.Text()
- e := parseSexpr(strings.Trim(line, " "))
-
- if !e.compound {
- panic("bad stmt: " + line)
- }
- if e.parts[0].compound {
- panic("bad op: " + line)
- }
- lines = append(lines, e)
- }
- return lines
-}
-
-// buildFunc converts from the 6g IR dump format to the internal
-// form. Builds SSA and all that.
-func buildFunc(lines []sexpr) *ssa.Func {
- f := new(ssa.Func)
-
- // We construct SSA using an algorithm similar to
- // Brau, Buchwald, Hack, Leißa, Mallon, and Zwinkau
- // http://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf
-
- // allocate starting block
- f.Entry = f.NewBlock(ssa.BlockPlain)
- // TODO: all args. Make a struct containing args/returnvals, declare
- // an FP which contains a pointer to that struct.
-
- var exit *ssa.Block // all returns (if any) branch to here TODO: defers & panics?
-
- // add a block for each label
- // Also a few other preprocessing steps, all in one pass.
- labels := map[string]*ssa.Block{}
- types := map[string]ssa.Type{}
- callFallthrough := map[int]*ssa.Block{}
- for i, e := range lines {
- switch e.parts[0].name {
- case "LABEL":
- labels[e.parts[1].name] = f.NewBlock(ssa.BlockPlain)
- case "NAME":
- f.Name = e.parts[1].name
- case "RETURN":
- if exit == nil {
- exit = f.NewBlock(ssa.BlockExit)
- }
- case "TYPE":
- types[e.parts[1].name] = parseSexprType(e.parts[2])
- case "CALL":
- // allocate a new block for fallthrough
- callFallthrough[i] = f.NewBlock(ssa.BlockPlain)
- if exit == nil {
- exit = f.NewBlock(ssa.BlockExit)
- }
- }
- }
-
- // map from block id to sexprs in that block
- blocklines := make([][]sexpr, f.NumBlocks())
-
- // Add sexprs to the correct block. Add edges between blocks.
- b := f.Entry
- var i int
- for j, e := range lines {
- if b == nil && e.parts[0].name != "LABEL" {
- // dead code (e.g. return in "if" branch makes the "goto end" statement dead)
- continue
- }
- switch e.parts[0].name {
- case "IF":
- if b.Kind != ssa.BlockPlain {
- panic("bad b state")
- }
- b.Kind = ssa.BlockIf
- edge(b, labels[e.parts[2].name])
- edge(b, labels[e.parts[3].name])
- blocklines[b.ID] = lines[i : j+1]
- b = nil
- case "GOTO":
- edge(b, labels[e.parts[1].name])
- blocklines[b.ID] = lines[i:j]
- b = nil
- case "LABEL":
- b = labels[e.parts[1].name]
- i = j + 1
- case "RETURN":
- if b.Kind != ssa.BlockPlain {
- panic("bad b state")
- }
- edge(b, exit)
- blocklines[b.ID] = lines[i:j]
- b = nil
- case "CALL":
- if b.Kind != ssa.BlockPlain {
- panic("bad b state")
- }
- b.Kind = ssa.BlockCall
- c := callFallthrough[j]
- edge(b, c)
- edge(b, exit)
- blocklines[b.ID] = lines[i : j+1]
- b = c
- i = j + 1
- }
- // note that we don't keep goto/label/return sexprs
- }
- if b != nil {
- panic("control flow falls off end of function")
- }
-
- // Read types for each variable
- // Number the variables densely
- varids := map[string]int{} // map from variable name to id
- var varnames []string // map from id to variable name
- var vartypes []ssa.Type // map from variable id to type
- for _, e := range lines {
- if e.parts[0].name != "DCL" {
- continue
- }
- name := e.parts[1].name
- if _, ok := varids[name]; ok {
- continue
- }
- id := len(varids)
- if id == 1<<31-1 {
- panic("too many variables")
- }
- fmt.Printf("var %d = %s\n", id, name)
- varids[name] = id
- varnames = append(varnames, name)
- vartypes = append(vartypes, types[e.parts[2].name])
- }
- memID := len(varids)
- fmt.Printf("var %d = .mem\n", memID)
- varids[".mem"] = memID // TODO: need .mem here?
- varnames = append(varnames, ".mem")
- vartypes = append(vartypes, ssa.TypeMem)
-
- // map from variable ID to current Value of that variable
- curBlock := NewSparseMap(len(varids))
-
- var state ssaFuncState
- state.types = types
- state.varids = varids
- state.varnames = varnames
- state.vartypes = vartypes
- state.curBlock = curBlock
- state.done = make([]bool, f.NumBlocks())
- state.defs = map[blockvar]*ssa.Value{}
- state.memID = memID
-
- // Convert each block to ssa
- // TODO: order blocks for maximum happiness - we want to process
- // all the predecessors of a block before processing the block itself,
- // if at all possible.
- for _, b := range f.Blocks {
- fmt.Printf("processing block %d\n", b.ID)
- curBlock.Clear()
- for _, e := range blocklines[b.ID] {
- switch e.parts[0].name {
- case "AS":
- if e.parts[1].compound {
- // store expression
- lhs := genExpr(&state, b, e.parts[1])
- rhs := genExpr(&state, b, e.parts[2])
- mem := genVar(&state, b, memID)
- v := b.NewValue(ssa.OpStore, ssa.TypeMem, nil)
- v.AddArg(lhs)
- v.AddArg(rhs)
- v.AddArg(mem)
- curBlock.Put(memID, v)
- } else {
- // variable assignment
- v := genExpr(&state, b, e.parts[2])
- curBlock.Put(varids[e.parts[1].name], v)
- }
- case "DCL":
- // nothing to do
- case "IF":
- b.Control = genExpr(&state, b, e.parts[1])
- case "CALL":
- // only direct call for now - indirect call takes addr value as well
- v := b.NewValue(ssa.OpStaticCall, ssa.TypeMem, e.parts[1].name)
- v.AddArg(genVar(&state, b, memID))
- curBlock.Put(memID, v)
- b.Control = v
- }
- }
- // link up forward references to their actual values
- for _, v := range b.Values {
- if v.Op != ssa.OpFwdRef {
- continue
- }
- varid := v.Aux.(int)
- w := genVar(&state, b, varid)
- v.Op = ssa.OpCopy
- v.Aux = nil
- v.AddArg(w)
- }
-
- // record final values at the end of the block
- for _, e := range curBlock.Contents() {
- state.defs[blockvar{b.ID, e.Key}] = e.Val
- // TODO: somehow avoid storing dead values to this map.
- }
- curBlock.Clear()
- state.done[b.ID] = true
- }
-
- // the final store value is returned
- if exit != nil {
- exit.Control = genVar(&state, exit, memID)
- }
-
- return f
-}
-
-func edge(a, b *ssa.Block) {
- a.Succs = append(a.Succs, b)
- b.Preds = append(b.Preds, a)
-}
-
-func genVar(state *ssaFuncState, b *ssa.Block, id int) *ssa.Value {
- // look up variable
- v := state.curBlock.Get(id)
- if v != nil {
- // variable was defined previously in this block
- // (or we memoized the result)
- return v
- }
-
- // Variable comes in from outside of basic block.
- v = lookupVarIncoming(state, b, id)
-
- // memoize result so future callers will not look it up again
- state.curBlock.Put(id, v)
- return v
-}
-
-func genExpr(state *ssaFuncState, b *ssa.Block, e sexpr) *ssa.Value {
- if !e.compound {
- return genVar(state, b, state.varids[e.name])
- }
- switch e.parts[0].name {
- case "ADD":
- x := genExpr(state, b, e.parts[1])
- y := genExpr(state, b, e.parts[2])
- v := b.NewValue(ssa.OpAdd, x.Type, nil)
- v.AddArg(x)
- v.AddArg(y)
- return v
- case "SUB":
- x := genExpr(state, b, e.parts[1])
- y := genExpr(state, b, e.parts[2])
- v := b.NewValue(ssa.OpSub, x.Type, nil)
- v.AddArg(x)
- v.AddArg(y)
- return v
- case "CINT":
- c, err := strconv.ParseInt(e.parts[1].name, 10, 64)
- if err != nil {
- panic("bad cint value")
- }
- return b.Func.ConstInt(ssa.TypeInt64, c)
- case "LT":
- x := genExpr(state, b, e.parts[1])
- y := genExpr(state, b, e.parts[2])
- v := b.NewValue(ssa.OpLess, ssa.TypeBool, nil)
- v.AddArg(x)
- v.AddArg(y)
- return v
- /*
- case "FP":
- typ := state.types[e.parts[1].name]
- offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
- if err != nil {
- panic(err)
- }
- v := b.NewValue(ssa.OpFPAddr, types.NewPointer(typ), offset)
- return v
- case "SP":
- typ := state.types[e.parts[1].name]
- offset, err := strconv.ParseInt(e.parts[2].name, 10, 64)
- if err != nil {
- panic(err)
- }
- v := b.NewValue(ssa.OpSPAddr, types.NewPointer(typ), offset)
- return v
- case "LOAD":
- p := genExpr(state, b, e.parts[1])
- v := b.NewValue(ssa.OpLoad, p.Type.(*types.Pointer).Elem(), nil)
- v.AddArg(p)
- v.AddArg(genVar(state, b, state.memID))
- return v
- */
- default:
- fmt.Println(e.parts[0].name)
- panic("unknown op")
- }
-}
-
-// map key combining block id and variable id
-type blockvar struct {
- bid ssa.ID
- varid int
-}
-
-type ssaFuncState struct {
- types map[string]ssa.Type
- varnames []string
- varids map[string]int
- vartypes []ssa.Type
- curBlock *SparseMap // value of each variable in block we're working on
- defs map[blockvar]*ssa.Value // values for variables at the end of blocks
- done []bool
- memID int
-}
-
-// Find the value of the variable with the given id leaving block b.
-func lookupVarOutgoing(state *ssaFuncState, b *ssa.Block, id int) *ssa.Value {
- fmt.Printf("lookupOutgoing var=%d block=%d\n", id, b.ID)
- v := state.defs[blockvar{b.ID, id}]
- if v != nil {
- return v
- }
- if state.done[b.ID] {
- // The variable was not defined in this block, and we haven't
- // memoized the answer yet. Look it up recursively. This might
- // cause infinite recursion, so add a copy first.
- v = b.NewValue(ssa.OpCopy, state.vartypes[id], nil)
- state.defs[blockvar{b.ID, id}] = v
- v.AddArg(lookupVarIncoming(state, b, id))
- return v
- }
- // We don't know about defined variables in this block (yet).
- // Make a forward reference for this variable.
- fmt.Printf("making fwdRef for var=%d in block=%d\n", id, b.ID)
- v = b.NewValue(ssa.OpFwdRef, state.vartypes[id], id)
-
- // memoize result
- state.defs[blockvar{b.ID, id}] = v
- return v
-}
-
-// Find the Value of the variable coming into block b.
-func lookupVarIncoming(state *ssaFuncState, b *ssa.Block, id int) *ssa.Value {
- fmt.Printf("lookupIncoming var=%d block=%d\n", id, b.ID)
- var v *ssa.Value
- switch len(b.Preds) {
- case 0:
- // TODO: handle function args some other way (assignments in starting block?)
- // TODO: error if variable isn't a function arg (including mem input)
- v = b.NewValue(ssa.OpArg, state.vartypes[id], state.varnames[id])
- case 1:
- v = lookupVarOutgoing(state, b.Preds[0], id)
- default:
- v = b.NewValue(ssa.OpCopy, state.vartypes[id], nil)
-
- args := make([]*ssa.Value, len(b.Preds))
- for i, p := range b.Preds {
- args[i] = lookupVarOutgoing(state, p, id)
- }
-
- // if <=1 value that isn't this variable's fwdRef, don't make phi
- v.Op = ssa.OpPhi
- v.AddArgs(args...) // note: order corresponding to b.Pred
- }
- return v
-}
-
-func parseSexprType(e sexpr) ssa.Type {
- if !e.compound {
- switch e.name {
- case "int":
- // TODO: pick correct width
- return ssa.TypeInt64
- default:
- fmt.Println(e.name)
- panic("unknown type")
- }
- }
- /*
- if e.parts[0].name == "FUNC" {
- // TODO: receiver? Already folded into args? Variadic?
- var args, rets []*types.Var
- for _, s := range e.parts[1].parts {
- t := parseSexprType(s)
- args = append(args, types.NewParam(0, nil, "noname", t))
- }
- for _, s := range e.parts[2].parts {
- t := parseSexprType(s)
- rets = append(rets, types.NewParam(0, nil, "noname", t))
- }
- sig := types.NewSignature(nil, nil, types.NewTuple(args...), types.NewTuple(rets...), false)
- return ssa.Type(sig)
- }
- */
- // TODO: array/struct/...
- panic("compound type")
-}