[dev.ssa] cmd/internal/ssa: reorganize opcode tables

Separate out opcode tables into separate ranges for each architecture.
Put architecture-specific opcodes into separate files.

Comment each opcode in a consistent format.

Change-Id: Iddf03c062bc8a88ad2bcebbf6528088c01a75779
Reviewed-on: https://go-review.googlesource.com/10033
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Keith Randall 2015-05-13 14:11:39 -07:00
parent 12f980bc85
commit b3137966db
7 changed files with 281 additions and 255 deletions

View File

@ -292,7 +292,7 @@ func (s *ssaState) expr(n *Node) *ssa.Value {
case OIND:
p := s.expr(n.Left)
c := s.curBlock.NewValue1(ssa.OpCheckNil, ssa.TypeBool, nil, p)
c := s.curBlock.NewValue1(ssa.OpIsNonNil, ssa.TypeBool, nil, p)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.Control = c
@ -322,7 +322,7 @@ func (s *ssaState) expr(n *Node) *ssa.Value {
// bounds check
len := s.curBlock.NewValue1(ssa.OpSliceLen, s.config.UIntPtr, nil, a)
cmp := s.curBlock.NewValue2(ssa.OpCheckBound, ssa.TypeBool, nil, i, len)
cmp := s.curBlock.NewValue2(ssa.OpIsInBounds, ssa.TypeBool, nil, i, len)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.Control = cmp
@ -345,7 +345,7 @@ func (s *ssaState) expr(n *Node) *ssa.Value {
log.Fatalf("can't handle CALLFUNC with non-ONAME fn %s", opnames[n.Left.Op])
}
bNext := s.f.NewBlock(ssa.BlockPlain)
call := s.curBlock.NewValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym.Name, s.mem())
call := s.curBlock.NewValue1(ssa.OpStaticCall, ssa.TypeMem, n.Left.Sym, s.mem())
b := s.endBlock()
b.Kind = ssa.BlockCall
b.Control = call

View File

@ -209,8 +209,8 @@ func lowerAmd64(v *Value) bool {
goto enda4e64c7eaeda16c1c0db9dac409cd126
enda4e64c7eaeda16c1c0db9dac409cd126:
;
case OpCheckBound:
// match: (CheckBound idx len)
case OpIsInBounds:
// match: (IsInBounds idx len)
// cond:
// result: (SETB (CMPQ <TypeFlags> idx len))
{
@ -226,11 +226,11 @@ func lowerAmd64(v *Value) bool {
v.AddArg(v0)
return true
}
goto end249426f6f996d45a62f89a591311a954
end249426f6f996d45a62f89a591311a954:
goto endb51d371171154c0f1613b687757e0576
endb51d371171154c0f1613b687757e0576:
;
case OpCheckNil:
// match: (CheckNil p)
case OpIsNonNil:
// match: (IsNonNil p)
// cond:
// result: (SETNE (TESTQ <TypeFlags> p p))
{
@ -245,8 +245,8 @@ func lowerAmd64(v *Value) bool {
v.AddArg(v0)
return true
}
goto end90d3057824f74ef953074e473aa0b282
end90d3057824f74ef953074e473aa0b282:
goto endff508c3726edfb573abc6128c177e76c
endff508c3726edfb573abc6128c177e76c:
;
case OpLess:
// match: (Less x y)
@ -378,17 +378,17 @@ func lowerAmd64(v *Value) bool {
;
// match: (MOVQload [off1] (LEAQ8 [off2] ptr idx) mem)
// cond:
// result: (MOVQload8 [off1.(int64)+off2.(int64)] ptr idx mem)
// result: (MOVQloadidx8 [off1.(int64)+off2.(int64)] ptr idx mem)
{
off1 := v.Aux
if v.Args[0].Op != OpLEAQ8 {
goto end35060118a284c93323ab3fb827156638
goto endba0e5cee85021614041016b1a2709ab8
}
off2 := v.Args[0].Aux
ptr := v.Args[0].Args[0]
idx := v.Args[0].Args[1]
mem := v.Args[1]
v.Op = OpMOVQload8
v.Op = OpMOVQloadidx8
v.Aux = nil
v.resetArgs()
v.Aux = off1.(int64) + off2.(int64)
@ -397,8 +397,8 @@ func lowerAmd64(v *Value) bool {
v.AddArg(mem)
return true
}
goto end35060118a284c93323ab3fb827156638
end35060118a284c93323ab3fb827156638:
goto endba0e5cee85021614041016b1a2709ab8
endba0e5cee85021614041016b1a2709ab8:
;
case OpMOVQstore:
// match: (MOVQstore [off1] (FPAddr [off2]) val mem)
@ -493,18 +493,18 @@ func lowerAmd64(v *Value) bool {
;
// match: (MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem)
// cond:
// result: (MOVQstore8 [off1.(int64)+off2.(int64)] ptr idx val mem)
// result: (MOVQstoreidx8 [off1.(int64)+off2.(int64)] ptr idx val mem)
{
off1 := v.Aux
if v.Args[0].Op != OpLEAQ8 {
goto endb5cba0ee3ba21d2bd8e5aa163d2b984e
goto end4ad469f534c7369f6ac36bdace3462ad
}
off2 := v.Args[0].Aux
ptr := v.Args[0].Args[0]
idx := v.Args[0].Args[1]
val := v.Args[1]
mem := v.Args[2]
v.Op = OpMOVQstore8
v.Op = OpMOVQstoreidx8
v.Aux = nil
v.resetArgs()
v.Aux = off1.(int64) + off2.(int64)
@ -514,8 +514,8 @@ func lowerAmd64(v *Value) bool {
v.AddArg(mem)
return true
}
goto endb5cba0ee3ba21d2bd8e5aa163d2b984e
endb5cba0ee3ba21d2bd8e5aa163d2b984e:
goto end4ad469f534c7369f6ac36bdace3462ad
end4ad469f534c7369f6ac36bdace3462ad:
;
case OpMULCQ:
// match: (MULCQ [c] x)

View File

@ -8,25 +8,33 @@ package ssa
// 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.
// Architecture-independent opcodes appear in this file.
// Architecture-dependent opcodes appear in op{arch}.go files.
type Op int32
// All the opcodes
// Opcode ranges, a generic one and one for each architecture.
const (
OpUnknown Op = iota
opInvalid Op = 0
opGenericBase Op = 1 + 1000*iota
opAMD64Base
op386Base
// machine-independent opcodes
opMax // sentinel
)
OpNop // should never be used, appears only briefly during construction, Has type Void.
OpFwdRef // used during ssa construction. Like OpCopy, but the arg has not been specified yet.
// Generic opcodes
const (
opGenericStart Op = opGenericBase + iota
// 2-input arithmetic
OpAdd
OpSub
OpMul
OpAdd // arg0 + arg1
OpSub // arg0 - arg1
OpMul // arg0 * arg1
// 2-input comparisons
OpLess
OpLess // arg0 < arg1
// constants. Constant values are stored in the aux field.
// booleans have a bool aux field, strings have a string aux
@ -36,44 +44,40 @@ const (
// as it may be different widths on the host and target.
OpConst
OpArg // address of a function parameter/result. Memory input is an arg called ".mem".
OpGlobal // address of a global variable (aux is a *gc.Sym)
OpArg // address of a function parameter/result. Memory input is an arg called ".mem". aux is a string (TODO: make it something other than a string?)
OpGlobal // the address of a global variable aux.(*gc.Sym)
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
OpCopy // output = arg0
OpPhi // select an argument based on which predecessor block we came from
OpStringMake // args are ptr/len
OpStringPtr
OpStringLen
OpSliceMake // arg0=ptr, arg1=len, arg2=cap
OpSlicePtr // ptr(arg0)
OpSliceLen // len(arg0)
OpSliceCap // cap(arg0)
OpSliceIndex
OpSliceIndexAddr
OpStringMake // arg0=ptr, arg1=len
OpStringPtr // ptr(arg0)
OpStringLen // len(arg0)
OpLoad // args are ptr, memory. Loads from ptr+aux.(int64)
OpStore // args are ptr, value, memory, returns memory. Stores to ptr+aux.(int64)
OpCheckNil // arg[0] != nil
OpCheckBound // 0 <= arg[0] < arg[1]
OpLoad // Load from arg0+aux.(int64). arg1=memory
OpStore // Store arg1 to arg0+aux.(int64). arg2=memory. Returns memory.
OpSliceIndex // arg0=slice, arg1=index, arg2=memory
OpIsNonNil // arg0 != nil
OpIsInBounds // 0 <= arg0 < arg1
// function calls. Arguments to the call have already been written to the stack.
// Return values appear on the stack. The method receiver, if any, is treated
// as a phantom first argument.
// TODO: closure pointer must be in a register.
OpCall // args are function ptr, memory
OpStaticCall // aux is function, arg is memory
OpCall // arg0=code pointer, arg1=context ptr, arg2=memory. Returns memory.
OpStaticCall // call function aux.(*gc.Sym), arg0=memory. Returns memory.
OpConvert
OpConvNop
OpConvert // convert arg0 to another type
OpConvNop // interpret arg0 as another type
// 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
// These ops return a pointer to a location on the stack.
OpFPAddr // FP + aux.(int64) (+ == args from caller, - == locals)
OpSPAddr // SP + aux.(int64)
// spill&restore ops for the register allocator. These are
// semantically identical to OpCopy; they do not take/return
@ -82,70 +86,19 @@ const (
OpStoreReg8
OpLoadReg8
// machine-dependent opcodes go here
// amd64
OpADDQ
OpSUBQ
OpADDCQ // 1 input arg. output = input + aux.(int64)
OpSUBCQ // 1 input arg. output = input - aux.(int64)
OpMULQ
OpMULCQ // output = input * aux.(int64)
OpSHLQ // output = input0 << input1
OpSHLCQ // output = input << aux.(int64)
OpNEGQ
OpCMPQ
OpCMPCQ // 1 input arg. Compares input with aux.(int64)
OpADDL
OpTESTQ // compute flags of arg[0] & arg[1]
OpSETEQ
OpSETNE
// generate boolean based on the flags setting
OpSETL // less than
OpSETGE // >=
OpSETB // "below" = unsigned less than
// InvertFlags reverses direction of flags register interpretation:
// (InvertFlags (OpCMPQ a b)) == (OpCMPQ b a)
// This is a pseudo-op which can't appear in assembly output.
OpInvertFlags
OpLEAQ // x+y
OpLEAQ2 // x+2*y
OpLEAQ4 // x+4*y
OpLEAQ8 // x+8*y
OpMOVQload // (ptr, mem): loads from ptr+aux.(int64)
OpMOVQstore // (ptr, val, mem): stores val to ptr+aux.(int64), returns mem
OpMOVQload8 // (ptr,idx,mem): loads from ptr+idx*8+aux.(int64)
OpMOVQstore8 // (ptr,idx,val,mem): stores to ptr+idx*8+aux.(int64), returns mem
// load/store from global. aux = GlobalOffset
OpMOVQloadglobal // (mem) -> value
OpMOVQstoreglobal // (val, mem) -> mem
// load/store 8-byte integer register from stack slot.
OpMOVQloadFP
OpMOVQloadSP
OpMOVQstoreFP
OpMOVQstoreSP
// materialize a constant into a register
OpMOVQconst
OpMax // sentinel
// used during ssa construction. Like OpCopy, but the arg has not been specified yet.
OpFwdRef
)
// GlobalOffset represents a fixed offset within a global variable
type GlobalOffset struct {
Global interface{} // holds a *cmd/internal/gc.Sym
Global interface{} // holds a *gc.Sym
Offset int64
}
//go:generate stringer -type=Op
type OpInfo struct {
type opInfo struct {
flags int32
// assembly template
@ -160,67 +113,13 @@ type OpInfo struct {
reg [2][]regMask
}
type regMask uint64
var regs386 = [...]string{
"AX",
"CX",
"DX",
"BX",
"SP",
"BP",
"SI",
"DI",
// pseudo registers
"FLAGS",
"OVERWRITE0", // the same register as the first input
}
// TODO: match up these with regs386 above
var gp regMask = 0xef
var cx regMask = 0x2
var flags regMask = 1 << 8
var overwrite0 regMask = 1 << 9
const (
// possible properties of opcodes
OpFlagCommutative int32 = 1 << iota
// architecture constants
Arch386
ArchAMD64
ArchARM
)
// general purpose registers, 2 input, 1 output
var gp21 = [2][]regMask{{gp, gp}, {gp}}
var gp21_overwrite = [2][]regMask{{gp, gp}, {gp}}
// general purpose registers, 1 input, 1 output
var gp11 = [2][]regMask{{gp}, {gp}}
var gp11_overwrite = [2][]regMask{{gp}, {gp}}
// general purpose registers, 0 input, 1 output
var gp01 = [2][]regMask{{}, {gp}}
// shift operations
var shift = [2][]regMask{{gp, cx}, {gp}}
var gp2_flags = [2][]regMask{{gp, gp}, {flags}}
var gp1_flags = [2][]regMask{{gp}, {flags}}
var gpload = [2][]regMask{{gp, 0}, {gp}}
var gploadX = [2][]regMask{{gp, gp, 0}, {gp}} // indexed loads
var gpstore = [2][]regMask{{gp, gp, 0}, {0}}
var gpstoreX = [2][]regMask{{gp, gp, gp, 0}, {0}} // indexed stores
var gploadglobal = [2][]regMask{{0}, {gp}}
var gpstoreglobal = [2][]regMask{{gp, 0}, {0}}
var gpload_stack = [2][]regMask{{0}, {gp}}
var gpstore_stack = [2][]regMask{{gp, 0}, {0}}
// Opcodes that represent the input Go program
var genericTable = [...]OpInfo{
var genericTable = map[Op]opInfo{
// the unknown op is used only during building and should not appear in a
// fully formed ssa representation.
@ -278,87 +177,11 @@ 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}, // 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},
OpMULQ: {asm: "MULQ\t%I0,%I1,%O0", reg: gp21},
OpMULCQ: {asm: "MULQ\t$%A,%I0,%O0", reg: gp11_overwrite},
OpSHLQ: {asm: "SHLQ\t%I0,%I1,%O0", reg: gp21},
OpSHLCQ: {asm: "SHLQ\t$%A,%I0,%O0", reg: gp11_overwrite},
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},
OpTESTQ: {asm: "TESTQ\t%I0,%I1", reg: gp2_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"},
// loads and stores
OpMOVQload: {asm: "MOVQ\t%A(%I0),%O0", reg: gpload},
OpMOVQstore: {asm: "MOVQ\t%I1,%A(%I0)", reg: gpstore},
OpMOVQload8: {asm: "MOVQ\t%A(%I0)(%I1*8),%O0", reg: gploadX},
OpMOVQstore8: {asm: "MOVQ\t%I2,%A(%I0)(%I1*8)", reg: gpstoreX},
OpMOVQloadglobal: {reg: gploadglobal},
OpMOVQstoreglobal: {reg: gpstoreglobal},
OpMOVQconst: {asm: "MOVQ\t$%A,%O0", reg: gp01},
OpStaticCall: {asm: "CALL\t%A(SB)"},
OpCopy: {asm: "MOVQ\t%I0,%O0", reg: gp11},
// convert from flags back to boolean
OpSETL: {},
// ops for load/store to stack
OpMOVQloadFP: {asm: "MOVQ\t%A(FP),%O0", reg: gpload_stack}, // mem -> value
OpMOVQloadSP: {asm: "MOVQ\t%A(SP),%O0", reg: gpload_stack}, // mem -> value
OpMOVQstoreFP: {asm: "MOVQ\t%I0,%A(FP)", reg: gpstore_stack}, // mem, value -> mem
OpMOVQstoreSP: {asm: "MOVQ\t%I0,%A(SP)", reg: gpstore_stack}, // mem, value -> mem
// 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"},
OpStoreReg8: {asm: "MOVQ\t%I0,%O0"},
}
// 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
var opcodeTable [opMax]opInfo
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)
for op, info := range genericTable {
opcodeTable[op] = info
}
}

View File

@ -4,13 +4,37 @@ package ssa
import "fmt"
const _Op_name = "OpUnknownOpNopOpFwdRefOpAddOpSubOpMulOpLessOpConstOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpSliceIndexOpSliceIndexAddrOpLoadOpStoreOpCheckNilOpCheckBoundOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpStoreReg8OpLoadReg8OpADDQOpSUBQOpADDCQOpSUBCQOpMULQOpMULCQOpSHLQOpSHLCQOpNEGQOpCMPQOpCMPCQOpADDLOpTESTQOpSETEQOpSETNEOpSETLOpSETGEOpSETBOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpMOVQloadOpMOVQstoreOpMOVQload8OpMOVQstore8OpMOVQloadglobalOpMOVQstoreglobalOpMOVQloadFPOpMOVQloadSPOpMOVQstoreFPOpMOVQstoreSPOpMOVQconstOpMax"
const (
_Op_name_0 = "opInvalid"
_Op_name_1 = "opGenericBaseOpAddOpSubOpMulOpLessOpConstOpArgOpGlobalOpFuncOpCopyOpPhiOpSliceMakeOpSlicePtrOpSliceLenOpSliceCapOpStringMakeOpStringPtrOpStringLenOpLoadOpStoreOpSliceIndexOpIsNonNilOpIsInBoundsOpCallOpStaticCallOpConvertOpConvNopOpFPAddrOpSPAddrOpStoreReg8OpLoadReg8OpFwdRef"
_Op_name_2 = "opAMD64BaseOpADDQOpSUBQOpADDCQOpSUBCQOpMULQOpMULCQOpSHLQOpSHLCQOpNEGQOpADDLOpCMPQOpCMPCQOpTESTQOpSETEQOpSETNEOpSETLOpSETGEOpSETBOpInvertFlagsOpLEAQOpLEAQ2OpLEAQ4OpLEAQ8OpMOVQloadOpMOVQstoreOpMOVQloadidx8OpMOVQstoreidx8OpMOVQloadglobalOpMOVQstoreglobalOpMOVQloadFPOpMOVQloadSPOpMOVQstoreFPOpMOVQstoreSPOpMOVQconst"
_Op_name_3 = "op386Base"
_Op_name_4 = "opMax"
)
var _Op_index = [...]uint16{0, 9, 14, 22, 27, 32, 37, 43, 50, 55, 63, 69, 75, 80, 91, 101, 111, 121, 133, 144, 155, 167, 183, 189, 196, 206, 218, 224, 236, 245, 254, 262, 270, 281, 291, 297, 303, 310, 317, 323, 330, 336, 343, 349, 355, 362, 368, 375, 382, 389, 395, 402, 408, 421, 427, 434, 441, 448, 458, 469, 480, 492, 508, 525, 537, 549, 562, 575, 586, 591}
var (
_Op_index_0 = [...]uint8{0, 9}
_Op_index_1 = [...]uint16{0, 13, 18, 23, 28, 34, 41, 46, 54, 60, 66, 71, 82, 92, 102, 112, 124, 135, 146, 152, 159, 171, 181, 193, 199, 211, 220, 229, 237, 245, 256, 266, 274}
_Op_index_2 = [...]uint16{0, 11, 17, 23, 30, 37, 43, 50, 56, 63, 69, 75, 81, 88, 95, 102, 109, 115, 122, 128, 141, 147, 154, 161, 168, 178, 189, 203, 218, 234, 251, 263, 275, 288, 301, 312}
_Op_index_3 = [...]uint8{0, 9}
_Op_index_4 = [...]uint8{0, 5}
)
func (i Op) String() string {
if i < 0 || i+1 >= Op(len(_Op_index)) {
switch {
case i == 0:
return _Op_name_0
case 1001 <= i && i <= 1032:
i -= 1001
return _Op_name_1[_Op_index_1[i]:_Op_index_1[i+1]]
case 2001 <= i && i <= 2035:
i -= 2001
return _Op_name_2[_Op_index_2[i]:_Op_index_2[i+1]]
case i == 3001:
return _Op_name_3
case i == 4001:
return _Op_name_4
default:
return fmt.Sprintf("Op(%d)", i)
}
return _Op_name[_Op_index[i]:_Op_index[i+1]]
}

View File

@ -0,0 +1,171 @@
// 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
// amd64-specific opcodes
const (
opAMD64start Op = opAMD64Base + iota
// Suffixes encode the bit width of various instructions.
// Q = 64 bit, L = 32 bit, W = 16 bit, B = 8 bit
// arithmetic
OpADDQ // arg0 + arg1
OpSUBQ // arg0 - arg1
OpADDCQ // arg + aux.(int64)
OpSUBCQ // arg - aux.(int64)
OpMULQ // arg0 * arg1
OpMULCQ // arg * aux.(int64)
OpSHLQ // arg0 << arg1
OpSHLCQ // arg << aux.(int64)
OpNEGQ // -arg
OpADDL // arg0 + arg1
// Flags value generation.
// We pretend the flags type is an opaque thing that comparisons generate
// and from which we can extract boolean conditions like <, ==, etc.
OpCMPQ // arg0 compare to arg1
OpCMPCQ // arg0 compare to aux.(int64)
OpTESTQ // (arg0 & arg1) compare to 0
// These opcodes extract a particular boolean condition from a flags value.
OpSETEQ // extract == condition from arg0
OpSETNE // extract != condition from arg0
OpSETL // extract signed < condition from arg0
OpSETGE // extract signed >= condition from arg0
OpSETB // extract unsigned < condition from arg0
// InvertFlags reverses the direction of a flags type interpretation:
// (InvertFlags (OpCMPQ a b)) == (OpCMPQ b a)
// This is a pseudo-op which can't appear in assembly output.
OpInvertFlags // reverse direction of arg0
OpLEAQ // arg0 + arg1 + aux.(int64)
OpLEAQ2 // arg0 + 2*arg1 + aux.(int64)
OpLEAQ4 // arg0 + 4*arg1 + aux.(int64)
OpLEAQ8 // arg0 + 8*arg1 + aux.(int64)
// Load/store from general address
OpMOVQload // Load from arg0+aux.(int64). arg1=memory
OpMOVQstore // Store arg1 to arg0+aux.(int64). arg2=memory, returns memory.
OpMOVQloadidx8 // Load from arg0+arg1*8+aux.(int64). arg2=memory
OpMOVQstoreidx8 // Store arg2 to arg0+arg1*8+aux.(int64). arg3=memory, returns memory.
// Load/store from global. aux.(GlobalOffset) encodes the global location.
OpMOVQloadglobal // arg0 = memory
OpMOVQstoreglobal // store arg0. arg1=memory, returns memory.
// Load/store from stack slot.
OpMOVQloadFP // load from FP+aux.(int64). arg0=memory
OpMOVQloadSP // load from SP+aux.(int64). arg0=memory
OpMOVQstoreFP // store arg0 to FP+aux.(int64). arg1=memory, returns memory.
OpMOVQstoreSP // store arg0 to SP+aux.(int64). arg1=memory, returns memory.
// materialize a constant into a register
OpMOVQconst // (takes no arguments)
)
type regMask uint64
var regsAMD64 = [...]string{
"AX",
"CX",
"DX",
"BX",
"SP",
"BP",
"SI",
"DI",
"R8",
"R9",
"R10",
"R11",
"R12",
"R13",
"R14",
"R15",
// pseudo registers
"FLAGS",
"OVERWRITE0", // the same register as the first input
}
var gp regMask = 0xef // all integer registers except SP
var cx regMask = 0x2
var flags regMask = 1 << 16
var (
// gp = general purpose (integer) registers
gp21 = [2][]regMask{{gp, gp}, {gp}} // 2 input, 1 output
gp11 = [2][]regMask{{gp}, {gp}} // 1 input, 1 output
gp01 = [2][]regMask{{}, {gp}} // 0 input, 1 output
shift = [2][]regMask{{gp, cx}, {gp}} // shift operations
gp2_flags = [2][]regMask{{gp, gp}, {flags}} // generate flags from 2 gp regs
gp1_flags = [2][]regMask{{gp}, {flags}} // generate flags from 1 gp reg
gpload = [2][]regMask{{gp, 0}, {gp}}
gploadidx = [2][]regMask{{gp, gp, 0}, {gp}}
gpstore = [2][]regMask{{gp, gp, 0}, {0}}
gpstoreidx = [2][]regMask{{gp, gp, gp, 0}, {0}}
gpload_stack = [2][]regMask{{0}, {gp}}
gpstore_stack = [2][]regMask{{gp, 0}, {0}}
)
// Opcodes that appear in an output amd64 program
var amd64Table = map[Op]opInfo{
OpADDQ: {flags: OpFlagCommutative, asm: "ADDQ\t%I0,%I1,%O0", reg: gp21}, // TODO: overwrite
OpADDCQ: {asm: "ADDQ\t$%A,%I0,%O0", reg: gp11}, // aux = int64 constant to add
OpSUBQ: {asm: "SUBQ\t%I0,%I1,%O0", reg: gp21},
OpSUBCQ: {asm: "SUBQ\t$%A,%I0,%O0", reg: gp11},
OpMULQ: {asm: "MULQ\t%I0,%I1,%O0", reg: gp21},
OpMULCQ: {asm: "MULQ\t$%A,%I0,%O0", reg: gp11},
OpSHLQ: {asm: "SHLQ\t%I0,%I1,%O0", reg: gp21},
OpSHLCQ: {asm: "SHLQ\t$%A,%I0,%O0", reg: gp11},
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},
OpTESTQ: {asm: "TESTQ\t%I0,%I1", reg: gp2_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"},
// loads and stores
OpMOVQload: {asm: "MOVQ\t%A(%I0),%O0", reg: gpload},
OpMOVQstore: {asm: "MOVQ\t%I1,%A(%I0)", reg: gpstore},
OpMOVQloadidx8: {asm: "MOVQ\t%A(%I0)(%I1*8),%O0", reg: gploadidx},
OpMOVQstoreidx8: {asm: "MOVQ\t%I2,%A(%I0)(%I1*8)", reg: gpstoreidx},
OpMOVQconst: {asm: "MOVQ\t$%A,%O0", reg: gp01},
OpStaticCall: {asm: "CALL\t%A(SB)"},
OpCopy: {asm: "MOVQ\t%I0,%O0", reg: gp11},
// convert from flags back to boolean
OpSETL: {},
// ops for load/store to stack
OpMOVQloadFP: {asm: "MOVQ\t%A(FP),%O0", reg: gpload_stack}, // mem -> value
OpMOVQloadSP: {asm: "MOVQ\t%A(SP),%O0", reg: gpload_stack}, // mem -> value
OpMOVQstoreFP: {asm: "MOVQ\t%I0,%A(FP)", reg: gpstore_stack}, // mem, value -> mem
OpMOVQstoreSP: {asm: "MOVQ\t%I0,%A(SP)", reg: gpstore_stack}, // mem, value -> mem
// 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"},
OpStoreReg8: {asm: "MOVQ\t%I0,%O0"},
}
func init() {
for op, info := range amd64Table {
opcodeTable[op] = info
}
}

View File

@ -28,8 +28,16 @@ var registers = [...]Register{
Register{"BP"},
Register{"SI"},
Register{"DI"},
Register{"R8"},
Register{"R9"},
Register{"R10"},
Register{"R11"},
Register{"R12"},
Register{"R13"},
Register{"R14"},
Register{"R15"},
// TODO R8, X0, ...
// TODO X0, ...
// TODO: make arch-dependent
Register{"FLAGS"},
Register{"OVERWRITE"},

View File

@ -34,8 +34,8 @@
(Store ptr val mem) && (is64BitInt(val.Type) || isPtr(val.Type)) -> (MOVQstore [int64(0)] ptr val mem)
// checks
(CheckNil p) -> (SETNE (TESTQ <TypeFlags> p p))
(CheckBound idx len) -> (SETB (CMPQ <TypeFlags> idx len))
(IsNonNil p) -> (SETNE (TESTQ <TypeFlags> p p))
(IsInBounds idx len) -> (SETB (CMPQ <TypeFlags> idx len))
// Rules below here apply some simple optimizations after lowering.
// TODO: Should this be a separate pass?
@ -80,8 +80,8 @@
(MOVQstore [off1] (ADDCQ [off2] ptr) val mem) -> (MOVQstore [off1.(int64)+off2.(int64)] ptr val mem)
// indexed loads and stores
(MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) -> (MOVQload8 [off1.(int64)+off2.(int64)] ptr idx mem)
(MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) -> (MOVQstore8 [off1.(int64)+off2.(int64)] ptr idx val mem)
(MOVQload [off1] (LEAQ8 [off2] ptr idx) mem) -> (MOVQloadidx8 [off1.(int64)+off2.(int64)] ptr idx mem)
(MOVQstore [off1] (LEAQ8 [off2] ptr idx) val mem) -> (MOVQstoreidx8 [off1.(int64)+off2.(int64)] ptr idx val mem)
// Combine the offset of a stack object with the offset within a stack object
(ADDCQ [off1] (FPAddr [off2])) -> (FPAddr [off1.(int64)+off2.(int64)])