go/src/cmd/compile/internal/ssa/func.go

313 lines
8.2 KiB
Go
Raw Normal View History

// 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 (
"math"
"sync"
)
// A Func represents a Go func declaration (or function literal) and
// its body. This package compiles each Func independently.
type Func struct {
Config *Config // architecture information
Name string // e.g. bytes·Compare
Type Type // type signature of the function.
StaticData interface{} // associated static data, untouched by the ssa package
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
scheduled bool // Values in Blocks are in final order
// when register allocation is done, maps value ids to locations
RegAlloc []Location
// map from LocalSlot to set of Values that we want to store in that slot.
NamedValues map[LocalSlot][]*Value
// Names is a copy of NamedValues.Keys. We keep a separate list
// of keys to make iteration order deterministic.
Names []LocalSlot
}
// 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()
}
const (
blockSize = 100
)
// blockPool provides a contiguous array of Blocks which
// improves the speed of traversing dominator trees.
type blockPool struct {
blocks []Block
mu sync.Mutex
}
func (bp *blockPool) newBlock() *Block {
bp.mu.Lock()
defer bp.mu.Unlock()
if len(bp.blocks) == 0 {
bp.blocks = make([]Block, blockSize, blockSize)
}
res := &bp.blocks[0]
bp.blocks = bp.blocks[1:]
return res
}
var bp blockPool
// NewBlock returns a new block of the given kind and appends it to f.Blocks.
func (f *Func) NewBlock(kind BlockKind) *Block {
b := bp.newBlock()
b.ID = f.bid.get()
b.Kind = kind
b.Func = f
f.Blocks = append(f.Blocks, b)
return b
}
// NewValue0 returns a new value in the block with no arguments and zero aux values.
func (b *Block) NewValue0(line int32, op Op, t Type) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Block: b,
Line: line,
}
v.Args = v.argstorage[:0]
b.Values = append(b.Values, v)
return v
}
// NewValue returns a new value in the block with no arguments and an auxint value.
func (b *Block) NewValue0I(line int32, op Op, t Type, auxint int64) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
Block: b,
Line: line,
}
v.Args = v.argstorage[:0]
b.Values = append(b.Values, v)
return v
}
// NewValue returns a new value in the block with no arguments and an aux value.
func (b *Block) NewValue0A(line int32, op Op, t Type, aux interface{}) *Value {
if _, ok := aux.(int64); ok {
// Disallow int64 aux values. They should be in the auxint field instead.
// Maybe we want to allow this at some point, but for now we disallow it
// to prevent errors like using NewValue1A instead of NewValue1I.
b.Fatalf("aux field has int64 type op=%s type=%s aux=%v", op, t, aux)
}
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Aux: aux,
Block: b,
Line: line,
}
v.Args = v.argstorage[:0]
b.Values = append(b.Values, v)
return v
}
// NewValue returns a new value in the block with no arguments and both an auxint and aux values.
func (b *Block) NewValue0IA(line int32, op Op, t Type, auxint int64, aux interface{}) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
Aux: aux,
Block: b,
Line: line,
}
v.Args = v.argstorage[:0]
b.Values = append(b.Values, v)
return v
}
// NewValue1 returns a new value in the block with one argument and zero aux values.
func (b *Block) NewValue1(line int32, op Op, t Type, arg *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Block: b,
Line: line,
}
v.Args = v.argstorage[:1]
v.Args[0] = arg
b.Values = append(b.Values, v)
return v
}
// NewValue1I returns a new value in the block with one argument and an auxint value.
func (b *Block) NewValue1I(line int32, op Op, t Type, auxint int64, arg *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
Block: b,
Line: line,
}
v.Args = v.argstorage[:1]
v.Args[0] = arg
b.Values = append(b.Values, v)
return v
}
// NewValue1A returns a new value in the block with one argument and an aux value.
func (b *Block) NewValue1A(line int32, op Op, t Type, aux interface{}, arg *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Aux: aux,
Block: b,
Line: line,
}
v.Args = v.argstorage[:1]
v.Args[0] = arg
b.Values = append(b.Values, v)
return v
}
// NewValue1IA returns a new value in the block with one argument and both an auxint and aux values.
func (b *Block) NewValue1IA(line int32, op Op, t Type, auxint int64, aux interface{}, arg *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: auxint,
Aux: aux,
Block: b,
Line: line,
}
v.Args = v.argstorage[:1]
v.Args[0] = arg
b.Values = append(b.Values, v)
return v
}
// NewValue2 returns a new value in the block with two arguments and zero aux values.
func (b *Block) NewValue2(line int32, op Op, t Type, arg0, arg1 *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Block: b,
Line: line,
}
v.Args = v.argstorage[:2]
v.Args[0] = arg0
v.Args[1] = arg1
b.Values = append(b.Values, v)
return v
}
// NewValue2I returns a new value in the block with two arguments and an auxint value.
func (b *Block) NewValue2I(line int32, op Op, t Type, aux int64, arg0, arg1 *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: aux,
Block: b,
Line: line,
}
v.Args = v.argstorage[:2]
v.Args[0] = arg0
v.Args[1] = arg1
b.Values = append(b.Values, v)
return v
}
// NewValue3 returns a new value in the block with three arguments and zero aux values.
func (b *Block) NewValue3(line int32, op Op, t Type, arg0, arg1, arg2 *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
Block: b,
Line: line,
}
v.Args = []*Value{arg0, arg1, arg2}
b.Values = append(b.Values, v)
return v
}
// NewValue3I returns a new value in the block with three arguments and an auxint value.
func (b *Block) NewValue3I(line int32, op Op, t Type, aux int64, arg0, arg1, arg2 *Value) *Value {
v := &Value{
ID: b.Func.vid.get(),
Op: op,
Type: t,
AuxInt: aux,
Block: b,
Line: line,
}
v.Args = []*Value{arg0, arg1, arg2}
b.Values = append(b.Values, v)
return v
}
// ConstInt returns an int constant representing its argument.
func (f *Func) ConstBool(line int32, t Type, c bool) *Value {
// TODO: cache?
i := int64(0)
if c {
i = 1
}
return f.Entry.NewValue0I(line, OpConstBool, t, i)
}
func (f *Func) ConstInt8(line int32, t Type, c int8) *Value {
// TODO: cache?
return f.Entry.NewValue0I(line, OpConst8, t, int64(c))
}
func (f *Func) ConstInt16(line int32, t Type, c int16) *Value {
// TODO: cache?
return f.Entry.NewValue0I(line, OpConst16, t, int64(c))
}
func (f *Func) ConstInt32(line int32, t Type, c int32) *Value {
// TODO: cache?
return f.Entry.NewValue0I(line, OpConst32, t, int64(c))
}
func (f *Func) ConstInt64(line int32, t Type, c int64) *Value {
// TODO: cache?
return f.Entry.NewValue0I(line, OpConst64, t, c)
}
func (f *Func) ConstFloat32(line int32, t Type, c float64) *Value {
// TODO: cache?
return f.Entry.NewValue0I(line, OpConst32F, t, int64(math.Float64bits(c)))
}
func (f *Func) ConstFloat64(line int32, t Type, c float64) *Value {
// TODO: cache?
return f.Entry.NewValue0I(line, OpConst64F, t, int64(math.Float64bits(c)))
}
[dev.ssa] cmd/compile/ssa: separate logging, work in progress, and fatal errors The SSA implementation logs for three purposes: * debug logging * fatal errors * unimplemented features Separating these three uses lets us attempt an SSA implementation for all functions, not just _ssa functions. This turns the entire standard library into a compilation test, and makes it easy to figure out things like "how much coverage does SSA have now" and "what should we do next to get more coverage?". Functions called _ssa are still special. They log profusely by default and the output of the SSA implementation is used. For all other functions, logging is off, and the implementation is built and discarded, due to lack of support for the runtime. While we're here, fix a few minor bugs and add some extra Unimplementeds to allow all.bash to pass. As of now, SSA handles 20.79% of the functions in the standard library (689 of 3314). The top missing features are: 10.03% 2597 SSA unimplemented: zero for type error not implemented 7.79% 2016 SSA unimplemented: addr: bad op DOTPTR 7.33% 1898 SSA unimplemented: unhandled expr EQ 6.10% 1579 SSA unimplemented: unhandled expr OROR 4.91% 1271 SSA unimplemented: unhandled expr NE 4.49% 1163 SSA unimplemented: unhandled expr LROT 4.00% 1036 SSA unimplemented: unhandled expr LEN 3.56% 923 SSA unimplemented: unhandled stmt CALLFUNC 2.37% 615 SSA unimplemented: zero for type []byte not implemented 1.90% 492 SSA unimplemented: unhandled stmt CALLMETH 1.74% 450 SSA unimplemented: unhandled expr CALLINTER 1.74% 450 SSA unimplemented: unhandled expr DOT 1.71% 444 SSA unimplemented: unhandled expr ANDAND 1.65% 426 SSA unimplemented: unhandled expr CLOSUREVAR 1.54% 400 SSA unimplemented: unhandled expr CALLMETH 1.51% 390 SSA unimplemented: unhandled stmt SWITCH 1.47% 380 SSA unimplemented: unhandled expr CONV 1.33% 345 SSA unimplemented: addr: bad op * 1.30% 336 SSA unimplemented: unhandled OLITERAL 6 Change-Id: I4ca07951e276714dc13c31de28640aead17a1be7 Reviewed-on: https://go-review.googlesource.com/11160 Reviewed-by: Keith Randall <khr@golang.org>
2015-06-12 11:01:13 -07:00
func (f *Func) Logf(msg string, args ...interface{}) { f.Config.Logf(msg, args...) }
func (f *Func) Fatalf(msg string, args ...interface{}) { f.Config.Fatalf(f.Entry.Line, msg, args...) }
func (f *Func) Unimplementedf(msg string, args ...interface{}) {
f.Config.Unimplementedf(f.Entry.Line, msg, args...)
}