mirror of https://github.com/golang/go.git
[dev.ssa] cmd/internal/ssa: delete ssac
We don't need this standalone tool any more. We can now feed the ssa compiler directly from the Go frontend. Change-Id: I922f1e061c2d3db6bf77acc137d4d1fc7dc86c0d Reviewed-on: https://go-review.googlesource.com/10034 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
23df95b9b5
commit
12f980bc85
|
|
@ -1 +0,0 @@
|
|||
ssac
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T7faedc523360 (FUNC (int) (int)))
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T7faedc523360 (FUNC (int) (int)))
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(TYPE T127bd68 int)
|
||||
(DCL n T127bd68)
|
||||
(AS n (LOAD (FP T127bd68 0)))
|
||||
(DCL ~r1 T127bd68)
|
||||
(DCL n T127bd68)
|
||||
(DCL autotmp_0000 T127bd68)
|
||||
(DCL fib T7faedc523360)
|
||||
(DCL n T127bd68)
|
||||
(DCL autotmp_0001 T127bd68)
|
||||
(DCL fib T7faedc523360)
|
||||
(DCL n T127bd68)
|
||||
(DCL ~r1 T127bd68)
|
||||
(DCL autotmp_0000 T127bd68)
|
||||
(DCL autotmp_0001 T127bd68)
|
||||
(DCL autotmp_0001 T127bd68)
|
||||
(DCL autotmp_0000 T127bd68)
|
||||
(IF (LT n (CINT 2)) .then0 .else0)
|
||||
(LABEL .then0)
|
||||
(AS ~r1 n)
|
||||
(AS (FP T127bd68 8) ~r1)
|
||||
(RETURN)
|
||||
(GOTO .end0)
|
||||
(LABEL .else0)
|
||||
(GOTO .end0)
|
||||
(LABEL .end0)
|
||||
(AS (SP T127bd68 0) (SUB n (CINT 1)))
|
||||
(CALL fib)
|
||||
(AS autotmp_0000 (LOAD (SP T127bd68 8)))
|
||||
(AS (SP T127bd68 0) (SUB n (CINT 2)))
|
||||
(CALL fib)
|
||||
(AS autotmp_0001 (LOAD (SP T127bd68 8)))
|
||||
(AS ~r1 (ADD autotmp_0000 autotmp_0001))
|
||||
(AS (FP T127bd68 8) ~r1)
|
||||
(RETURN)
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
(NAME runtime·fibiter)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(TYPE Tf5dd68 int)
|
||||
(DCL a Tf5dd68)
|
||||
(DCL a Tf5dd68)
|
||||
(DCL b Tf5dd68)
|
||||
(DCL b Tf5dd68)
|
||||
(DCL i Tf5dd68)
|
||||
(DCL i Tf5dd68)
|
||||
(DCL i Tf5dd68)
|
||||
(DCL n Tf5dd68)
|
||||
(DCL autotmp_0002 Tf5dd68)
|
||||
(DCL i Tf5dd68)
|
||||
(DCL i Tf5dd68)
|
||||
(DCL autotmp_0002 Tf5dd68)
|
||||
(DCL autotmp_0002 Tf5dd68)
|
||||
(DCL autotmp_0003 Tf5dd68)
|
||||
(DCL a Tf5dd68)
|
||||
(DCL b Tf5dd68)
|
||||
(DCL a Tf5dd68)
|
||||
(DCL b Tf5dd68)
|
||||
(DCL b Tf5dd68)
|
||||
(DCL autotmp_0003 Tf5dd68)
|
||||
(DCL ~r1 Tf5dd68)
|
||||
(DCL a Tf5dd68)
|
||||
(AS n (LOAD (FP Tf5dd68 0)))
|
||||
(AS a (CINT 0))
|
||||
(AS b (CINT 1))
|
||||
(AS i (CINT 0))
|
||||
(GOTO .top0)
|
||||
(LABEL .top0)
|
||||
(IF (LT i n) .body0 .end0)
|
||||
(LABEL .body0)
|
||||
(AS autotmp_0003 (ADD a b))
|
||||
(AS a b)
|
||||
(AS b autotmp_0003)
|
||||
(AS autotmp_0002 i)
|
||||
(AS i (ADD autotmp_0002 (CINT 1)))
|
||||
(GOTO .top0)
|
||||
(LABEL .end0)
|
||||
(AS (FP Tf5dd68 8) a)
|
||||
(RETURN)
|
||||
|
|
@ -1,439 +0,0 @@
|
|||
// 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")
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
// 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
|
||||
|
||||
import "strings"
|
||||
|
||||
// an sexpr is an s-expression. It is either a token or a
|
||||
// parenthesized list of s-expressions.
|
||||
//
|
||||
// Used just for initial development. Should we keep it for testing, or
|
||||
// ditch it once we've plugged into the main compiler output?
|
||||
|
||||
type sexpr struct {
|
||||
compound bool
|
||||
name string // !compound
|
||||
parts []sexpr // compound
|
||||
}
|
||||
|
||||
func (s *sexpr) String() string {
|
||||
if !s.compound {
|
||||
return s.name
|
||||
}
|
||||
x := "("
|
||||
for i, p := range s.parts {
|
||||
if i != 0 {
|
||||
x += " "
|
||||
}
|
||||
x += p.String()
|
||||
}
|
||||
return x + ")"
|
||||
}
|
||||
|
||||
func parseSexpr(s string) sexpr {
|
||||
var e string
|
||||
e, s = grabOne(s)
|
||||
if len(e) > 0 && e[0] == '(' {
|
||||
e = e[1 : len(e)-1]
|
||||
var parts []sexpr
|
||||
for e != "" {
|
||||
var p string
|
||||
p, e = grabOne(e)
|
||||
parts = append(parts, parseSexpr(p))
|
||||
}
|
||||
return sexpr{true, "", parts}
|
||||
}
|
||||
return sexpr{false, e, nil}
|
||||
}
|
||||
|
||||
// grabOne peels off first token or parenthesized string from s.
|
||||
// returns first thing and the remainder of s.
|
||||
func grabOne(s string) (string, string) {
|
||||
for len(s) > 0 && s[0] == ' ' {
|
||||
s = s[1:]
|
||||
}
|
||||
if len(s) == 0 || s[0] != '(' {
|
||||
i := strings.Index(s, " ")
|
||||
if i < 0 {
|
||||
return s, ""
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
d := 0
|
||||
i := 0
|
||||
for {
|
||||
if len(s) == i {
|
||||
panic("unterminated s-expression: " + s)
|
||||
}
|
||||
if s[i] == '(' {
|
||||
d++
|
||||
}
|
||||
if s[i] == ')' {
|
||||
d--
|
||||
if d == 0 {
|
||||
i++
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
// 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
|
||||
|
||||
// Maintains a map[int]*ssa.Value, but cheaper.
|
||||
|
||||
// from http://research.swtch.com/sparse
|
||||
// in turn, from Briggs and Torczon
|
||||
|
||||
import (
|
||||
"cmd/internal/ssa"
|
||||
)
|
||||
|
||||
type SparseMap struct {
|
||||
dense []SparseMapEntry
|
||||
sparse []int
|
||||
}
|
||||
type SparseMapEntry struct {
|
||||
Key int
|
||||
Val *ssa.Value
|
||||
}
|
||||
|
||||
// NewSparseMap returns a SparseMap that can have
|
||||
// integers between 0 and n-1 as keys.
|
||||
func NewSparseMap(n int) *SparseMap {
|
||||
return &SparseMap{nil, make([]int, n)}
|
||||
}
|
||||
|
||||
func (s *SparseMap) Get(x int) *ssa.Value {
|
||||
i := s.sparse[x]
|
||||
if i < len(s.dense) && s.dense[i].Key == x {
|
||||
return s.dense[i].Val
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SparseMap) Put(x int, v *ssa.Value) {
|
||||
i := s.sparse[x]
|
||||
if i < len(s.dense) && s.dense[i].Key == x {
|
||||
s.dense[i].Val = v
|
||||
return
|
||||
}
|
||||
i = len(s.dense)
|
||||
s.dense = append(s.dense, SparseMapEntry{x, v})
|
||||
s.sparse[x] = i
|
||||
}
|
||||
|
||||
func (s *SparseMap) Remove(x int) {
|
||||
i := s.sparse[x]
|
||||
if i < len(s.dense) && s.dense[i].Key == x {
|
||||
y := s.dense[len(s.dense)-1]
|
||||
s.dense[i] = y
|
||||
s.sparse[y.Key] = i
|
||||
s.dense = s.dense[:len(s.dense)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SparseMap) Clear() {
|
||||
s.dense = s.dense[:0]
|
||||
}
|
||||
|
||||
// Contents returns a slice of key/value pairs.
|
||||
// Caller must not modify any returned entries.
|
||||
// The return value is invalid after the SparseMap is modified in any way.
|
||||
func (s *SparseMap) Contents() []SparseMapEntry {
|
||||
return s.dense
|
||||
}
|
||||
Loading…
Reference in New Issue