diff --git a/src/cmd/internal/ssa/block.go b/src/cmd/internal/ssa/block.go new file mode 100644 index 0000000000..ff1cb1b30a --- /dev/null +++ b/src/cmd/internal/ssa/block.go @@ -0,0 +1,92 @@ +// 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 ssa + +import ( + "fmt" + "strings" +) + +// Block represents a basic block in the control flow graph of a function. +type Block struct { + // A unique identifier for the block. The system will attempt to allocate + // these IDs densely, but no guarantees. + ID ID + + // The kind of block this is. + Kind BlockKind + + // Subsequent blocks, if any. The number and order depend on the block kind. + // All blocks must be distinct (to make phi values in successors unambiguous). + Succs []*Block + + // Inverse of successors. + // The order is significant to Phi nodes in the block. + Preds []*Block + // TODO: predecessors is a pain to maintain. Can we somehow order phi + // arguments by block id and have this field computed explicitly when needed? + + // A value that determines how the block is exited. Its value depends on the kind + // of the block. For instance, a BlockIf has a boolean control value and BlockExit + // has a memory control value. + Control *Value + + // The unordered set of Values contained in this block. + // The list must include the control value, if any. (TODO: need this last condition?) + Values []*Value + + // The containing function + Func *Func +} + +// kind control successors +// ------------------------------------------ +// Exit return mem [] +// Plain nil [next] +// If a boolean Value [then, else] +// Call mem [nopanic, panic] (control opcode should be OpCall or OpStaticCall) +type BlockKind int8 + +const ( + BlockExit BlockKind = iota // no successors. There should only be 1 of these. + BlockPlain // a single successor + BlockIf // 2 successors, if control goto Succs[0] else goto Succs[1] + BlockCall // 2 successors, normal return and panic + BlockUnknown + + // 386/amd64 variants of BlockIf that take the flags register as an arg + BlockEQ + BlockNE + BlockLT + BlockLE + BlockGT + BlockGE + BlockULT + BlockULE + BlockUGT + BlockUGE +) + +//go:generate stringer -type=BlockKind + +// short form print +func (b *Block) String() string { + return fmt.Sprintf("b%d", b.ID) +} + +// long form print +func (b *Block) LongString() string { + s := strings.TrimPrefix(b.Kind.String(), "Block") + if b.Control != nil { + s += fmt.Sprintf(" %s", b.Control) + } + if len(b.Succs) > 0 { + s += " ->" + for _, c := range b.Succs { + s += " " + c.String() + } + } + return s +} diff --git a/src/cmd/internal/ssa/blockkind_string.go b/src/cmd/internal/ssa/blockkind_string.go new file mode 100644 index 0000000000..6204f1948f --- /dev/null +++ b/src/cmd/internal/ssa/blockkind_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=BlockKind; DO NOT EDIT + +package ssa + +import "fmt" + +const _BlockKind_name = "BlockExitBlockPlainBlockIfBlockCallBlockUnknownBlockEQBlockNEBlockLTBlockLEBlockGTBlockGEBlockULTBlockULEBlockUGTBlockUGE" + +var _BlockKind_index = [...]uint8{0, 9, 19, 26, 35, 47, 54, 61, 68, 75, 82, 89, 97, 105, 113, 121} + +func (i BlockKind) String() string { + if i < 0 || i+1 >= BlockKind(len(_BlockKind_index)) { + return fmt.Sprintf("BlockKind(%d)", i) + } + return _BlockKind_name[_BlockKind_index[i]:_BlockKind_index[i+1]] +} diff --git a/src/cmd/internal/ssa/check.go b/src/cmd/internal/ssa/check.go new file mode 100644 index 0000000000..b501cdb54c --- /dev/null +++ b/src/cmd/internal/ssa/check.go @@ -0,0 +1,125 @@ +// 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 ssa + +import "log" + +// checkFunc checks invariants of f. +func checkFunc(f *Func) { + blockMark := make([]bool, f.NumBlocks()) + valueMark := make([]bool, f.NumValues()) + + for _, b := range f.Blocks { + if blockMark[b.ID] { + log.Panicf("block %s appears twice in %s!", b, f.Name) + } + blockMark[b.ID] = true + if b.Func != f { + log.Panicf("%s.Func=%s, want %s", b, b.Func.Name, f.Name) + } + + for i, c := range b.Succs { + for j, d := range b.Succs { + if i != j && c == d { + log.Panicf("%s.Succs has duplicate block %s", b, c) + } + } + } + // Note: duplicate successors are hard in the following case: + // if(...) goto x else goto x + // x: v = phi(a, b) + // If the conditional is true, does v get the value of a or b? + // We could solve this other ways, but the easiest is just to + // require (by possibly adding empty control-flow blocks) that + // all successors are distinct. They will need to be distinct + // anyway for register allocation (duplicate successors implies + // the existence of critical edges). + + for _, p := range b.Preds { + var found bool + for _, c := range p.Succs { + if c == b { + found = true + break + } + } + if !found { + log.Panicf("block %s is not a succ of its pred block %s", b, p) + } + } + + switch b.Kind { + case BlockExit: + if len(b.Succs) != 0 { + log.Panicf("exit block %s has successors", b) + } + if b.Control == nil { + log.Panicf("exit block %s has no control value", b) + } + if b.Control.Type != TypeMem { + log.Panicf("exit block %s has non-memory control value %s", b, b.Control.LongString()) + } + case BlockPlain: + if len(b.Succs) != 1 { + log.Panicf("plain block %s len(Succs)==%d, want 1", b, len(b.Succs)) + } + if b.Control != nil { + log.Panicf("plain block %s has non-nil control %s", b, b.Control.LongString()) + } + case BlockIf: + if len(b.Succs) != 2 { + log.Panicf("if block %s len(Succs)==%d, want 2", b, len(b.Succs)) + } + if b.Control == nil { + log.Panicf("if block %s has no control value", b) + } + if b.Control.Type != TypeBool { + log.Panicf("if block %s has non-bool control value %s", b, b.Control.LongString()) + } + case BlockCall: + if len(b.Succs) != 2 { + log.Panicf("call block %s len(Succs)==%d, want 2", b, len(b.Succs)) + } + if b.Control == nil { + log.Panicf("call block %s has no control value", b) + } + if b.Control.Type != TypeMem { + log.Panicf("call block %s has non-memory control value %s", b, b.Control.LongString()) + } + if b.Succs[1].Kind != BlockExit { + log.Panicf("exception edge from call block %s does not go to exit but %s", b, b.Succs[1]) + } + } + + for _, v := range b.Values { + if valueMark[v.ID] { + log.Panicf("value %s appears twice!", v.LongString()) + } + valueMark[v.ID] = true + + if v.Block != b { + log.Panicf("%s.block != %s", v, b) + } + if v.Op == OpPhi && len(v.Args) != len(b.Preds) { + log.Panicf("phi length %s does not match pred length %d for block %s", v.LongString(), len(b.Preds), b) + } + + // TODO: check idom + // TODO: check for cycles in values + // TODO: check type + } + } + + for _, id := range f.bid.free { + if blockMark[id] { + log.Panicf("used block b%d in free list", id) + } + } + for _, id := range f.vid.free { + if valueMark[id] { + log.Panicf("used value v%d in free list", id) + } + } +} diff --git a/src/cmd/internal/ssa/compile.go b/src/cmd/internal/ssa/compile.go new file mode 100644 index 0000000000..5e21bdf6e1 --- /dev/null +++ b/src/cmd/internal/ssa/compile.go @@ -0,0 +1,65 @@ +// 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 ssa + +import "fmt" + +// Compile is the main entry point for this package. +// Compile modifies f so that on return: +// · all Values in f map to 0 or 1 assembly instructions of the target architecture +// · the order of f.Blocks is the order to emit the Blocks +// · the order of b.Values is the order to emit the Values in each Block +// · f has a non-nil regAlloc field +func Compile(f *Func) { + // TODO: debugging - set flags to control verbosity of compiler, + // which phases to dump IR before/after, etc. + fmt.Printf("compiling %s\n", f.Name) + + // hook to print function & phase if panic happens + phaseName := "init" + defer func() { + if phaseName != "" { + fmt.Printf("panic during %s while compiling %s\n", phaseName, f.Name) + } + }() + + // Run all the passes + printFunc(f) + checkFunc(f) + for _, p := range passes { + phaseName = p.name + fmt.Printf(" pass %s begin\n", p.name) + p.fn(f) + fmt.Printf(" pass %s end\n", p.name) + printFunc(f) + checkFunc(f) + } + + // Squash error printing defer + phaseName = "" +} + +type pass struct { + name string + fn func(*Func) +} + +// list of passes for the compiler +var passes = [...]pass{ + {"phielim", phielim}, + {"copyelim", copyelim}, + //{"opt", opt}, + // cse + {"deadcode", deadcode}, + //{"fuse", fuse}, + //{"lower", lower}, + // cse + //{"critical", critical}, // remove critical edges + //{"layout", layout}, // schedule blocks + //{"schedule", schedule}, // schedule values + // regalloc + // stack slot alloc (+size stack frame) + //{"cgen", cgen}, +} diff --git a/src/cmd/internal/ssa/copyelim.go b/src/cmd/internal/ssa/copyelim.go new file mode 100644 index 0000000000..10c2dcc440 --- /dev/null +++ b/src/cmd/internal/ssa/copyelim.go @@ -0,0 +1,29 @@ +// 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 ssa + +// copyelim removes all copies from f. +func copyelim(f *Func) { + for _, b := range f.Blocks { + for _, v := range b.Values { + for i, w := range v.Args { + x := w + for x.Op == OpCopy { + x = x.Args[0] + } + if x != w { + v.Args[i] = x + } + } + } + v := b.Control + if v != nil { + for v.Op == OpCopy { + v = v.Args[0] + } + b.Control = v + } + } +} diff --git a/src/cmd/internal/ssa/deadcode.go b/src/cmd/internal/ssa/deadcode.go new file mode 100644 index 0000000000..1647ea955d --- /dev/null +++ b/src/cmd/internal/ssa/deadcode.go @@ -0,0 +1,153 @@ +// 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 ssa + +import "log" + +// deadcode removes dead code from f. +func deadcode(f *Func) { + + // Find all reachable basic blocks. + reachable := make([]bool, f.NumBlocks()) + reachable[f.Entry.ID] = true + p := []*Block{f.Entry} // stack-like worklist + for len(p) > 0 { + // pop a reachable block + b := p[len(p)-1] + p = p[:len(p)-1] + + // constant-fold conditionals + // TODO: rewrite rules instead? + if b.Kind == BlockIf && b.Control.Op == OpConstBool { + cond := b.Control.Aux.(bool) + var c *Block + if cond { + // then branch is always taken + c = b.Succs[1] + } else { + // else branch is always taken + c = b.Succs[0] + b.Succs[0] = b.Succs[1] + } + b.Succs[1] = nil // aid GC + b.Succs = b.Succs[:1] + removePredecessor(b, c) + b.Kind = BlockPlain + b.Control = nil + } + + for _, c := range b.Succs { + if !reachable[c.ID] { + reachable[c.ID] = true + p = append(p, c) // push + } + } + } + + // Find all live values + live := make([]bool, f.NumValues()) // flag to set for each live value + var q []*Value // stack-like worklist of unscanned values + + // Starting set: all control values of reachable blocks are live. + for _, b := range f.Blocks { + if !reachable[b.ID] { + continue + } + if v := b.Control; v != nil && !live[v.ID] { + live[v.ID] = true + q = append(q, v) + } + } + + // Compute transitive closure of live values. + for len(q) > 0 { + // pop a reachable value + v := q[len(q)-1] + q = q[:len(q)-1] + for _, x := range v.Args { + if !live[x.ID] { + live[x.ID] = true + q = append(q, x) // push + } + } + } + + // Remove dead values from blocks' value list. Return dead + // value ids to the allocator. + for _, b := range f.Blocks { + i := 0 + for _, v := range b.Values { + if live[v.ID] { + b.Values[i] = v + i++ + } else { + f.vid.put(v.ID) + } + } + for j := i; j < len(b.Values); j++ { + b.Values[j] = nil // aid GC + } + b.Values = b.Values[:i] + } + + // Remove unreachable blocks. Return dead block ids to allocator. + i := 0 + for _, b := range f.Blocks { + if reachable[b.ID] { + f.Blocks[i] = b + i++ + } else { + if len(b.Values) > 0 { + panic("live value in unreachable block") + } + f.bid.put(b.ID) + } + } + // zero remainder to help gc + for j := i; j < len(f.Blocks); j++ { + f.Blocks[j] = nil + } + f.Blocks = f.Blocks[:i] + + // TODO: renumber Blocks and Values densely? +} + +// There was an edge b->c. It has been removed from b's successors. +// Fix up c to handle that fact. +func removePredecessor(b, c *Block) { + n := len(c.Preds) - 1 + if n == 0 { + // c is now dead - don't bother working on it + if c.Preds[0] != b { + log.Panicf("%s.Preds[0]==%s, want %s", c, c.Preds[0], b) + } + return + } + + // find index of b in c's predecessor list + var i int + for j, p := range c.Preds { + if p == b { + i = j + break + } + } + + c.Preds[i] = c.Preds[n] + c.Preds[n] = nil // aid GC + c.Preds = c.Preds[:n] + // rewrite phi ops to match the new predecessor list + for _, v := range c.Values { + if v.Op != OpPhi { + continue + } + v.Args[i] = v.Args[n] + v.Args[n] = nil // aid GC + v.Args = v.Args[:n] + if n == 1 { + v.Op = OpCopy + } + } +} diff --git a/src/cmd/internal/ssa/deadcode_test.go b/src/cmd/internal/ssa/deadcode_test.go new file mode 100644 index 0000000000..94fc359af7 --- /dev/null +++ b/src/cmd/internal/ssa/deadcode_test.go @@ -0,0 +1,112 @@ +// 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. + +// TODO: these tests are pretty verbose. Is there a way to simplify +// building a small Func for testing? + +package ssa_test + +import ( + . "cmd/internal/ssa" + "testing" +) + +func TestDeadLoop(t *testing.T) { + f := new(Func) + entry := f.NewBlock(BlockPlain) + exit := f.NewBlock(BlockExit) + f.Entry = entry + addEdge(entry, exit) + mem := entry.NewValue(OpArg, TypeMem, ".mem") + exit.Control = mem + + // dead loop + deadblock := f.NewBlock(BlockIf) + addEdge(deadblock, deadblock) + addEdge(deadblock, exit) + + // dead value in dead block + deadval := deadblock.NewValue(OpConstBool, TypeBool, true) + deadblock.Control = deadval + + CheckFunc(f) + Deadcode(f) + CheckFunc(f) + + for _, b := range f.Blocks { + if b == deadblock { + t.Errorf("dead block not removed") + } + for _, v := range b.Values { + if v == deadval { + t.Errorf("control value of dead block not removed") + } + } + } +} + +func TestDeadValue(t *testing.T) { + f := new(Func) + entry := f.NewBlock(BlockPlain) + exit := f.NewBlock(BlockExit) + f.Entry = entry + addEdge(entry, exit) + mem := entry.NewValue(OpArg, TypeMem, ".mem") + exit.Control = mem + + deadval := entry.NewValue(OpConstInt, TypeInt, 37) + + CheckFunc(f) + Deadcode(f) + CheckFunc(f) + + for _, b := range f.Blocks { + for _, v := range b.Values { + if v == deadval { + t.Errorf("dead value not removed") + } + } + } +} + +func TestNeverTaken(t *testing.T) { + f := new(Func) + entry := f.NewBlock(BlockIf) + exit := f.NewBlock(BlockExit) + then := f.NewBlock(BlockPlain) + else_ := f.NewBlock(BlockPlain) + f.Entry = entry + addEdge(entry, then) + addEdge(entry, else_) + addEdge(then, exit) + addEdge(else_, exit) + mem := entry.NewValue(OpArg, TypeMem, ".mem") + exit.Control = mem + + cond := entry.NewValue(OpConstBool, TypeBool, false) + entry.Control = cond + + CheckFunc(f) + Deadcode(f) + CheckFunc(f) + + if entry.Kind != BlockPlain { + t.Errorf("if(false) not simplified") + } + for _, b := range f.Blocks { + if b == then { + t.Errorf("then block still present") + } + for _, v := range b.Values { + if v == cond { + t.Errorf("constant condition still present") + } + } + } +} + +func addEdge(b, c *Block) { + b.Succs = append(b.Succs, c) + c.Preds = append(c.Preds, b) +} diff --git a/src/cmd/internal/ssa/export_test.go b/src/cmd/internal/ssa/export_test.go new file mode 100644 index 0000000000..ab4ab82345 --- /dev/null +++ b/src/cmd/internal/ssa/export_test.go @@ -0,0 +1,9 @@ +// 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 ssa + +var CheckFunc = checkFunc +var PrintFunc = printFunc +var Deadcode = deadcode diff --git a/src/cmd/internal/ssa/func.go b/src/cmd/internal/ssa/func.go new file mode 100644 index 0000000000..6868e3d1ed --- /dev/null +++ b/src/cmd/internal/ssa/func.go @@ -0,0 +1,61 @@ +// 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 ssa + +// A Func represents a Go func declaration (or function literal) and +// its body. This package compiles each Func independently. +type Func struct { + Name string // e.g. bytes·Compare + Type Type // type signature of the function. + Blocks []*Block // unordered set of all basic blocks (note: not indexable by ID) + Entry *Block // the entry basic block + bid idAlloc // block ID allocator + vid idAlloc // value ID allocator + + // when register allocation is done, maps value ids to locations + RegAlloc []Location +} + +// NumBlocks returns an integer larger than the id of any Block in the Func. +func (f *Func) NumBlocks() int { + return f.bid.num() +} + +// NumValues returns an integer larger than the id of any Value in the Func. +func (f *Func) NumValues() int { + return f.vid.num() +} + +// NewBlock returns a new block of the given kind and appends it to f.Blocks. +func (f *Func) NewBlock(kind BlockKind) *Block { + b := &Block{ + ID: f.bid.get(), + Kind: kind, + Func: f, + } + f.Blocks = append(f.Blocks, b) + return b +} + +// NewValue returns a new value in the block with no arguments. +func (b *Block) NewValue(op Op, t Type, aux interface{}) *Value { + v := &Value{ + ID: b.Func.vid.get(), + Op: op, + Type: t, + Aux: aux, + Block: b, + } + v.Args = v.argstorage[:0] + b.Values = append(b.Values, v) + return v +} + +// ConstInt returns an int constant representing its argument. +func (f *Func) ConstInt(c int64) *Value { + // TODO: cache? + // TODO: different types? + return f.Entry.NewValue(OpConstInt, TypeInt, c) +} diff --git a/src/cmd/internal/ssa/id.go b/src/cmd/internal/ssa/id.go new file mode 100644 index 0000000000..43f23c838c --- /dev/null +++ b/src/cmd/internal/ssa/id.go @@ -0,0 +1,41 @@ +// 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 ssa + +type ID int32 + +// idAlloc provides an allocator for unique integers. +type idAlloc struct { + last ID + free []ID +} + +// get allocates an ID and returns it. +func (a *idAlloc) get() ID { + if n := len(a.free); n > 0 { + x := a.free[n-1] + a.free = a.free[:n-1] + return x + } + x := a.last + x++ + if x == 1<<31-1 { + panic("too many ids for this function") + } + a.last = x + return x +} + +// put deallocates an ID. +func (a *idAlloc) put(x ID) { + a.free = append(a.free, x) + // TODO: IR check should make sure that the IR contains + // no IDs that are in the free list. +} + +// num returns the maximum ID ever returned + 1. +func (a *idAlloc) num() int { + return int(a.last + 1) +} diff --git a/src/cmd/internal/ssa/location.go b/src/cmd/internal/ssa/location.go new file mode 100644 index 0000000000..94c1b426a2 --- /dev/null +++ b/src/cmd/internal/ssa/location.go @@ -0,0 +1,42 @@ +// 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 ssa + +import ( + "fmt" +) + +// A place that an ssa variable can reside. +type Location interface { + Name() string // name to use in assembly templates: %rax, 16(%rsp), ... +} + +// A Register is a machine register, like %rax. +type Register struct { + name string +} + +func (r *Register) Name() string { + return r.name +} + +// A LocalSlot is a location in the stack frame. +type LocalSlot struct { + idx int64 // offset in locals area (distance down from FP == caller's SP) +} + +func (s *LocalSlot) Name() string { + return fmt.Sprintf("loc%d", s.idx) +} + +// An ArgSlot is a location in the parents' stack frame where it passed us an argument. +type ArgSlot struct { + idx int64 // offset in argument area +} + +// A CalleeSlot is a location in the stack frame where we pass an argument to a callee. +type CalleeSlot struct { + idx int64 // offset in callee area +} diff --git a/src/cmd/internal/ssa/op.go b/src/cmd/internal/ssa/op.go new file mode 100644 index 0000000000..a4364b1c5c --- /dev/null +++ b/src/cmd/internal/ssa/op.go @@ -0,0 +1,345 @@ +// 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 ssa + +// An Op encodes the specific operation that a Value performs. +// Opcodes' semantics can be modified by the type and aux fields of the Value. +// For instance, OpAdd can be 32 or 64 bit, signed or unsigned, float or complex, depending on Value.Type. +// Semantics of each op are described below. +// Ops come in two flavors, architecture-independent and architecture-dependent. +type Op int32 + +// All the opcodes +const ( + OpUnknown Op = iota + + // machine-independent opcodes + + OpNop // should never be used, appears only briefly during construction, Has type Void. + OpThunk // used during ssa construction. Like OpCopy, but the arg has not been specified yet. + + // 2-input arithmetic + OpAdd + OpSub + OpMul + + // 2-input comparisons + OpLess + + // constants + OpConstNil + OpConstBool // aux is type bool + OpConstString // aux is type string + OpConstInt // aux is type int64 + OpConstFloat // aux is type float64 + OpConstComplex // aux is type complex128 + + OpArg // address of a function parameter/result + OpGlobal // address of a global variable + OpFunc // entry address of a function + OpCopy // output = input + OpPhi // select an input based on which predecessor we came from + + OpSliceMake // args are ptr/len/cap + OpSlicePtr + OpSliceLen + OpSliceCap + + OpStringMake // args are ptr/len + OpStringPtr + OpStringLen + + OpSlice + OpIndex + OpIndexAddr + + OpLoad // args are ptr, memory + OpStore // args are ptr, memory, returns memory + + OpCheckNil // arg[0] != nil + OpCheckBound // 0 <= arg[0] < arg[1] + + // function calls. Arguments to the call have already been written to the stack. + // Return values appear on the stack. + OpCall // args are function ptr, memory + OpStaticCall // aux is function, arg is memory + + OpConvert + OpConvNop + + // These ops return a pointer to a location on the stack. Aux contains an int64 + // indicating an offset from the base pointer. + OpFPAddr // offset from FP (+ == args from caller, - == locals) + OpSPAddr // offset from SP + + // load/store from constant offsets from SP/FP + // The distinction between FP/SP needs to be maintained until after + // register allocation because we don't know the size of the frame yet. + OpLoadFP + OpLoadSP + OpStoreFP + OpStoreSP + + // spill&restore ops for the register allocator. These are + // semantically identical to OpCopy - they do not take/return + // stores like regular memory ops do. We can get away with that because + // we know there is no aliasing to spill slots on the stack. + OpStoreReg8 + OpLoadReg8 + + // machine-dependent opcodes go here + + // x86 + OpADDQ + OpSUBQ + OpADDCQ // 1 input arg, add aux which is an int64 constant + OpSUBCQ // 1 input arg. output = input - aux.(int64) + OpNEGQ + OpCMPQ + OpCMPCQ // 1 input arg. Compares input with aux.(int64) + OpADDL + OpInvertFlags // inverts interpretation of the flags register (< to >=, etc.) + OpSETL // generate bool = "flags encode less than" + OpSETGE + + OpLEAQ // x+y + OpLEAQ2 // x+2*y + OpLEAQ4 // x+4*y + OpLEAQ8 // x+8*y + + OpLoadFP8 + OpLoadSP8 + OpStoreFP8 + OpStoreSP8 + + OpMax // sentinel +) + +//go:generate stringer -type=Op + +type OpInfo struct { + flags int32 + + // assembly template + // %In: location of input n + // %On: location of output n + // %A: print aux with fmt.Print + asm string + + // computes type for values with this opcode + typer func(v *Value) + + // returns a reg constraint for the instruction. [0] gives a reg constraint + // for each input, [1] gives a reg constraint for each output. (Values have + // exactly one output for now) + reg [2][]regMask +} + +type regMask uint64 + +var regs386 = [...]string{ + "AX", + "BX", + "CX", + "DX", + "SI", + "DI", + "SP", + "BP", + "X0", + + // pseudo registers + "FLAGS", + "OVERWRITE0", // the same register as the first input +} + +// TODO: match up these with regs386 above +var gp regMask = 0xff +var cx regMask = 0x4 +var flags regMask = 1 << 9 +var overwrite0 regMask = 1 << 10 + +const ( + // possible properties of opcodes + OpFlagCommutative int32 = 1 << iota + + // architecture constants + Arch386 + ArchAmd64 + ArchArm +) + +func firstArgTyper(v *Value) { + v.Type = v.Args[0].Type +} +func boolTyper(v *Value) { + v.Type = TypeBool +} +func stringTyper(v *Value) { + v.Type = TypeString +} +func flagsTyper(v *Value) { + v.Type = TypeFlags +} +func uint8Typer(v *Value) { + v.Type = TypeUint8 +} +func uint64Typer(v *Value) { + v.Type = TypeUint64 +} +func auxTyper(v *Value) { + v.Type = v.Aux.(Type) +} + +// general purpose registers, 2 input, 1 output +var gp21 = [2][]regMask{{gp, gp}, {gp}} +var gp21_overwrite = [2][]regMask{{gp, gp}, {overwrite0}} + +// general purpose registers, 1 input, 1 output +var gp11 = [2][]regMask{{gp}, {gp}} +var gp11_overwrite = [2][]regMask{{gp}, {overwrite0}} + +// shift operations +var shift = [2][]regMask{{gp, cx}, {overwrite0}} + +var gp2_flags = [2][]regMask{{gp, gp}, {flags}} +var gp1_flags = [2][]regMask{{gp}, {flags}} +var gpload = [2][]regMask{{gp, 0}, {gp}} +var gpstore = [2][]regMask{{gp, gp, 0}, {0}} + +// Opcodes that represent the input Go program +var genericTable = [...]OpInfo{ + // the unknown op is used only during building and should not appear in a + // fully formed ssa representation. + + OpAdd: {flags: OpFlagCommutative, typer: firstArgTyper}, + OpSub: {typer: firstArgTyper}, + OpMul: {flags: OpFlagCommutative, typer: firstArgTyper}, + OpLess: {typer: boolTyper}, + + OpConstBool: {typer: boolTyper}, // aux is a bool + OpConstString: {typer: stringTyper}, // aux is a string + OpConstInt: {}, // aux is an int64 + OpConstFloat: {}, // aux is a float64 + OpConstComplex: {}, + OpArg: {}, // aux is the name of the input variable TODO:? + OpGlobal: {}, // address of a global variable + OpFunc: {}, + OpCopy: {}, + OpPhi: {}, + + OpConvNop: {}, // aux is the type to convert to + + /* + // build and take apart slices + {name: "slicemake"}, // (ptr,len,cap) -> slice + {name: "sliceptr"}, // pointer part of slice + {name: "slicelen"}, // length part of slice + {name: "slicecap"}, // capacity part of slice + + // build and take apart strings + {name: "stringmake"}, // (ptr,len) -> string + {name: "stringptr"}, // pointer part of string + {name: "stringlen"}, // length part of string + + // operations on arrays/slices/strings + {name: "slice"}, // (s, i, j) -> s[i:j] + {name: "index"}, // (mem, ptr, idx) -> val + {name: "indexaddr"}, // (ptr, idx) -> ptr + + // loads & stores + {name: "load"}, // (mem, check, ptr) -> val + {name: "store"}, // (mem, check, ptr, val) -> mem + + // checks + {name: "checknil"}, // (mem, ptr) -> check + {name: "checkbound"}, // (mem, idx, len) -> check + + // functions + {name: "call"}, + + // builtins + {name: "len"}, + {name: "convert"}, + + // tuples + {name: "tuple"}, // build a tuple out of its arguments + {name: "extract"}, // aux is an int64. Extract that index out of a tuple + {name: "extractsuffix"}, // aux is an int64. Slice a tuple with [aux:] + + */ +} + +// Opcodes that appear in an output amd64 program +var amd64Table = [...]OpInfo{ + OpADDQ: {flags: OpFlagCommutative, asm: "ADDQ\t%I0,%I1,%O0", reg: gp21, typer: firstArgTyper}, // TODO: overwrite + OpADDCQ: {asm: "ADDQ\t$%A,%I0,%O0", reg: gp11_overwrite, typer: firstArgTyper}, // aux = int64 constant to add + OpSUBQ: {asm: "SUBQ\t%I0,%I1,%O0", reg: gp21, typer: firstArgTyper}, + OpSUBCQ: {asm: "SUBQ\t$%A,%I0,%O0", reg: gp11_overwrite, typer: firstArgTyper}, + + OpCMPQ: {asm: "CMPQ\t%I0,%I1", reg: gp2_flags, typer: flagsTyper}, // compute arg[0]-arg[1] and produce flags + OpCMPCQ: {asm: "CMPQ\t$%A,%I0", reg: gp1_flags}, + + OpLEAQ: {flags: OpFlagCommutative, asm: "LEAQ\t%A(%I0)(%I1*1),%O0", reg: gp21}, // aux = int64 constant to add + OpLEAQ2: {asm: "LEAQ\t%A(%I0)(%I1*2),%O0"}, + OpLEAQ4: {asm: "LEAQ\t%A(%I0)(%I1*4),%O0"}, + OpLEAQ8: {asm: "LEAQ\t%A(%I0)(%I1*8),%O0"}, + + //OpLoad8: {asm: "MOVQ\t%A(%I0),%O0", reg: gpload}, + //OpStore8: {asm: "MOVQ\t%I1,%A(%I0)", reg: gpstore}, + + OpStaticCall: {asm: "CALL\t%A(SB)"}, + + OpCopy: {asm: "MOVQ\t%I0,%O0", reg: gp11}, + + // convert from flags back to boolean + OpSETL: {typer: boolTyper}, + + // ops for load/store to stack + OpLoadFP8: {asm: "MOVQ\t%A(FP),%O0"}, + OpLoadSP8: {asm: "MOVQ\t%A(SP),%O0"}, + OpStoreFP8: {asm: "MOVQ\t%I0,%A(FP)"}, + OpStoreSP8: {asm: "MOVQ\t%I0,%A(SP)"}, + + // ops for spilling of registers + // unlike regular loads & stores, these take no memory argument. + // They are just like OpCopy but we use them during register allocation. + // TODO: different widths, float + OpLoadReg8: {asm: "MOVQ\t%I0,%O0", reg: gp11}, + OpStoreReg8: {asm: "MOVQ\t%I0,%O0", reg: gp11}, +} + +// A Table is a list of opcodes with a common set of flags. +type Table struct { + t []OpInfo + flags int32 +} + +var tables = []Table{ + {genericTable[:], 0}, + {amd64Table[:], ArchAmd64}, // TODO: pick this dynamically +} + +// table of opcodes, indexed by opcode ID +var opcodeTable [OpMax]OpInfo + +// map from opcode names to opcode IDs +var nameToOp map[string]Op + +func init() { + // build full opcode table + // Note that the arch-specific table overwrites the generic table + for _, t := range tables { + for op, entry := range t.t { + entry.flags |= t.flags + opcodeTable[op] = entry + } + } + // build name to opcode mapping + nameToOp = make(map[string]Op) + for op := range opcodeTable { + nameToOp[Op(op).String()] = Op(op) + } +} diff --git a/src/cmd/internal/ssa/op_string.go b/src/cmd/internal/ssa/op_string.go new file mode 100644 index 0000000000..40051eb321 --- /dev/null +++ b/src/cmd/internal/ssa/op_string.go @@ -0,0 +1,16 @@ +// generated by stringer -type=Op; DO NOT EDIT + +package ssa + +import "fmt" + +const _Op_name = "OpUnknownOpNopOpThunkOpAddOpSubOpMulOpLessOpConstNilOpConstBoolOpConstStringOpConstIntOpConstFloatOpConstComplexOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceOpIndexOpIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpLoadFPOpLoadSPOpStoreFPOpStoreSPOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpNEGQOpCMPQOpCMPCQOpADDLOpInvertFlagsOpSETLOpSETGEOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLoadFP8OpLoadSP8OpStoreFP8OpStoreSP8OpMax" + +var _Op_index = [...]uint16{0, 9, 14, 21, 26, 31, 36, 42, 52, 63, 76, 86, 98, 112, 117, 125, 131, 137, 142, 153, 163, 173, 183, 195, 206, 217, 224, 231, 242, 248, 255, 265, 277, 283, 295, 304, 313, 321, 329, 337, 345, 354, 363, 374, 384, 390, 396, 403, 410, 416, 422, 429, 435, 448, 454, 461, 467, 474, 481, 488, 497, 506, 516, 526, 531} + +func (i Op) String() string { + if i < 0 || i+1 >= Op(len(_Op_index)) { + return fmt.Sprintf("Op(%d)", i) + } + return _Op_name[_Op_index[i]:_Op_index[i+1]] +} diff --git a/src/cmd/internal/ssa/phielim.go b/src/cmd/internal/ssa/phielim.go new file mode 100644 index 0000000000..19c0d077e5 --- /dev/null +++ b/src/cmd/internal/ssa/phielim.go @@ -0,0 +1,44 @@ +// 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 ssa + +// phielim eliminates redundant phi values from f. +// A phi is redundant if its arguments are all equal. For +// purposes of counting, ignore the phi itself. Both of +// these phis are redundant: +// v = phi(x,x,x) +// v = phi(x,v,x,v) +func phielim(f *Func) { + args := newSparseSet(f.NumValues()) + for _, b := range f.Blocks { + for _, v := range b.Values { + if v.Op != OpPhi { + continue + } + args.clear() + for _, x := range v.Args { + for x.Op == OpCopy { + x = x.Args[0] + } + args.add(x.ID) + } + switch { + case args.size() == 1: + v.Op = OpCopy + v.SetArgs1(v.Args[0]) + case args.size() == 2 && args.contains(v.ID): + var w *Value + for _, x := range v.Args { + if x.ID != v.ID { + w = x + break + } + } + v.Op = OpCopy + v.SetArgs1(w) + } + } + } +} diff --git a/src/cmd/internal/ssa/print.go b/src/cmd/internal/ssa/print.go new file mode 100644 index 0000000000..eeea30d970 --- /dev/null +++ b/src/cmd/internal/ssa/print.go @@ -0,0 +1,63 @@ +// 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 ssa + +import "fmt" + +func printFunc(f *Func) { + fmt.Print(f.Name) + fmt.Print(" ") + fmt.Println(f.Type) + printed := make([]bool, f.NumValues()) + for _, b := range f.Blocks { + fmt.Printf(" b%d:\n", b.ID) + n := 0 + + // print phis first since all value cycles contain a phi + for _, v := range b.Values { + if v.Op != OpPhi { + continue + } + fmt.Print(" ") + fmt.Println(v.LongString()) + printed[v.ID] = true + n++ + } + + // print rest of values in dependency order + for n < len(b.Values) { + m := n + outer: + for _, v := range b.Values { + if printed[v.ID] { + continue + } + for _, w := range v.Args { + if w.Block == b && !printed[w.ID] { + continue outer + } + } + fmt.Print(" ") + fmt.Println(v.LongString()) + printed[v.ID] = true + n++ + } + if m == n { + fmt.Println("dependency cycle!") + for _, v := range b.Values { + if printed[v.ID] { + continue + } + fmt.Print(" ") + fmt.Println(v.LongString()) + printed[v.ID] = true + n++ + } + } + } + + fmt.Println(" " + b.LongString()) + } +} diff --git a/src/cmd/internal/ssa/sparseset.go b/src/cmd/internal/ssa/sparseset.go new file mode 100644 index 0000000000..e1f9a9a81d --- /dev/null +++ b/src/cmd/internal/ssa/sparseset.go @@ -0,0 +1,60 @@ +// 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 ssa + +// from http://research.swtch.com/sparse +// in turn, from Briggs and Torczon + +type sparseSet struct { + dense []ID + sparse []int +} + +// newSparseSet returns a sparseSet that can represent +// integers between 0 and n-1 +func newSparseSet(n int) *sparseSet { + return &sparseSet{nil, make([]int, n)} +} + +func (s *sparseSet) size() int { + return len(s.dense) +} + +func (s *sparseSet) contains(x ID) bool { + i := s.sparse[x] + return i < len(s.dense) && s.dense[i] == x +} + +func (s *sparseSet) add(x ID) { + i := len(s.dense) + s.dense = append(s.dense, x) + s.sparse[x] = i +} + +func (s *sparseSet) remove(x ID) { + i := s.sparse[x] + if i < len(s.dense) && s.dense[i] == x { + y := s.dense[len(s.dense)-1] + s.dense[i] = y + s.sparse[y] = i + s.dense = s.dense[:len(s.dense)-1] + } +} + +// pop removes an arbitrary element from the set. +// The set must be nonempty. +func (s *sparseSet) pop() ID { + x := s.dense[len(s.dense)-1] + s.dense = s.dense[:len(s.dense)-1] + return x +} + +func (s *sparseSet) clear() { + s.dense = s.dense[:0] +} + +func (s *sparseSet) contents() []ID { + return s.dense +} diff --git a/src/cmd/internal/ssa/ssac/.gitignore b/src/cmd/internal/ssa/ssac/.gitignore new file mode 100644 index 0000000000..ab17b9d28e --- /dev/null +++ b/src/cmd/internal/ssa/ssac/.gitignore @@ -0,0 +1 @@ +ssac diff --git a/src/cmd/internal/ssa/ssac/fib.goir b/src/cmd/internal/ssa/ssac/fib.goir new file mode 100644 index 0000000000..b572cdaa3a --- /dev/null +++ b/src/cmd/internal/ssa/ssac/fib.goir @@ -0,0 +1,46 @@ + (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) + (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 (SP 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 (SP T127bd68 8) ~r1) + (RETURN) diff --git a/src/cmd/internal/ssa/ssac/fibiter.goir b/src/cmd/internal/ssa/ssac/fibiter.goir new file mode 100644 index 0000000000..43c7a3de91 --- /dev/null +++ b/src/cmd/internal/ssa/ssac/fibiter.goir @@ -0,0 +1,62 @@ + (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 (SP 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 (SP Tf5dd68 8) a) + (RETURN) diff --git a/src/cmd/internal/ssa/ssac/main.go b/src/cmd/internal/ssa/ssac/main.go new file mode 100644 index 0000000000..4975b50db4 --- /dev/null +++ b/src/cmd/internal/ssa/ssac/main.go @@ -0,0 +1,436 @@ +// 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/types" + + "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 thunks to their actual values + for _, v := range b.Values { + if v.Op != ssa.OpThunk { + 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(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 thunk for this variable. + fmt.Printf("making thunk for var=%d in block=%d\n", id, b.ID) + v = b.NewValue(ssa.OpThunk, 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 thunk, 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": + return ssa.TypeInt + 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") +} diff --git a/src/cmd/internal/ssa/ssac/sexpr.go b/src/cmd/internal/ssa/ssac/sexpr.go new file mode 100644 index 0000000000..77e8923dd0 --- /dev/null +++ b/src/cmd/internal/ssa/ssac/sexpr.go @@ -0,0 +1,82 @@ +// 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++ + } +} diff --git a/src/cmd/internal/ssa/ssac/sparsemap.go b/src/cmd/internal/ssa/ssac/sparsemap.go new file mode 100644 index 0000000000..b7a0fb0fde --- /dev/null +++ b/src/cmd/internal/ssa/ssac/sparsemap.go @@ -0,0 +1,69 @@ +// 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 +} diff --git a/src/cmd/internal/ssa/type.go b/src/cmd/internal/ssa/type.go new file mode 100644 index 0000000000..3389622c74 --- /dev/null +++ b/src/cmd/internal/ssa/type.go @@ -0,0 +1,84 @@ +// 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 ssa + +import ( + "cmd/internal/ssa/types" // TODO: use golang.org/x/tools/go/types instead +) + +// We just inherit types from go/types +type Type types.Type + +var ( + // shortcuts for commonly used basic types + TypeInt = types.Typ[types.Int] + TypeUint = types.Typ[types.Uint] + TypeInt8 = types.Typ[types.Int8] + TypeInt16 = types.Typ[types.Int16] + TypeInt32 = types.Typ[types.Int32] + TypeInt64 = types.Typ[types.Int64] + TypeUint8 = types.Typ[types.Uint8] + TypeUint16 = types.Typ[types.Uint16] + TypeUint32 = types.Typ[types.Uint32] + TypeUint64 = types.Typ[types.Uint64] + TypeUintptr = types.Typ[types.Uintptr] + TypeBool = types.Typ[types.Bool] + TypeString = types.Typ[types.String] + + TypeInvalid = types.Typ[types.Invalid] + + // Additional compiler-only types go here. + TypeMem = &Memory{} + TypeFlags = &Flags{} +) + +// typeIdentical returns whether it two arguments are the same type. +func typeIdentical(t, u Type) bool { + if t == TypeMem { + return u == TypeMem + } + if t == TypeFlags { + return u == TypeFlags + } + return types.Identical(t, u) +} + +// A type representing all of memory +type Memory struct { +} + +func (t *Memory) Underlying() types.Type { panic("Underlying of Memory") } +func (t *Memory) String() string { return "mem" } + +// A type representing the unknown type +type Unknown struct { +} + +func (t *Unknown) Underlying() types.Type { panic("Underlying of Unknown") } +func (t *Unknown) String() string { return "unk" } + +// A type representing the void type. Used during building, should always +// be eliminated by the first deadcode pass. +type Void struct { +} + +func (t *Void) Underlying() types.Type { panic("Underlying of Void") } +func (t *Void) String() string { return "void" } + +// A type representing the results of a nil check or bounds check. +// TODO: or type check? +// TODO: just use bool? +type Check struct { +} + +func (t *Check) Underlying() types.Type { panic("Underlying of Check") } +func (t *Check) String() string { return "check" } + +// x86 flags type +type Flags struct { +} + +func (t *Flags) Underlying() types.Type { panic("Underlying of Flags") } +func (t *Flags) String() string { return "flags" } diff --git a/src/cmd/internal/ssa/types/object.go b/src/cmd/internal/ssa/types/object.go new file mode 100644 index 0000000000..cd0be163b7 --- /dev/null +++ b/src/cmd/internal/ssa/types/object.go @@ -0,0 +1,39 @@ +// 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. + +// This package is a drop-in replacement for go/types +// for use until go/types is included in the main repo. + +package types + +// An Object describes a named language entity such as a package, +// constant, type, variable, function (incl. methods), or label. +// All objects implement the Object interface. +// +type Object interface { + Name() string // package local object name + Type() Type // object type +} + +// An object implements the common parts of an Object. +type object struct { + name string + typ Type +} + +func (obj *object) Name() string { return obj.name } +func (obj *object) Type() Type { return obj.typ } + +// A Variable represents a declared variable (including function parameters and results, and struct fields). +type Var struct { + object + anonymous bool // if set, the variable is an anonymous struct field, and name is the type name + visited bool // for initialization cycle detection + isField bool // var is struct field + used bool // set if the variable was used +} + +func NewParam(pos int, pkg *int, name string, typ Type) *Var { + return &Var{object: object{name, typ}, used: true} // parameters are always 'used' +} diff --git a/src/cmd/internal/ssa/types/type.go b/src/cmd/internal/ssa/types/type.go new file mode 100644 index 0000000000..e01de5c1e4 --- /dev/null +++ b/src/cmd/internal/ssa/types/type.go @@ -0,0 +1,229 @@ +// 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. + +// This package is a drop-in replacement for go/types +// for use until go/types is included in the main repo. + +package types + +// A Type represents a type of Go. +// All types implement the Type interface. +type Type interface { + // Underlying returns the underlying type of a type. + Underlying() Type + + // String returns a string representation of a type. + String() string +} + +// BasicKind describes the kind of basic type. +type BasicKind int + +const ( + Invalid BasicKind = iota // type is invalid + + // predeclared types + Bool + Int + Int8 + Int16 + Int32 + Int64 + Uint + Uint8 + Uint16 + Uint32 + Uint64 + Uintptr + Float32 + Float64 + Complex64 + Complex128 + String + UnsafePointer + + // types for untyped values + UntypedBool + UntypedInt + UntypedRune + UntypedFloat + UntypedComplex + UntypedString + UntypedNil + + // aliases + Byte = Uint8 + Rune = Int32 +) + +// BasicInfo is a set of flags describing properties of a basic type. +type BasicInfo int + +// Properties of basic types. +const ( + IsBoolean BasicInfo = 1 << iota + IsInteger + IsUnsigned + IsFloat + IsComplex + IsString + IsUntyped + + IsOrdered = IsInteger | IsFloat | IsString + IsNumeric = IsInteger | IsFloat | IsComplex + IsConstType = IsBoolean | IsNumeric | IsString +) + +// A Basic represents a basic type. +type Basic struct { + kind BasicKind + info BasicInfo + name string +} + +// Kind returns the kind of basic type b. +func (b *Basic) Kind() BasicKind { return b.kind } + +// Info returns information about properties of basic type b. +func (b *Basic) Info() BasicInfo { return b.info } + +// Name returns the name of basic type b. +func (b *Basic) Name() string { return b.name } + +// A Pointer represents a pointer type. +type Pointer struct { + base Type // element type +} + +// NewPointer returns a new pointer type for the given element (base) type. +func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} } + +// Elem returns the element type for the given pointer p. +func (p *Pointer) Elem() Type { return p.base } + +// A Slice represents a slice type. +type Slice struct { + elem Type +} + +// NewSlice returns a new slice type for the given element type. +func NewSlice(elem Type) *Slice { return &Slice{elem} } + +// Elem returns the element type of slice s. +func (s *Slice) Elem() Type { return s.elem } + +// Implementations for Type methods. +func (t *Basic) Underlying() Type { return t } +func (t *Slice) Underlying() Type { return t } +func (t *Pointer) Underlying() Type { return t } +func (t *Signature) Underlying() Type { return t } + +func (b *Basic) String() string { return b.name } +func (t *Slice) String() string { return "[]" + t.elem.String() } +func (t *Pointer) String() string { return "*" + t.base.String() } +func (t *Signature) String() string { return "sig" /* TODO */ } + +var Typ = [...]*Basic{ + Invalid: {Invalid, 0, "invalid type"}, + + Bool: {Bool, IsBoolean, "bool"}, + Int: {Int, IsInteger, "int"}, + Int8: {Int8, IsInteger, "int8"}, + Int16: {Int16, IsInteger, "int16"}, + Int32: {Int32, IsInteger, "int32"}, + Int64: {Int64, IsInteger, "int64"}, + Uint: {Uint, IsInteger | IsUnsigned, "uint"}, + Uint8: {Uint8, IsInteger | IsUnsigned, "uint8"}, + Uint16: {Uint16, IsInteger | IsUnsigned, "uint16"}, + Uint32: {Uint32, IsInteger | IsUnsigned, "uint32"}, + Uint64: {Uint64, IsInteger | IsUnsigned, "uint64"}, + Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr"}, + Float32: {Float32, IsFloat, "float32"}, + Float64: {Float64, IsFloat, "float64"}, + Complex64: {Complex64, IsComplex, "complex64"}, + Complex128: {Complex128, IsComplex, "complex128"}, + String: {String, IsString, "string"}, + UnsafePointer: {UnsafePointer, 0, "Pointer"}, + + UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool"}, + UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int"}, + UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune"}, + UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float"}, + UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"}, + UntypedString: {UntypedString, IsString | IsUntyped, "untyped string"}, + UntypedNil: {UntypedNil, IsUntyped, "untyped nil"}, +} + +// Identical reports whether x and y are identical. +func Identical(x, y Type) bool { + if x == y { + return true + } + + switch x := x.(type) { + case *Basic: + // Basic types are singletons except for the rune and byte + // aliases, thus we cannot solely rely on the x == y check + // above. + if y, ok := y.(*Basic); ok { + return x.kind == y.kind + } + default: + panic("can't handle yet") + } + return false +} + +// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple. +// Tuples are used as components of signatures and to represent the type of multiple +// assignments; they are not first class types of Go. +type Tuple struct { + vars []*Var +} + +// NewTuple returns a new tuple for the given variables. +func NewTuple(x ...*Var) *Tuple { + if len(x) > 0 { + return &Tuple{x} + } + return nil +} + +// Len returns the number variables of tuple t. +func (t *Tuple) Len() int { + if t != nil { + return len(t.vars) + } + return 0 +} + +// At returns the i'th variable of tuple t. +func (t *Tuple) At(i int) *Var { return t.vars[i] } + +// A Signature represents a (non-builtin) function or method type. +type Signature struct { + recv *Var // nil if not a method + params *Tuple // (incoming) parameters from left to right; or nil + results *Tuple // (outgoing) results from left to right; or nil + variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) +} + +// NewSignature returns a new function type for the given receiver, parameters, +// and results, either of which may be nil. If variadic is set, the function +// is variadic, it must have at least one parameter, and the last parameter +// must be of unnamed slice type. +func NewSignature(scope *int, recv *Var, params, results *Tuple, variadic bool) *Signature { + // TODO(gri) Should we rely on the correct (non-nil) incoming scope + // or should this function allocate and populate a scope? + if variadic { + n := params.Len() + if n == 0 { + panic("types.NewSignature: variadic function must have at least one parameter") + } + if _, ok := params.At(n - 1).typ.(*Slice); !ok { + panic("types.NewSignature: variadic parameter must be of unnamed slice type") + } + } + return &Signature{recv, params, results, variadic} +} diff --git a/src/cmd/internal/ssa/value.go b/src/cmd/internal/ssa/value.go new file mode 100644 index 0000000000..740525a5f5 --- /dev/null +++ b/src/cmd/internal/ssa/value.go @@ -0,0 +1,117 @@ +// 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 ssa + +import ( + "fmt" + "strings" +) + +// A Value represents a value in the SSA representation of the program. +// The ID and Type fields must not be modified. The remainder may be modified +// if they preserve the value of the Value (e.g. changing a (mul 2 x) to an (add x x)). +type Value struct { + // A unique identifier for the value. For performance we allocate these IDs + // densely starting at 0. There is no guarantee that there won't be occasional holes, though. + ID ID + + // The operation that computes this value. See op.go. + Op Op + + // The type of this value. Normally this will be a Go type, but there + // are a few other pseudo-types, see type.go. + Type Type + + // Auxiliary info for this value. The type of this information depends on the opcode (& type). + Aux interface{} + + // Arguments of this value + Args []*Value + + // Containing basic block + Block *Block + + // Storage for the first two args + argstorage [2]*Value +} + +// Examples: +// Opcode aux args +// OpAdd nil 2 +// OpConstStr string 0 +// OpConstInt int64 0 +// OpAddcq int64 1 amd64 op: v = arg[0] + constant + +// short form print. Just v#. +func (v *Value) String() string { + return fmt.Sprintf("v%d", v.ID) +} + +// long form print. v# = opcode [aux] args [: reg] +func (v *Value) LongString() string { + s := fmt.Sprintf("v%d = %s", v.ID, strings.TrimPrefix(v.Op.String(), "Op")) + s += " <" + v.Type.String() + ">" + if v.Aux != nil { + s += fmt.Sprintf(" [%v]", v.Aux) + } + for _, a := range v.Args { + s += fmt.Sprintf(" %v", a) + } + r := v.Block.Func.RegAlloc + if r != nil && r[v.ID] != nil { + s += " : " + r[v.ID].Name() + } + return s +} + +func (v *Value) AddArg(w *Value) { + v.Args = append(v.Args, w) +} +func (v *Value) AddArgs(a ...*Value) { + v.Args = append(v.Args, a...) +} +func (v *Value) SetArg(i int, w *Value) { + v.Args[i] = w +} +func (v *Value) RemoveArg(i int) { + copy(v.Args[i:], v.Args[i+1:]) + v.Args = v.Args[:len(v.Args)-1] +} +func (v *Value) SetArgs1(a *Value) { + v.resetArgs() + v.AddArg(a) +} +func (v *Value) SetArgs2(a *Value, b *Value) { + v.resetArgs() + v.AddArg(a) + v.AddArg(b) +} + +func (v *Value) resetArgs() { + v.argstorage[0] = nil + v.argstorage[1] = nil + v.Args = v.argstorage[:0] +} + +// CopyFrom converts v to be the same value as w. v and w must +// have the same type. +func (v *Value) CopyFrom(w *Value) { + if !typeIdentical(v.Type, w.Type) { + panic("copyFrom with unequal types") + } + v.Op = w.Op + v.Aux = w.Aux + v.resetArgs() + v.AddArgs(w.Args...) +} + +// SetType sets the type of v. v must not have had its type +// set yet (it must be TypeInvalid). +func (v *Value) SetType() { + if v.Type != TypeInvalid { + panic("setting type when it is already set") + } + opcodeTable[v.Op].typer(v) +}