2015-06-06 16:03:33 -07:00
// 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.
2016-04-05 15:11:08 +10:00
// +build ignore
2015-06-06 16:03:33 -07:00
package main
2016-03-29 16:39:53 -07:00
// Generic opcodes typically specify a width. The inputs and outputs
// of that op are the given number of bits wide. There is no notion of
// "sign", so Add32 can be used both for signed and unsigned 32-bit
// addition.
// Signed/unsigned is explicit with the extension ops
// (SignExt*/ZeroExt*) and implicit as the arg to some opcodes
// (e.g. the second argument to shifts is unsigned). If not mentioned,
// all args take signed inputs, or don't care whether their inputs
// are signed or unsigned.
// Unused portions of AuxInt are filled by sign-extending the used portion.
// Users of AuxInt which interpret AuxInt as unsigned (e.g. shifts) must be careful.
2015-06-06 16:03:33 -07:00
var genericOps = [ ] opData {
// 2-input arithmetic
2016-03-01 23:21:55 +00:00
// Types must be consistent with Go typing. Add, for example, must take two values
2015-06-06 16:03:33 -07:00
// of the same type and produces that same type.
2016-02-27 08:04:48 -06:00
{ name : "Add8" , argLength : 2 , commutative : true } , // arg0 + arg1
{ name : "Add16" , argLength : 2 , commutative : true } ,
{ name : "Add32" , argLength : 2 , commutative : true } ,
{ name : "Add64" , argLength : 2 , commutative : true } ,
{ name : "AddPtr" , argLength : 2 } , // For address calculations. arg0 is a pointer and arg1 is an int.
{ name : "Add32F" , argLength : 2 } ,
{ name : "Add64F" , argLength : 2 } ,
2015-07-19 15:48:20 -07:00
2016-02-27 08:04:48 -06:00
{ name : "Sub8" , argLength : 2 } , // arg0 - arg1
{ name : "Sub16" , argLength : 2 } ,
{ name : "Sub32" , argLength : 2 } ,
{ name : "Sub64" , argLength : 2 } ,
{ name : "SubPtr" , argLength : 2 } ,
{ name : "Sub32F" , argLength : 2 } ,
{ name : "Sub64F" , argLength : 2 } ,
{ name : "Mul8" , argLength : 2 , commutative : true } , // arg0 * arg1
{ name : "Mul16" , argLength : 2 , commutative : true } ,
{ name : "Mul32" , argLength : 2 , commutative : true } ,
{ name : "Mul64" , argLength : 2 , commutative : true } ,
{ name : "Mul32F" , argLength : 2 } ,
{ name : "Mul64F" , argLength : 2 } ,
{ name : "Div32F" , argLength : 2 } , // arg0 / arg1
{ name : "Div64F" , argLength : 2 } ,
2016-03-29 16:39:53 -07:00
{ name : "Hmul8" , argLength : 2 } , // (arg0 * arg1) >> width, signed
{ name : "Hmul8u" , argLength : 2 } , // (arg0 * arg1) >> width, unsigned
2016-02-27 08:04:48 -06:00
{ name : "Hmul16" , argLength : 2 } ,
{ name : "Hmul16u" , argLength : 2 } ,
{ name : "Hmul32" , argLength : 2 } ,
{ name : "Hmul32u" , argLength : 2 } ,
{ name : "Hmul64" , argLength : 2 } ,
{ name : "Hmul64u" , argLength : 2 } ,
2016-02-05 20:26:18 -08:00
2016-10-06 15:43:47 -04:00
{ name : "Mul32uhilo" , argLength : 2 , typ : "(UInt32,UInt32)" } , // arg0 * arg1, returns (hi, lo)
{ name : "Mul64uhilo" , argLength : 2 , typ : "(UInt64,UInt64)" } , // arg0 * arg1, returns (hi, lo)
2016-02-05 20:26:18 -08:00
// Weird special instruction for strength reduction of divides.
2016-02-27 08:04:48 -06:00
{ name : "Avg64u" , argLength : 2 } , // (uint64(arg0) + uint64(arg1)) / 2, correct to all 64 bits.
2016-03-29 16:39:53 -07:00
{ name : "Div8" , argLength : 2 } , // arg0 / arg1, signed
{ name : "Div8u" , argLength : 2 } , // arg0 / arg1, unsigned
2016-02-27 08:04:48 -06:00
{ name : "Div16" , argLength : 2 } ,
{ name : "Div16u" , argLength : 2 } ,
{ name : "Div32" , argLength : 2 } ,
{ name : "Div32u" , argLength : 2 } ,
{ name : "Div64" , argLength : 2 } ,
{ name : "Div64u" , argLength : 2 } ,
2016-10-06 15:43:47 -04:00
{ name : "Div128u" , argLength : 3 } , // arg0:arg1 / arg2 (128-bit divided by 64-bit), returns (q, r)
2016-02-27 08:04:48 -06:00
2016-03-29 16:39:53 -07:00
{ name : "Mod8" , argLength : 2 } , // arg0 % arg1, signed
{ name : "Mod8u" , argLength : 2 } , // arg0 % arg1, unsigned
2016-02-27 08:04:48 -06:00
{ name : "Mod16" , argLength : 2 } ,
{ name : "Mod16u" , argLength : 2 } ,
{ name : "Mod32" , argLength : 2 } ,
{ name : "Mod32u" , argLength : 2 } ,
{ name : "Mod64" , argLength : 2 } ,
{ name : "Mod64u" , argLength : 2 } ,
{ name : "And8" , argLength : 2 , commutative : true } , // arg0 & arg1
{ name : "And16" , argLength : 2 , commutative : true } ,
{ name : "And32" , argLength : 2 , commutative : true } ,
{ name : "And64" , argLength : 2 , commutative : true } ,
{ name : "Or8" , argLength : 2 , commutative : true } , // arg0 | arg1
{ name : "Or16" , argLength : 2 , commutative : true } ,
{ name : "Or32" , argLength : 2 , commutative : true } ,
{ name : "Or64" , argLength : 2 , commutative : true } ,
{ name : "Xor8" , argLength : 2 , commutative : true } , // arg0 ^ arg1
{ name : "Xor16" , argLength : 2 , commutative : true } ,
{ name : "Xor32" , argLength : 2 , commutative : true } ,
{ name : "Xor64" , argLength : 2 , commutative : true } ,
2015-07-28 16:04:50 -07:00
2015-07-29 17:07:09 -07:00
// For shifts, AxB means the shifted value has A bits and the shift amount has B bits.
2016-03-29 16:39:53 -07:00
// Shift amounts are considered unsigned.
2016-02-27 08:04:48 -06:00
{ name : "Lsh8x8" , argLength : 2 } , // arg0 << arg1
{ name : "Lsh8x16" , argLength : 2 } ,
{ name : "Lsh8x32" , argLength : 2 } ,
{ name : "Lsh8x64" , argLength : 2 } ,
{ name : "Lsh16x8" , argLength : 2 } ,
{ name : "Lsh16x16" , argLength : 2 } ,
{ name : "Lsh16x32" , argLength : 2 } ,
{ name : "Lsh16x64" , argLength : 2 } ,
{ name : "Lsh32x8" , argLength : 2 } ,
{ name : "Lsh32x16" , argLength : 2 } ,
{ name : "Lsh32x32" , argLength : 2 } ,
{ name : "Lsh32x64" , argLength : 2 } ,
{ name : "Lsh64x8" , argLength : 2 } ,
{ name : "Lsh64x16" , argLength : 2 } ,
{ name : "Lsh64x32" , argLength : 2 } ,
{ name : "Lsh64x64" , argLength : 2 } ,
{ name : "Rsh8x8" , argLength : 2 } , // arg0 >> arg1, signed
{ name : "Rsh8x16" , argLength : 2 } ,
{ name : "Rsh8x32" , argLength : 2 } ,
{ name : "Rsh8x64" , argLength : 2 } ,
{ name : "Rsh16x8" , argLength : 2 } ,
{ name : "Rsh16x16" , argLength : 2 } ,
{ name : "Rsh16x32" , argLength : 2 } ,
{ name : "Rsh16x64" , argLength : 2 } ,
{ name : "Rsh32x8" , argLength : 2 } ,
{ name : "Rsh32x16" , argLength : 2 } ,
{ name : "Rsh32x32" , argLength : 2 } ,
{ name : "Rsh32x64" , argLength : 2 } ,
{ name : "Rsh64x8" , argLength : 2 } ,
{ name : "Rsh64x16" , argLength : 2 } ,
{ name : "Rsh64x32" , argLength : 2 } ,
{ name : "Rsh64x64" , argLength : 2 } ,
{ name : "Rsh8Ux8" , argLength : 2 } , // arg0 >> arg1, unsigned
{ name : "Rsh8Ux16" , argLength : 2 } ,
{ name : "Rsh8Ux32" , argLength : 2 } ,
{ name : "Rsh8Ux64" , argLength : 2 } ,
{ name : "Rsh16Ux8" , argLength : 2 } ,
{ name : "Rsh16Ux16" , argLength : 2 } ,
{ name : "Rsh16Ux32" , argLength : 2 } ,
{ name : "Rsh16Ux64" , argLength : 2 } ,
{ name : "Rsh32Ux8" , argLength : 2 } ,
{ name : "Rsh32Ux16" , argLength : 2 } ,
{ name : "Rsh32Ux32" , argLength : 2 } ,
{ name : "Rsh32Ux64" , argLength : 2 } ,
{ name : "Rsh64Ux8" , argLength : 2 } ,
{ name : "Rsh64Ux16" , argLength : 2 } ,
{ name : "Rsh64Ux32" , argLength : 2 } ,
{ name : "Rsh64Ux64" , argLength : 2 } ,
2015-06-06 16:03:33 -07:00
2015-08-05 22:11:14 -04:00
// (Left) rotates replace pattern matches in the front end
// of (arg0 << arg1) ^ (arg0 >> (A-arg1))
// where A is the bit width of arg0 and result.
// Note that because rotates are pattern-matched from
// shifts, that a rotate of arg1=A+k (k > 0) bits originated from
// (arg0 << A+k) ^ (arg0 >> -k) =
// 0 ^ arg0>>huge_unsigned =
// 0 ^ 0 = 0
// which is not the same as a rotation by A+k
//
// However, in the specific case of k = 0, the result of
// the shift idiom is the same as the result for the
// rotate idiom, i.e., result=arg0.
// This is different from shifts, where
// arg0 << A is defined to be zero.
//
// Because of this, and also because the primary use case
// for rotates is hashing and crypto code with constant
// distance, rotate instructions are only substituted
// when arg1 is a constant between 1 and A-1, inclusive.
2016-02-27 08:04:48 -06:00
{ name : "Lrot8" , argLength : 1 , aux : "Int64" } ,
{ name : "Lrot16" , argLength : 1 , aux : "Int64" } ,
{ name : "Lrot32" , argLength : 1 , aux : "Int64" } ,
{ name : "Lrot64" , argLength : 1 , aux : "Int64" } ,
2015-08-05 22:11:14 -04:00
2015-06-06 16:03:33 -07:00
// 2-input comparisons
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
{ name : "Eq8" , argLength : 2 , commutative : true , typ : "Bool" } , // arg0 == arg1
{ name : "Eq16" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "Eq32" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "Eq64" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "EqPtr" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "EqInter" , argLength : 2 , typ : "Bool" } , // arg0 or arg1 is nil; other cases handled by frontend
{ name : "EqSlice" , argLength : 2 , typ : "Bool" } , // arg0 or arg1 is nil; other cases handled by frontend
{ name : "Eq32F" , argLength : 2 , typ : "Bool" } ,
{ name : "Eq64F" , argLength : 2 , typ : "Bool" } ,
{ name : "Neq8" , argLength : 2 , commutative : true , typ : "Bool" } , // arg0 != arg1
{ name : "Neq16" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "Neq32" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "Neq64" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "NeqPtr" , argLength : 2 , commutative : true , typ : "Bool" } ,
{ name : "NeqInter" , argLength : 2 , typ : "Bool" } , // arg0 or arg1 is nil; other cases handled by frontend
{ name : "NeqSlice" , argLength : 2 , typ : "Bool" } , // arg0 or arg1 is nil; other cases handled by frontend
{ name : "Neq32F" , argLength : 2 , typ : "Bool" } ,
2016-02-27 08:04:48 -06:00
{ name : "Neq64F" , argLength : 2 } ,
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
{ name : "Less8" , argLength : 2 , typ : "Bool" } , // arg0 < arg1, signed
{ name : "Less8U" , argLength : 2 , typ : "Bool" } , // arg0 < arg1, unsigned
{ name : "Less16" , argLength : 2 , typ : "Bool" } ,
{ name : "Less16U" , argLength : 2 , typ : "Bool" } ,
{ name : "Less32" , argLength : 2 , typ : "Bool" } ,
{ name : "Less32U" , argLength : 2 , typ : "Bool" } ,
{ name : "Less64" , argLength : 2 , typ : "Bool" } ,
{ name : "Less64U" , argLength : 2 , typ : "Bool" } ,
{ name : "Less32F" , argLength : 2 , typ : "Bool" } ,
{ name : "Less64F" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq8" , argLength : 2 , typ : "Bool" } , // arg0 <= arg1, signed
{ name : "Leq8U" , argLength : 2 , typ : "Bool" } , // arg0 <= arg1, unsigned
{ name : "Leq16" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq16U" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq32" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq32U" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq64" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq64U" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq32F" , argLength : 2 , typ : "Bool" } ,
{ name : "Leq64F" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater8" , argLength : 2 , typ : "Bool" } , // arg0 > arg1, signed
{ name : "Greater8U" , argLength : 2 , typ : "Bool" } , // arg0 > arg1, unsigned
{ name : "Greater16" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater16U" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater32" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater32U" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater64" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater64U" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater32F" , argLength : 2 , typ : "Bool" } ,
{ name : "Greater64F" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq8" , argLength : 2 , typ : "Bool" } , // arg0 <= arg1, signed
{ name : "Geq8U" , argLength : 2 , typ : "Bool" } , // arg0 <= arg1, unsigned
{ name : "Geq16" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq16U" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq32" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq32U" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq64" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq64U" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq32F" , argLength : 2 , typ : "Bool" } ,
{ name : "Geq64F" , argLength : 2 , typ : "Bool" } ,
2015-06-06 16:03:33 -07:00
2016-04-24 21:21:07 +02:00
// boolean ops
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
{ name : "AndB" , argLength : 2 , typ : "Bool" } , // arg0 && arg1 (not shortcircuited)
{ name : "OrB" , argLength : 2 , typ : "Bool" } , // arg0 || arg1 (not shortcircuited)
{ name : "EqB" , argLength : 2 , typ : "Bool" } , // arg0 == arg1
{ name : "NeqB" , argLength : 2 , typ : "Bool" } , // arg0 != arg1
{ name : "Not" , argLength : 1 , typ : "Bool" } , // !arg0, boolean
2016-02-27 08:04:48 -06:00
2016-04-24 21:21:07 +02:00
// 1-input ops
2016-02-27 08:04:48 -06:00
{ name : "Neg8" , argLength : 1 } , // -arg0
{ name : "Neg16" , argLength : 1 } ,
{ name : "Neg32" , argLength : 1 } ,
{ name : "Neg64" , argLength : 1 } ,
{ name : "Neg32F" , argLength : 1 } ,
{ name : "Neg64F" , argLength : 1 } ,
{ name : "Com8" , argLength : 1 } , // ^arg0
{ name : "Com16" , argLength : 1 } ,
{ name : "Com32" , argLength : 1 } ,
{ name : "Com64" , argLength : 1 } ,
2016-08-23 10:43:47 -07:00
{ name : "Ctz32" , argLength : 1 } , // Count trailing (low order) zeroes (returns 0-32)
2016-03-11 00:10:52 -05:00
{ name : "Ctz64" , argLength : 1 } , // Count trailing zeroes (returns 0-64)
{ name : "Bswap32" , argLength : 1 } , // Swap bytes
{ name : "Bswap64" , argLength : 1 } , // Swap bytes
2016-02-27 08:04:48 -06:00
{ name : "Sqrt" , argLength : 1 } , // sqrt(arg0), float64 only
// Data movement, max argument length for Phi is indefinite so just pick
// a really large number
{ name : "Phi" , argLength : - 1 } , // select an argument based on which predecessor block we came from
{ name : "Copy" , argLength : 1 } , // output = arg0
2015-11-10 15:35:36 -08:00
// Convert converts between pointers and integers.
// We have a special op for this so as to not confuse GC
// (particularly stack maps). It takes a memory arg so it
// gets correctly ordered with respect to GC safepoints.
// arg0=ptr/int arg1=mem, output=int/ptr
2016-02-27 08:04:48 -06:00
{ name : "Convert" , argLength : 2 } ,
2015-06-06 16:03:33 -07:00
2016-03-01 23:21:55 +00:00
// constants. Constant values are stored in the aux or
2016-01-25 09:21:17 -08:00
// auxint fields.
2016-01-31 11:39:39 -08:00
{ name : "ConstBool" , aux : "Bool" } , // auxint is 0 for false and 1 for true
{ name : "ConstString" , aux : "String" } , // value is aux.(string)
{ name : "ConstNil" , typ : "BytePtr" } , // nil pointer
2016-03-29 16:39:53 -07:00
{ name : "Const8" , aux : "Int8" } , // auxint is sign-extended 8 bits
{ name : "Const16" , aux : "Int16" } , // auxint is sign-extended 16 bits
{ name : "Const32" , aux : "Int32" } , // auxint is sign-extended 32 bits
2016-01-31 11:39:39 -08:00
{ name : "Const64" , aux : "Int64" } , // value is auxint
2016-03-11 19:36:54 -06:00
{ name : "Const32F" , aux : "Float32" } , // value is math.Float64frombits(uint64(auxint)) and is exactly prepresentable as float 32
{ name : "Const64F" , aux : "Float64" } , // value is math.Float64frombits(uint64(auxint))
2016-01-31 11:39:39 -08:00
{ name : "ConstInterface" } , // nil interface
{ name : "ConstSlice" } , // nil slice
2015-06-06 16:03:33 -07:00
// Constant-like things
2016-01-31 11:39:39 -08:00
{ name : "InitMem" } , // memory input to the function.
{ name : "Arg" , aux : "SymOff" } , // argument to the function. aux=GCNode of arg, off = offset in that arg.
2015-06-19 21:02:28 -07:00
// The address of a variable. arg0 is the base pointer (SB or SP, depending
// on whether it is a global or stack variable). The Aux field identifies the
2016-03-01 23:21:55 +00:00
// variable. It will be either an *ExternSymbol (with arg0=SB), *ArgSymbol (arg0=SP),
2015-06-19 21:02:28 -07:00
// or *AutoSymbol (arg0=SP).
2016-02-27 08:04:48 -06:00
{ name : "Addr" , argLength : 1 , aux : "Sym" } , // Address of a variable. Arg0=SP or SB. Aux identifies the variable.
2015-06-19 21:02:28 -07:00
2015-09-01 09:16:58 -07:00
{ name : "SP" } , // stack pointer
{ name : "SB" , typ : "Uintptr" } , // static base pointer (a.k.a. globals pointer)
2016-01-31 11:39:39 -08:00
{ name : "Func" , aux : "Sym" } , // entry address of a function
2016-09-13 17:01:01 -07:00
{ name : "Invalid" } , // unused value
2015-06-06 16:03:33 -07:00
// Memory operations
2016-08-31 12:30:46 -07:00
{ name : "Load" , argLength : 2 } , // Load from arg0. arg1=memory
{ name : "Store" , argLength : 3 , typ : "Mem" , aux : "Int64" } , // Store arg1 to arg0. arg2=memory, auxint=size. Returns memory.
{ name : "Move" , argLength : 3 , typ : "Mem" , aux : "SizeAndAlign" } , // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size+alignment. Returns memory.
{ name : "Zero" , argLength : 2 , typ : "Mem" , aux : "SizeAndAlign" } , // arg0=destptr, arg1=mem, auxint=size+alignment. Returns memory.
2015-06-06 16:03:33 -07:00
2016-10-13 06:57:00 -04:00
// Memory operations with write barriers.
// Expand to runtime calls. Write barrier will be removed if write on stack.
{ name : "StoreWB" , argLength : 3 , typ : "Mem" , aux : "Int64" } , // Store arg1 to arg0. arg2=memory, auxint=size. Returns memory.
{ name : "MoveWB" , argLength : 3 , typ : "Mem" , aux : "SymSizeAndAlign" } , // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size+alignment, aux=symbol-of-type (for typedmemmove). Returns memory.
{ name : "MoveWBVolatile" , argLength : 3 , typ : "Mem" , aux : "SymSizeAndAlign" } , // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size+alignment, aux=symbol-of-type (for typedmemmove). Returns memory. Src is volatile, i.e. needs to move to a temp space before calling typedmemmove.
// maybe we'll need a ZeroWB for the new barrier
2016-03-01 23:21:55 +00:00
// 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
2015-06-06 16:03:33 -07:00
// as a phantom first argument.
2016-09-09 13:11:07 -07:00
{ name : "ClosureCall" , argLength : 3 , aux : "Int64" , call : true } , // arg0=code pointer, arg1=context ptr, arg2=memory. auxint=arg size. Returns memory.
{ name : "StaticCall" , argLength : 1 , aux : "SymOff" , call : true } , // call function aux.(*gc.Sym), arg0=memory. auxint=arg size. Returns memory.
{ name : "DeferCall" , argLength : 1 , aux : "Int64" , call : true } , // defer call. arg0=memory, auxint=arg size. Returns memory.
{ name : "GoCall" , argLength : 1 , aux : "Int64" , call : true } , // go call. arg0=memory, auxint=arg size. Returns memory.
{ name : "InterCall" , argLength : 2 , aux : "Int64" , call : true } , // interface call. arg0=code pointer, arg1=memory, auxint=arg size. Returns memory.
2015-06-06 16:03:33 -07:00
2015-07-28 14:31:25 -07:00
// Conversions: signed extensions, zero (unsigned) extensions, truncations
2016-02-27 08:04:48 -06:00
{ name : "SignExt8to16" , argLength : 1 , typ : "Int16" } ,
2016-05-06 10:13:31 -07:00
{ name : "SignExt8to32" , argLength : 1 , typ : "Int32" } ,
2016-06-25 16:07:56 -07:00
{ name : "SignExt8to64" , argLength : 1 , typ : "Int64" } ,
2016-05-06 10:13:31 -07:00
{ name : "SignExt16to32" , argLength : 1 , typ : "Int32" } ,
2016-06-25 16:07:56 -07:00
{ name : "SignExt16to64" , argLength : 1 , typ : "Int64" } ,
{ name : "SignExt32to64" , argLength : 1 , typ : "Int64" } ,
2016-02-27 08:04:48 -06:00
{ name : "ZeroExt8to16" , argLength : 1 , typ : "UInt16" } ,
2016-05-06 10:13:31 -07:00
{ name : "ZeroExt8to32" , argLength : 1 , typ : "UInt32" } ,
2016-06-25 16:07:56 -07:00
{ name : "ZeroExt8to64" , argLength : 1 , typ : "UInt64" } ,
2016-05-06 10:13:31 -07:00
{ name : "ZeroExt16to32" , argLength : 1 , typ : "UInt32" } ,
2016-06-25 16:07:56 -07:00
{ name : "ZeroExt16to64" , argLength : 1 , typ : "UInt64" } ,
{ name : "ZeroExt32to64" , argLength : 1 , typ : "UInt64" } ,
2016-02-27 08:04:48 -06:00
{ name : "Trunc16to8" , argLength : 1 } ,
{ name : "Trunc32to8" , argLength : 1 } ,
{ name : "Trunc32to16" , argLength : 1 } ,
{ name : "Trunc64to8" , argLength : 1 } ,
{ name : "Trunc64to16" , argLength : 1 } ,
{ name : "Trunc64to32" , argLength : 1 } ,
{ name : "Cvt32to32F" , argLength : 1 } ,
{ name : "Cvt32to64F" , argLength : 1 } ,
{ name : "Cvt64to32F" , argLength : 1 } ,
{ name : "Cvt64to64F" , argLength : 1 } ,
{ name : "Cvt32Fto32" , argLength : 1 } ,
{ name : "Cvt32Fto64" , argLength : 1 } ,
{ name : "Cvt64Fto32" , argLength : 1 } ,
{ name : "Cvt64Fto64" , argLength : 1 } ,
{ name : "Cvt32Fto64F" , argLength : 1 } ,
{ name : "Cvt64Fto32F" , argLength : 1 } ,
2015-08-20 15:14:20 -04:00
2015-07-24 11:55:52 -07:00
// Automatically inserted safety checks
2016-02-27 08:04:48 -06:00
{ name : "IsNonNil" , argLength : 1 , typ : "Bool" } , // arg0 != nil
2016-03-29 16:39:53 -07:00
{ name : "IsInBounds" , argLength : 2 , typ : "Bool" } , // 0 <= arg0 < arg1. arg1 is guaranteed >= 0.
{ name : "IsSliceInBounds" , argLength : 2 , typ : "Bool" } , // 0 <= arg0 <= arg1. arg1 is guaranteed >= 0.
2016-09-13 17:01:01 -07:00
{ name : "NilCheck" , argLength : 2 , typ : "Void" } , // arg0=ptr, arg1=mem. Panics if arg0 is nil. Returns void.
2015-06-06 16:03:33 -07:00
2015-08-12 11:22:16 -07:00
// Pseudo-ops
2016-03-29 16:39:53 -07:00
{ name : "GetG" , argLength : 1 } , // runtime.getg() (read g pointer). arg0=mem
2016-02-27 08:04:48 -06:00
{ name : "GetClosurePtr" } , // get closure pointer from dedicated register
2015-08-11 09:47:45 -07:00
2015-06-06 16:03:33 -07:00
// Indexing operations
2016-03-29 16:39:53 -07:00
{ name : "ArrayIndex" , aux : "Int64" , argLength : 1 } , // arg0=array, auxint=index. Returns a[i]
2016-03-01 15:59:15 -08:00
{ name : "PtrIndex" , argLength : 2 } , // arg0=ptr, arg1=index. Computes ptr+sizeof(*v.type)*index, where index is extended to ptrwidth type
{ name : "OffPtr" , argLength : 1 , aux : "Int64" } , // arg0 + auxint (arg0 and result are pointers)
2015-06-06 16:03:33 -07:00
// Slices
2016-02-27 08:04:48 -06:00
{ name : "SliceMake" , argLength : 3 } , // arg0=ptr, arg1=len, arg2=cap
{ name : "SlicePtr" , argLength : 1 , typ : "BytePtr" } , // ptr(arg0)
{ name : "SliceLen" , argLength : 1 } , // len(arg0)
{ name : "SliceCap" , argLength : 1 } , // cap(arg0)
2015-06-06 16:03:33 -07:00
2015-08-28 14:24:10 -04:00
// Complex (part/whole)
2016-02-27 08:04:48 -06:00
{ name : "ComplexMake" , argLength : 2 } , // arg0=real, arg1=imag
{ name : "ComplexReal" , argLength : 1 } , // real(arg0)
{ name : "ComplexImag" , argLength : 1 } , // imag(arg0)
2015-08-28 14:24:10 -04:00
2015-06-06 16:03:33 -07:00
// Strings
2016-04-21 19:28:28 -07:00
{ name : "StringMake" , argLength : 2 } , // arg0=ptr, arg1=len
{ name : "StringPtr" , argLength : 1 , typ : "BytePtr" } , // ptr(arg0)
{ name : "StringLen" , argLength : 1 , typ : "Int" } , // len(arg0)
2015-06-06 16:03:33 -07:00
2015-08-04 15:47:22 -07:00
// Interfaces
2016-02-27 08:04:48 -06:00
{ name : "IMake" , argLength : 2 } , // arg0=itab, arg1=data
{ name : "ITab" , argLength : 1 , typ : "BytePtr" } , // arg0=interface, returns itable field
{ name : "IData" , argLength : 1 } , // arg0=interface, returns data field
2015-08-04 15:47:22 -07:00
2016-01-11 21:05:33 -08:00
// Structs
2016-02-27 08:04:48 -06:00
{ name : "StructMake0" } , // Returns struct with 0 fields.
{ name : "StructMake1" , argLength : 1 } , // arg0=field0. Returns struct.
{ name : "StructMake2" , argLength : 2 } , // arg0,arg1=field0,field1. Returns struct.
{ name : "StructMake3" , argLength : 3 } , // arg0..2=field0..2. Returns struct.
{ name : "StructMake4" , argLength : 4 } , // arg0..3=field0..3. Returns struct.
{ name : "StructSelect" , argLength : 1 , aux : "Int64" } , // arg0=struct, auxint=field index. Returns the auxint'th field.
2016-01-11 21:05:33 -08:00
2016-03-01 23:21:55 +00:00
// Spill&restore ops for the register allocator. These are
2015-06-06 16:03:33 -07:00
// semantically identical to OpCopy; they do not take/return
2016-03-01 23:21:55 +00:00
// stores like regular memory ops do. We can get away without memory
2015-06-06 16:03:33 -07:00
// args because we know there is no aliasing of spill slots on the stack.
2016-02-27 08:04:48 -06:00
{ name : "StoreReg" , argLength : 1 } ,
{ name : "LoadReg" , argLength : 1 } ,
2015-06-06 16:03:33 -07:00
2016-03-01 23:21:55 +00:00
// Used during ssa construction. Like Copy, but the arg has not been specified yet.
2016-04-21 19:28:28 -07:00
{ name : "FwdRef" , aux : "Sym" } ,
2015-08-24 02:16:19 -07:00
2016-03-01 23:21:55 +00:00
// Unknown value. Used for Values whose values don't matter because they are dead code.
2016-01-14 16:02:23 -08:00
{ name : "Unknown" } ,
2016-02-27 08:04:48 -06:00
{ name : "VarDef" , argLength : 1 , aux : "Sym" , typ : "Mem" } , // aux is a *gc.Node of a variable that is about to be initialized. arg0=mem, returns mem
{ name : "VarKill" , argLength : 1 , aux : "Sym" } , // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
{ name : "VarLive" , argLength : 1 , aux : "Sym" } , // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
2016-04-21 19:28:28 -07:00
{ name : "KeepAlive" , argLength : 2 , typ : "Mem" } , // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
// Ops for breaking 64-bit operations on 32-bit architectures
{ name : "Int64Make" , argLength : 2 , typ : "UInt64" } , // arg0=hi, arg1=lo
{ name : "Int64Hi" , argLength : 1 , typ : "UInt32" } , // high 32-bit of arg0
{ name : "Int64Lo" , argLength : 1 , typ : "UInt32" } , // low 32-bit of arg0
2016-08-23 16:49:28 -07:00
{ name : "Add32carry" , argLength : 2 , commutative : true , typ : "(UInt32,Flags)" } , // arg0 + arg1, returns (value, carry)
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
{ name : "Add32withcarry" , argLength : 3 , commutative : true } , // arg0 + arg1 + arg2, arg2=carry (0 or 1)
2016-08-23 16:49:28 -07:00
{ name : "Sub32carry" , argLength : 2 , typ : "(UInt32,Flags)" } , // arg0 - arg1, returns (value, carry)
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
{ name : "Sub32withcarry" , argLength : 3 } , // arg0 - arg1 - arg2, arg2=carry (0 or 1)
2016-05-25 23:17:42 -04:00
{ name : "Signmask" , argLength : 1 , typ : "Int32" } , // 0 if arg0 >= 0, -1 if arg0 < 0
{ name : "Zeromask" , argLength : 1 , typ : "UInt32" } , // 0 if arg0 == 0, 0xffffffff if arg0 != 0
2016-10-25 15:49:52 -07:00
{ name : "Slicemask" , argLength : 1 } , // 0 if arg0 == 0, -1 if arg0 > 0, undef if arg0<0. Type is native int size.
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
2016-05-31 11:27:16 -04:00
{ name : "Cvt32Uto32F" , argLength : 1 } , // uint32 -> float32, only used on 32-bit arch
{ name : "Cvt32Uto64F" , argLength : 1 } , // uint32 -> float64, only used on 32-bit arch
{ name : "Cvt32Fto32U" , argLength : 1 } , // float32 -> uint32, only used on 32-bit arch
{ name : "Cvt64Fto32U" , argLength : 1 } , // float64 -> uint32, only used on 32-bit arch
2016-08-16 14:17:33 -04:00
{ name : "Cvt64Uto32F" , argLength : 1 } , // uint64 -> float32, only used on archs that has the instruction
{ name : "Cvt64Uto64F" , argLength : 1 } , // uint64 -> float64, only used on archs that has the instruction
{ name : "Cvt32Fto64U" , argLength : 1 } , // float32 -> uint64, only used on archs that has the instruction
{ name : "Cvt64Fto64U" , argLength : 1 } , // float64 -> uint64, only used on archs that has the instruction
2016-05-31 11:27:16 -04:00
[dev.ssa] cmd/compile: decompose 64-bit integer on ARM
Introduce dec64 rules to (generically) decompose 64-bit integer on
32-bit architectures. 64-bit integer is composed/decomposed with
Int64Make/Hi/Lo ops, as for complex types.
The idea of dealing with Add64 is the following:
(Add64 (Int64Make xh xl) (Int64Make yh yl))
->
(Int64Make
(Add32withcarry xh yh (Select0 (Add32carry xl yl)))
(Select1 (Add32carry xl yl)))
where Add32carry returns a tuple (flags,uint32). Select0 and Select1
read the first and the second component of the tuple, respectively.
The two Add32carry will be CSE'd.
Similarly for multiplication, Mul32uhilo returns a tuple (hi, lo).
Also add support of KeepAlive, to fix build after merge.
Tests addressed_ssa.go, array_ssa.go, break_ssa.go, chan_ssa.go,
cmp_ssa.go, ctl_ssa.go, map_ssa.go, and string_ssa.go in
cmd/compile/internal/gc/testdata passed.
Progress on SSA for ARM. Still not complete.
Updates #15365.
Change-Id: I7867c76785a456312de5d8398a6b3f7ca5a4f7ec
Reviewed-on: https://go-review.googlesource.com/23213
Reviewed-by: Keith Randall <khr@golang.org>
2016-05-18 18:14:36 -04:00
// pseudo-ops for breaking Tuple
{ name : "Select0" , argLength : 1 } , // the first component of a tuple
{ name : "Select1" , argLength : 1 } , // the second component of a tuple
2016-08-23 16:49:28 -07:00
// Atomic operations used for semantically inlining runtime/internal/atomic.
// Atomic loads return a new memory so that the loads are properly ordered
// with respect to other loads and stores.
// TODO: use for sync/atomic at some point.
2016-08-25 16:02:57 -07:00
{ name : "AtomicLoad32" , argLength : 2 , typ : "(UInt32,Mem)" } , // Load from arg0. arg1=memory. Returns loaded value and new memory.
{ name : "AtomicLoad64" , argLength : 2 , typ : "(UInt64,Mem)" } , // Load from arg0. arg1=memory. Returns loaded value and new memory.
{ name : "AtomicLoadPtr" , argLength : 2 , typ : "(BytePtr,Mem)" } , // Load from arg0. arg1=memory. Returns loaded value and new memory.
{ name : "AtomicStore32" , argLength : 3 , typ : "Mem" } , // Store arg1 to *arg0. arg2=memory. Returns memory.
{ name : "AtomicStore64" , argLength : 3 , typ : "Mem" } , // Store arg1 to *arg0. arg2=memory. Returns memory.
{ name : "AtomicStorePtrNoWB" , argLength : 3 , typ : "Mem" } , // Store arg1 to *arg0. arg2=memory. Returns memory.
{ name : "AtomicExchange32" , argLength : 3 , typ : "(UInt32,Mem)" } , // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
{ name : "AtomicExchange64" , argLength : 3 , typ : "(UInt64,Mem)" } , // Store arg1 to *arg0. arg2=memory. Returns old contents of *arg0 and new memory.
{ name : "AtomicAdd32" , argLength : 3 , typ : "(UInt32,Mem)" } , // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
{ name : "AtomicAdd64" , argLength : 3 , typ : "(UInt64,Mem)" } , // Do *arg0 += arg1. arg2=memory. Returns sum and new memory.
{ name : "AtomicCompareAndSwap32" , argLength : 4 , typ : "(Bool,Mem)" } , // if *arg0==arg1, then set *arg0=arg2. Returns true iff store happens and new memory.
{ name : "AtomicCompareAndSwap64" , argLength : 4 , typ : "(Bool,Mem)" } , // if *arg0==arg1, then set *arg0=arg2. Returns true iff store happens and new memory.
{ name : "AtomicAnd8" , argLength : 3 , typ : "Mem" } , // *arg0 &= arg1. arg2=memory. Returns memory.
{ name : "AtomicOr8" , argLength : 3 , typ : "Mem" } , // *arg0 |= arg1. arg2=memory. Returns memory.
2015-06-06 16:03:33 -07:00
}
2015-09-09 18:03:41 -07:00
// kind control successors implicit exit
// ----------------------------------------------------------
// Exit return mem [] yes
// Ret return mem [] yes
// RetJmp return mem [] yes
2015-06-06 16:03:33 -07:00
// Plain nil [next]
// If a boolean Value [then, else]
2015-09-09 18:03:41 -07:00
// Call mem [next] yes (control opcode should be OpCall or OpStaticCall)
2015-10-23 19:12:49 -07:00
// Check void [next] yes (control opcode should be Op{Lowered}NilCheck)
2015-09-08 16:04:37 -07:00
// First nil [always,never]
2015-06-06 16:03:33 -07:00
var genericBlocks = [ ] blockData {
2015-09-08 21:28:44 -07:00
{ name : "Plain" } , // a single successor
{ name : "If" } , // 2 successors, if control goto Succs[0] else goto Succs[1]
2016-03-09 19:27:57 -08:00
{ name : "Defer" } , // 2 successors, Succs[0]=defer queued, Succs[1]=defer recovered. control is call op (of memory type)
2015-09-09 18:03:41 -07:00
{ name : "Ret" } , // no successors, control value is memory result
{ name : "RetJmp" } , // no successors, jumps to b.Aux.(*gc.Sym)
{ name : "Exit" } , // no successors, control value generates a panic
2016-04-28 15:04:10 -07:00
// transient block state used for dead code removal
2015-09-09 18:03:41 -07:00
{ name : "First" } , // 2 successors, always takes the first one (second is dead)
2015-06-06 16:03:33 -07:00
}
func init ( ) {
2016-03-12 14:07:40 -08:00
archs = append ( archs , arch {
2016-03-21 22:57:26 -07:00
name : "generic" ,
ops : genericOps ,
blocks : genericBlocks ,
generic : true ,
2016-03-12 14:07:40 -08:00
} )
2015-06-06 16:03:33 -07:00
}