mirror of
https://github.com/golang/go
synced 2024-10-06 07:21:22 -06:00
[dev.ssa] cmd/internal/ssa: SSA cleanups
Mostly suggested by Alan. Convert Const* ops to just one Const op. Use more of go/types. Get rid of typers, all types must be specified explicitly. Change-Id: Id4758f2b887d8a6888e88a7e047d97af55e34b62 Reviewed-on: https://go-review.googlesource.com/8110 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
7b96284295
commit
2c9b491e01
@ -54,6 +54,7 @@ const (
|
||||
BlockPlain // a single successor
|
||||
BlockIf // 2 successors, if control goto Succs[0] else goto Succs[1]
|
||||
BlockCall // 2 successors, normal return and panic
|
||||
// TODO(khr): BlockPanic for the built-in panic call, has 1 edge to the exit block
|
||||
BlockUnknown
|
||||
|
||||
// 386/amd64 variants of BlockIf that take the flags register as an arg
|
||||
|
@ -79,7 +79,7 @@ var passOrder = map[string]string{
|
||||
// regalloc requires all the values in a block to be scheduled
|
||||
//"schedule": "regalloc",
|
||||
// code generation requires register allocation
|
||||
//"cgen":"regalloc",
|
||||
//"regalloc": "cgen",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -20,7 +20,7 @@ func deadcode(f *Func) {
|
||||
|
||||
// constant-fold conditionals
|
||||
// TODO: rewrite rules instead?
|
||||
if b.Kind == BlockIf && b.Control.Op == OpConstBool {
|
||||
if b.Kind == BlockIf && b.Control.Op == OpConst {
|
||||
cond := b.Control.Aux.(bool)
|
||||
var c *Block
|
||||
if cond {
|
||||
|
@ -57,5 +57,5 @@ func (b *Block) NewValue(op Op, t Type, aux interface{}) *Value {
|
||||
func (f *Func) ConstInt(c int64) *Value {
|
||||
// TODO: cache?
|
||||
// TODO: different types?
|
||||
return f.Entry.NewValue(OpConstInt, TypeInt, c)
|
||||
return f.Entry.NewValue(OpConst, TypeInt64, c)
|
||||
}
|
||||
|
@ -5,23 +5,23 @@ package ssa
|
||||
func genericRules(v *Value) bool {
|
||||
switch v.Op {
|
||||
case OpAdd:
|
||||
// match: (Add <t> (ConstInt [c]) (ConstInt [d]))
|
||||
// cond: is64BitInt(t)
|
||||
// result: (ConstInt [{c.(int64)+d.(int64)}])
|
||||
// match: (Add <t> (Const [c]) (Const [d]))
|
||||
// cond: is64BitInt(t) && isSigned(t)
|
||||
// result: (Const [{c.(int64)+d.(int64)}])
|
||||
{
|
||||
t := v.Type
|
||||
if v.Args[0].Op != OpConstInt {
|
||||
if v.Args[0].Op != OpConst {
|
||||
goto end0
|
||||
}
|
||||
c := v.Args[0].Aux
|
||||
if v.Args[1].Op != OpConstInt {
|
||||
if v.Args[1].Op != OpConst {
|
||||
goto end0
|
||||
}
|
||||
d := v.Args[1].Aux
|
||||
if !(is64BitInt(t)) {
|
||||
if !(is64BitInt(t) && isSigned(t)) {
|
||||
goto end0
|
||||
}
|
||||
v.Op = OpConstInt
|
||||
v.Op = OpConst
|
||||
v.Aux = nil
|
||||
v.Args = v.argstorage[:0]
|
||||
v.Aux = c.(int64) + d.(int64)
|
||||
@ -29,13 +29,37 @@ func genericRules(v *Value) bool {
|
||||
}
|
||||
end0:
|
||||
;
|
||||
// match: (Add <t> (Const [c]) (Const [d]))
|
||||
// cond: is64BitInt(t) && !isSigned(t)
|
||||
// result: (Const [{c.(uint64)+d.(uint64)}])
|
||||
{
|
||||
t := v.Type
|
||||
if v.Args[0].Op != OpConst {
|
||||
goto end1
|
||||
}
|
||||
c := v.Args[0].Aux
|
||||
if v.Args[1].Op != OpConst {
|
||||
goto end1
|
||||
}
|
||||
d := v.Args[1].Aux
|
||||
if !(is64BitInt(t) && !isSigned(t)) {
|
||||
goto end1
|
||||
}
|
||||
v.Op = OpConst
|
||||
v.Aux = nil
|
||||
v.Args = v.argstorage[:0]
|
||||
v.Aux = c.(uint64) + d.(uint64)
|
||||
return true
|
||||
}
|
||||
end1:
|
||||
;
|
||||
case OpLoad:
|
||||
// match: (Load (FPAddr [offset]) mem)
|
||||
// cond:
|
||||
// result: (LoadFP [offset] mem)
|
||||
{
|
||||
if v.Args[0].Op != OpFPAddr {
|
||||
goto end1
|
||||
goto end2
|
||||
}
|
||||
offset := v.Args[0].Aux
|
||||
mem := v.Args[1]
|
||||
@ -46,14 +70,14 @@ func genericRules(v *Value) bool {
|
||||
v.AddArg(mem)
|
||||
return true
|
||||
}
|
||||
end1:
|
||||
end2:
|
||||
;
|
||||
// match: (Load (SPAddr [offset]) mem)
|
||||
// cond:
|
||||
// result: (LoadSP [offset] mem)
|
||||
{
|
||||
if v.Args[0].Op != OpSPAddr {
|
||||
goto end2
|
||||
goto end3
|
||||
}
|
||||
offset := v.Args[0].Aux
|
||||
mem := v.Args[1]
|
||||
@ -64,7 +88,7 @@ func genericRules(v *Value) bool {
|
||||
v.AddArg(mem)
|
||||
return true
|
||||
}
|
||||
end2:
|
||||
end3:
|
||||
;
|
||||
case OpStore:
|
||||
// match: (Store (FPAddr [offset]) val mem)
|
||||
@ -72,7 +96,7 @@ func genericRules(v *Value) bool {
|
||||
// result: (StoreFP [offset] val mem)
|
||||
{
|
||||
if v.Args[0].Op != OpFPAddr {
|
||||
goto end3
|
||||
goto end4
|
||||
}
|
||||
offset := v.Args[0].Aux
|
||||
val := v.Args[1]
|
||||
@ -85,14 +109,14 @@ func genericRules(v *Value) bool {
|
||||
v.AddArg(mem)
|
||||
return true
|
||||
}
|
||||
end3:
|
||||
end4:
|
||||
;
|
||||
// match: (Store (SPAddr [offset]) val mem)
|
||||
// cond:
|
||||
// result: (StoreSP [offset] val mem)
|
||||
{
|
||||
if v.Args[0].Op != OpSPAddr {
|
||||
goto end4
|
||||
goto end5
|
||||
}
|
||||
offset := v.Args[0].Aux
|
||||
val := v.Args[1]
|
||||
@ -105,7 +129,7 @@ func genericRules(v *Value) bool {
|
||||
v.AddArg(mem)
|
||||
return true
|
||||
}
|
||||
end4:
|
||||
end5:
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -5,12 +5,12 @@ package ssa
|
||||
func lowerAmd64(v *Value) bool {
|
||||
switch v.Op {
|
||||
case OpADDQ:
|
||||
// match: (ADDQ x (ConstInt [c]))
|
||||
// match: (ADDQ x (Const [c]))
|
||||
// cond:
|
||||
// result: (ADDCQ [c] x)
|
||||
{
|
||||
x := v.Args[0]
|
||||
if v.Args[1].Op != OpConstInt {
|
||||
if v.Args[1].Op != OpConst {
|
||||
goto end0
|
||||
}
|
||||
c := v.Args[1].Aux
|
||||
@ -23,11 +23,11 @@ func lowerAmd64(v *Value) bool {
|
||||
}
|
||||
end0:
|
||||
;
|
||||
// match: (ADDQ (ConstInt [c]) x)
|
||||
// match: (ADDQ (Const [c]) x)
|
||||
// cond:
|
||||
// result: (ADDCQ [c] x)
|
||||
{
|
||||
if v.Args[0].Op != OpConstInt {
|
||||
if v.Args[0].Op != OpConst {
|
||||
goto end1
|
||||
}
|
||||
c := v.Args[0].Aux
|
||||
@ -81,12 +81,12 @@ func lowerAmd64(v *Value) bool {
|
||||
end3:
|
||||
;
|
||||
case OpCMPQ:
|
||||
// match: (CMPQ x (ConstInt [c]))
|
||||
// match: (CMPQ x (Const [c]))
|
||||
// cond:
|
||||
// result: (CMPCQ x [c])
|
||||
{
|
||||
x := v.Args[0]
|
||||
if v.Args[1].Op != OpConstInt {
|
||||
if v.Args[1].Op != OpConst {
|
||||
goto end4
|
||||
}
|
||||
c := v.Args[1].Aux
|
||||
@ -99,11 +99,11 @@ func lowerAmd64(v *Value) bool {
|
||||
}
|
||||
end4:
|
||||
;
|
||||
// match: (CMPQ (ConstInt [c]) x)
|
||||
// match: (CMPQ (Const [c]) x)
|
||||
// cond:
|
||||
// result: (InvertFlags (CMPCQ x [c]))
|
||||
// result: (InvertFlags (CMPCQ <TypeFlags> x [c]))
|
||||
{
|
||||
if v.Args[0].Op != OpConstInt {
|
||||
if v.Args[0].Op != OpConst {
|
||||
goto end5
|
||||
}
|
||||
c := v.Args[0].Aux
|
||||
@ -112,9 +112,9 @@ func lowerAmd64(v *Value) bool {
|
||||
v.Aux = nil
|
||||
v.Args = v.argstorage[:0]
|
||||
v0 := v.Block.NewValue(OpCMPCQ, TypeInvalid, nil)
|
||||
v0.Type = TypeFlags
|
||||
v0.AddArg(x)
|
||||
v0.Aux = c
|
||||
v0.SetType()
|
||||
v.AddArg(v0)
|
||||
return true
|
||||
}
|
||||
@ -123,7 +123,7 @@ func lowerAmd64(v *Value) bool {
|
||||
case OpLess:
|
||||
// match: (Less x y)
|
||||
// cond: is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type)
|
||||
// result: (SETL (CMPQ x y))
|
||||
// result: (SETL (CMPQ <TypeFlags> x y))
|
||||
{
|
||||
x := v.Args[0]
|
||||
y := v.Args[1]
|
||||
@ -134,9 +134,9 @@ func lowerAmd64(v *Value) bool {
|
||||
v.Aux = nil
|
||||
v.Args = v.argstorage[:0]
|
||||
v0 := v.Block.NewValue(OpCMPQ, TypeInvalid, nil)
|
||||
v0.Type = TypeFlags
|
||||
v0.AddArg(x)
|
||||
v0.AddArg(y)
|
||||
v0.SetType()
|
||||
v.AddArg(v0)
|
||||
return true
|
||||
}
|
||||
@ -202,12 +202,12 @@ func lowerAmd64(v *Value) bool {
|
||||
end9:
|
||||
;
|
||||
case OpSUBQ:
|
||||
// match: (SUBQ x (ConstInt [c]))
|
||||
// match: (SUBQ x (Const [c]))
|
||||
// cond:
|
||||
// result: (SUBCQ x [c])
|
||||
{
|
||||
x := v.Args[0]
|
||||
if v.Args[1].Op != OpConstInt {
|
||||
if v.Args[1].Op != OpConst {
|
||||
goto end10
|
||||
}
|
||||
c := v.Args[1].Aux
|
||||
@ -220,11 +220,12 @@ func lowerAmd64(v *Value) bool {
|
||||
}
|
||||
end10:
|
||||
;
|
||||
// match: (SUBQ (ConstInt [c]) x)
|
||||
// match: (SUBQ <t> (Const [c]) x)
|
||||
// cond:
|
||||
// result: (NEGQ (SUBCQ x [c]))
|
||||
// result: (NEGQ (SUBCQ <t> x [c]))
|
||||
{
|
||||
if v.Args[0].Op != OpConstInt {
|
||||
t := v.Type
|
||||
if v.Args[0].Op != OpConst {
|
||||
goto end11
|
||||
}
|
||||
c := v.Args[0].Aux
|
||||
@ -233,9 +234,9 @@ func lowerAmd64(v *Value) bool {
|
||||
v.Aux = nil
|
||||
v.Args = v.argstorage[:0]
|
||||
v0 := v.Block.NewValue(OpSUBCQ, TypeInvalid, nil)
|
||||
v0.Type = t
|
||||
v0.AddArg(x)
|
||||
v0.Aux = c
|
||||
v0.SetType()
|
||||
v.AddArg(v0)
|
||||
return true
|
||||
}
|
||||
|
@ -29,14 +29,9 @@ const (
|
||||
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
|
||||
OpConst
|
||||
|
||||
OpArg // address of a function parameter/result
|
||||
OpArg // address of a function parameter/result. Memory input is an arg called ".mem".
|
||||
OpGlobal // address of a global variable
|
||||
OpFunc // entry address of a function
|
||||
OpCopy // output = input
|
||||
@ -56,7 +51,7 @@ const (
|
||||
OpIndexAddr
|
||||
|
||||
OpLoad // args are ptr, memory
|
||||
OpStore // args are ptr, memory, returns memory
|
||||
OpStore // args are ptr, value, memory, returns memory
|
||||
|
||||
OpCheckNil // arg[0] != nil
|
||||
OpCheckBound // 0 <= arg[0] < arg[1]
|
||||
@ -135,9 +130,6 @@ type OpInfo struct {
|
||||
// %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)
|
||||
@ -178,28 +170,6 @@ const (
|
||||
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}}
|
||||
@ -221,16 +191,12 @@ 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},
|
||||
OpAdd: {flags: OpFlagCommutative},
|
||||
OpSub: {},
|
||||
OpMul: {flags: OpFlagCommutative},
|
||||
OpLess: {},
|
||||
|
||||
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: {},
|
||||
OpConst: {}, // aux matches the type (e.g. bool, int64 float64)
|
||||
OpArg: {}, // aux is the name of the input variable TODO:?
|
||||
OpGlobal: {}, // address of a global variable
|
||||
OpFunc: {},
|
||||
@ -281,12 +247,12 @@ var genericTable = [...]OpInfo{
|
||||
|
||||
// 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},
|
||||
OpADDQ: {flags: OpFlagCommutative, asm: "ADDQ\t%I0,%I1,%O0", reg: gp21}, // TODO: overwrite
|
||||
OpADDCQ: {asm: "ADDQ\t$%A,%I0,%O0", reg: gp11_overwrite}, // aux = int64 constant to add
|
||||
OpSUBQ: {asm: "SUBQ\t%I0,%I1,%O0", reg: gp21},
|
||||
OpSUBCQ: {asm: "SUBQ\t$%A,%I0,%O0", reg: gp11_overwrite},
|
||||
|
||||
OpCMPQ: {asm: "CMPQ\t%I0,%I1", reg: gp2_flags, typer: flagsTyper}, // compute arg[0]-arg[1] and produce flags
|
||||
OpCMPQ: {asm: "CMPQ\t%I0,%I1", reg: gp2_flags}, // 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
|
||||
@ -302,7 +268,7 @@ var amd64Table = [...]OpInfo{
|
||||
OpCopy: {asm: "MOVQ\t%I0,%O0", reg: gp11},
|
||||
|
||||
// convert from flags back to boolean
|
||||
OpSETL: {typer: boolTyper},
|
||||
OpSETL: {},
|
||||
|
||||
// ops for load/store to stack
|
||||
OpLoadFP8: {asm: "MOVQ\t%A(FP),%O0"},
|
||||
|
@ -4,9 +4,9 @@ package ssa
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _Op_name = "OpUnknownOpNopOpThunkOpAddOpSubOpMulOpLessOpConstNilOpConstBoolOpConstStringOpConstIntOpConstFloatOpConstComplexOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceOpIndexOpIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpLoadFPOpLoadSPOpStoreFPOpStoreSPOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpNEGQOpCMPQOpCMPCQOpADDLOpSETLOpSETGEOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLoadFP8OpLoadSP8OpStoreFP8OpStoreSP8OpMax"
|
||||
const _Op_name = "OpUnknownOpNopOpThunkOpAddOpSubOpMulOpLessOpConstOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceOpIndexOpIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpLoadFPOpLoadSPOpStoreFPOpStoreSPOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpNEGQOpCMPQOpCMPCQOpADDLOpSETLOpSETGEOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpLoadFP8OpLoadSP8OpStoreFP8OpStoreSP8OpMax"
|
||||
|
||||
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, 441, 448, 461, 467, 474, 481, 488, 497, 506, 516, 526, 531}
|
||||
var _Op_index = [...]uint16{0, 9, 14, 21, 26, 31, 36, 42, 49, 54, 62, 68, 74, 79, 90, 100, 110, 120, 132, 143, 154, 161, 168, 179, 185, 192, 202, 214, 220, 232, 241, 250, 258, 266, 274, 282, 291, 300, 311, 321, 327, 333, 340, 347, 353, 359, 366, 372, 378, 385, 398, 404, 411, 418, 425, 434, 443, 453, 463, 468}
|
||||
|
||||
func (i Op) String() string {
|
||||
if i < 0 || i+1 >= Op(len(_Op_index)) {
|
||||
|
@ -28,43 +28,36 @@ func applyRewrite(f *Func, r func(*Value) bool) {
|
||||
// Common functions called from rewriting rules
|
||||
|
||||
func is64BitInt(t Type) bool {
|
||||
return typeIdentical(t, TypeInt64) ||
|
||||
typeIdentical(t, TypeUint64) ||
|
||||
(typeIdentical(t, TypeInt) && intSize == 8) ||
|
||||
(typeIdentical(t, TypeUint) && intSize == 8) ||
|
||||
(typeIdentical(t, TypeUintptr) && ptrSize == 8)
|
||||
if b, ok := t.Underlying().(*types.Basic); ok {
|
||||
switch b.Kind() {
|
||||
case types.Int64, types.Uint64:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func is32BitInt(t Type) bool {
|
||||
return typeIdentical(t, TypeInt32) ||
|
||||
typeIdentical(t, TypeUint32) ||
|
||||
(typeIdentical(t, TypeInt) && intSize == 4) ||
|
||||
(typeIdentical(t, TypeUint) && intSize == 4) ||
|
||||
(typeIdentical(t, TypeUintptr) && ptrSize == 4)
|
||||
if b, ok := t.Underlying().(*types.Basic); ok {
|
||||
switch b.Kind() {
|
||||
case types.Int32, types.Uint32:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isSigned(t Type) bool {
|
||||
return typeIdentical(t, TypeInt) ||
|
||||
typeIdentical(t, TypeInt8) ||
|
||||
typeIdentical(t, TypeInt16) ||
|
||||
typeIdentical(t, TypeInt32) ||
|
||||
typeIdentical(t, TypeInt64)
|
||||
if b, ok := t.Underlying().(*types.Basic); ok {
|
||||
switch b.Kind() {
|
||||
case types.Int8, types.Int16, types.Int32, types.Int64:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func typeSize(t Type) int {
|
||||
switch t {
|
||||
case TypeInt32, TypeUint32:
|
||||
return 4
|
||||
case TypeInt64, TypeUint64:
|
||||
return 8
|
||||
case TypeUintptr:
|
||||
return ptrSize
|
||||
case TypeInt, TypeUint:
|
||||
return intSize
|
||||
default:
|
||||
if _, ok := t.(*types.Pointer); ok {
|
||||
return ptrSize
|
||||
}
|
||||
panic("TODO: width of " + t.String())
|
||||
}
|
||||
var sizer types.Sizes = &types.StdSizes{int64(ptrSize), int64(ptrSize)} // TODO(khr): from config
|
||||
func typeSize(t Type) int64 {
|
||||
return sizer.Sizeof(t)
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// constant folding
|
||||
(Add <t> (ConstInt [c]) (ConstInt [d])) && is64BitInt(t) -> (ConstInt [{c.(int64)+d.(int64)}])
|
||||
(Add <t> (Const [c]) (Const [d])) && is64BitInt(t) && isSigned(t) -> (Const [{c.(int64)+d.(int64)}])
|
||||
(Add <t> (Const [c]) (Const [d])) && is64BitInt(t) && !isSigned(t) -> (Const [{c.(uint64)+d.(uint64)}])
|
||||
|
||||
// load/store to stack
|
||||
(Load (FPAddr [offset]) mem) -> (LoadFP [offset] mem)
|
||||
|
@ -8,7 +8,8 @@
|
||||
// on the matching side
|
||||
// - the types and aux fields must match if they are specified.
|
||||
// on the generated side
|
||||
// - types will be computed by opcode typers if not specified explicitly.
|
||||
// - the type of the top-level expression is the same as the one on the left-hand side.
|
||||
// - the type of any subexpressions must be specified explicitly.
|
||||
// - aux will be nil if not specified.
|
||||
|
||||
// x86 register conventions:
|
||||
@ -24,7 +25,7 @@
|
||||
|
||||
(Sub <t> x y) && is64BitInt(t) -> (SUBQ x y)
|
||||
|
||||
(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ x y))
|
||||
(Less x y) && is64BitInt(v.Args[0].Type) && isSigned(v.Args[0].Type) -> (SETL (CMPQ <TypeFlags> x y))
|
||||
|
||||
// stack loads/stores
|
||||
(LoadFP <t> [offset] mem) && typeSize(t) == 8 -> (LoadFP8 <t> [offset] mem)
|
||||
@ -35,12 +36,12 @@
|
||||
// Rules below here apply some simple optimizations after lowering.
|
||||
// TODO: Should this be a separate pass?
|
||||
|
||||
(ADDQ x (ConstInt [c])) -> (ADDCQ [c] x) // TODO: restrict c to int32 range?
|
||||
(ADDQ (ConstInt [c]) x) -> (ADDCQ [c] x)
|
||||
(SUBQ x (ConstInt [c])) -> (SUBCQ x [c])
|
||||
(SUBQ (ConstInt [c]) x) -> (NEGQ (SUBCQ x [c]))
|
||||
(CMPQ x (ConstInt [c])) -> (CMPCQ x [c])
|
||||
(CMPQ (ConstInt [c]) x) -> (InvertFlags (CMPCQ x [c]))
|
||||
(ADDQ x (Const [c])) -> (ADDCQ [c] x) // TODO: restrict c to int32 range?
|
||||
(ADDQ (Const [c]) x) -> (ADDCQ [c] x)
|
||||
(SUBQ x (Const [c])) -> (SUBCQ x [c])
|
||||
(SUBQ <t> (Const [c]) x) -> (NEGQ (SUBCQ <t> x [c]))
|
||||
(CMPQ x (Const [c])) -> (CMPCQ x [c])
|
||||
(CMPQ (Const [c]) x) -> (InvertFlags (CMPCQ <TypeFlags> x [c]))
|
||||
|
||||
// reverse ordering of compare instruction
|
||||
(SETL (InvertFlags x)) -> (SETGE x)
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
@ -148,22 +149,14 @@ func main() {
|
||||
}
|
||||
|
||||
// Write to a file if given, otherwise stdout.
|
||||
var out io.WriteCloser
|
||||
if len(os.Args) >= 4 {
|
||||
outfile := os.Args[3]
|
||||
out, err = os.Create(outfile)
|
||||
if err != nil {
|
||||
log.Fatalf("can't open output file %s: %v\n", outfile, err)
|
||||
}
|
||||
err = ioutil.WriteFile(os.Args[3], b, 0666)
|
||||
} else {
|
||||
out = os.Stdout
|
||||
_, err = os.Stdout.Write(b)
|
||||
}
|
||||
if _, err = out.Write(b); err != nil {
|
||||
if err != nil {
|
||||
log.Fatalf("can't write output: %v\n", err)
|
||||
}
|
||||
if err = out.Close(); err != nil {
|
||||
log.Fatalf("can't close output: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func genMatch(w io.Writer, match, fail string) {
|
||||
@ -251,17 +244,17 @@ func genResult0(w io.Writer, result string, alloc *int, top bool) string {
|
||||
|
||||
s := split(result[1 : len(result)-1])
|
||||
var v string
|
||||
var needsType bool
|
||||
var hasType bool
|
||||
if top {
|
||||
v = "v"
|
||||
fmt.Fprintf(w, "v.Op = Op%s\n", s[0])
|
||||
fmt.Fprintf(w, "v.Aux = nil\n")
|
||||
fmt.Fprintf(w, "v.Args = v.argstorage[:0]\n")
|
||||
hasType = true
|
||||
} else {
|
||||
v = fmt.Sprintf("v%d", *alloc)
|
||||
*alloc++
|
||||
fmt.Fprintf(w, "%s := v.Block.NewValue(Op%s, TypeInvalid, nil)\n", v, s[0])
|
||||
needsType = true
|
||||
}
|
||||
for _, a := range s[1:] {
|
||||
if a[0] == '<' {
|
||||
@ -271,7 +264,7 @@ func genResult0(w io.Writer, result string, alloc *int, top bool) string {
|
||||
t = t[1 : len(t)-1]
|
||||
}
|
||||
fmt.Fprintf(w, "%s.Type = %s\n", v, t)
|
||||
needsType = false
|
||||
hasType = true
|
||||
} else if a[0] == '[' {
|
||||
// aux restriction
|
||||
x := a[1 : len(a)-1]
|
||||
@ -287,8 +280,8 @@ func genResult0(w io.Writer, result string, alloc *int, top bool) string {
|
||||
fmt.Fprintf(w, "%s.AddArg(%s)\n", v, x)
|
||||
}
|
||||
}
|
||||
if needsType {
|
||||
fmt.Fprintf(w, "%s.SetType()\n", v)
|
||||
if !hasType {
|
||||
log.Fatalf("sub-expression %s must have a type", result)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
@ -411,7 +411,8 @@ func parseSexprType(e sexpr) ssa.Type {
|
||||
if !e.compound {
|
||||
switch e.name {
|
||||
case "int":
|
||||
return ssa.TypeInt
|
||||
// TODO: pick correct width
|
||||
return ssa.TypeInt64
|
||||
default:
|
||||
fmt.Println(e.name)
|
||||
panic("unknown type")
|
||||
|
@ -13,8 +13,8 @@ type Type types.Type
|
||||
|
||||
var (
|
||||
// shortcuts for commonly used basic types
|
||||
TypeInt = types.Typ[types.Int]
|
||||
TypeUint = types.Typ[types.Uint]
|
||||
//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]
|
||||
@ -23,7 +23,7 @@ var (
|
||||
TypeUint16 = types.Typ[types.Uint16]
|
||||
TypeUint32 = types.Typ[types.Uint32]
|
||||
TypeUint64 = types.Typ[types.Uint64]
|
||||
TypeUintptr = types.Typ[types.Uintptr]
|
||||
//TypeUintptr = types.Typ[types.Uintptr]
|
||||
TypeBool = types.Typ[types.Bool]
|
||||
TypeString = types.Typ[types.String]
|
||||
|
||||
@ -32,6 +32,14 @@ var (
|
||||
// Additional compiler-only types go here.
|
||||
TypeMem = &Memory{}
|
||||
TypeFlags = &Flags{}
|
||||
|
||||
// TODO(khr): we probably shouldn't use int/uint/uintptr as Value types in the compiler.
|
||||
// In OpConst's case, their width is the compiler's width, not the to-be-compiled
|
||||
// program's width. For now, we can translate int/uint/uintptr to their specific
|
||||
// widths variants before SSA.
|
||||
// However, we may need at some point to maintain all possible user types in the
|
||||
// compiler to handle things like interface conversion. At that point, we may
|
||||
// need to revisit this decision.
|
||||
)
|
||||
|
||||
// typeIdentical reports whether its two arguments are the same type.
|
||||
|
117
src/cmd/internal/ssa/types/sizes.go
Normal file
117
src/cmd/internal/ssa/types/sizes.go
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright 2013 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 file implements Sizes.
|
||||
|
||||
package types
|
||||
|
||||
import "log"
|
||||
|
||||
// Sizes defines the sizing functions for package unsafe.
|
||||
type Sizes interface {
|
||||
// Alignof returns the alignment of a variable of type T.
|
||||
// Alignof must implement the alignment guarantees required by the spec.
|
||||
Alignof(T Type) int64
|
||||
|
||||
// Offsetsof returns the offsets of the given struct fields, in bytes.
|
||||
// Offsetsof must implement the offset guarantees required by the spec.
|
||||
Offsetsof(fields []*Var) []int64
|
||||
|
||||
// Sizeof returns the size of a variable of type T.
|
||||
// Sizeof must implement the size guarantees required by the spec.
|
||||
Sizeof(T Type) int64
|
||||
}
|
||||
|
||||
// StdSizes is a convenience type for creating commonly used Sizes.
|
||||
// It makes the following simplifying assumptions:
|
||||
//
|
||||
// - The size of explicitly sized basic types (int16, etc.) is the
|
||||
// specified size.
|
||||
// - The size of strings and interfaces is 2*WordSize.
|
||||
// - The size of slices is 3*WordSize.
|
||||
// - The size of an array of n elements corresponds to the size of
|
||||
// a struct of n consecutive fields of the array's element type.
|
||||
// - The size of a struct is the offset of the last field plus that
|
||||
// field's size. As with all element types, if the struct is used
|
||||
// in an array its size must first be aligned to a multiple of the
|
||||
// struct's alignment.
|
||||
// - All other types have size WordSize.
|
||||
// - Arrays and structs are aligned per spec definition; all other
|
||||
// types are naturally aligned with a maximum alignment MaxAlign.
|
||||
//
|
||||
// *StdSizes implements Sizes.
|
||||
//
|
||||
type StdSizes struct {
|
||||
WordSize int64 // word size in bytes - must be >= 4 (32bits)
|
||||
MaxAlign int64 // maximum alignment in bytes - must be >= 1
|
||||
}
|
||||
|
||||
func (s *StdSizes) Alignof(T Type) int64 {
|
||||
a := s.Sizeof(T) // may be 0
|
||||
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||
if a < 1 {
|
||||
return 1
|
||||
}
|
||||
if a > s.MaxAlign {
|
||||
return s.MaxAlign
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
|
||||
offsets := make([]int64, len(fields))
|
||||
var o int64
|
||||
for i, f := range fields {
|
||||
a := s.Alignof(f.typ)
|
||||
o = align(o, a)
|
||||
offsets[i] = o
|
||||
o += s.Sizeof(f.typ)
|
||||
}
|
||||
return offsets
|
||||
}
|
||||
|
||||
var basicSizes = [...]byte{
|
||||
Bool: 1,
|
||||
Int8: 1,
|
||||
Int16: 2,
|
||||
Int32: 4,
|
||||
Int64: 8,
|
||||
Uint8: 1,
|
||||
Uint16: 2,
|
||||
Uint32: 4,
|
||||
Uint64: 8,
|
||||
Float32: 4,
|
||||
Float64: 8,
|
||||
Complex64: 8,
|
||||
Complex128: 16,
|
||||
}
|
||||
|
||||
func (s *StdSizes) Sizeof(T Type) int64 {
|
||||
switch t := T.Underlying().(type) {
|
||||
case *Basic:
|
||||
k := t.kind
|
||||
if int(k) < len(basicSizes) {
|
||||
if s := basicSizes[k]; s > 0 {
|
||||
return int64(s)
|
||||
}
|
||||
}
|
||||
if k == String {
|
||||
return s.WordSize * 2
|
||||
}
|
||||
case *Slice:
|
||||
return s.WordSize * 3
|
||||
default:
|
||||
log.Fatalf("not implemented")
|
||||
}
|
||||
return s.WordSize // catch-all
|
||||
}
|
||||
|
||||
// stdSizes is used if Config.Sizes == nil.
|
||||
var stdSizes = StdSizes{8, 8}
|
||||
|
||||
// align returns the smallest y >= x such that y % a == 0.
|
||||
func align(x, a int64) int64 {
|
||||
y := x + a - 1
|
||||
return y - y%a
|
||||
}
|
@ -40,8 +40,8 @@ type Value struct {
|
||||
// Examples:
|
||||
// Opcode aux args
|
||||
// OpAdd nil 2
|
||||
// OpConstStr string 0
|
||||
// OpConstInt int64 0
|
||||
// OpConst string 0 string constant
|
||||
// OpConst int64 0 int64 constant
|
||||
// OpAddcq int64 1 amd64 op: v = arg[0] + constant
|
||||
|
||||
// short form print. Just v#.
|
||||
@ -113,12 +113,3 @@ func (v *Value) CopyFrom(w *Value) {
|
||||
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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user