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
2015-06-14 11:38:46 -07:00
import "strings"
2015-06-06 16:03:33 -07:00
2016-03-29 16:39:53 -07:00
// Notes:
// - Integer types live in the low portion of registers. Upper portions are junk.
// - Boolean types use the low-order byte of a register. 0=false, 1=true.
// Upper bytes are junk.
// - Floating-point types live in the low natural slot of an sse2 register.
// Unused portions are junk.
// - We do not use AH,BH,CH,DH registers.
// - When doing sub-register operations, we try to write the whole
// destination register to avoid a partial-register write.
// - Unused portions of AuxInt (or the Val portion of ValAndOff) are
// filled by sign-extending the used portion. Users of AuxInt which interpret
// AuxInt as unsigned (e.g. shifts) must be careful.
2017-08-28 12:57:52 -07:00
// - All SymOff opcodes require their offset to fit in an int32.
2016-03-29 16:39:53 -07:00
// Suffixes encode the bit width of various instructions.
// Q (quad word) = 64 bit
// L (long word) = 32 bit
// W (word) = 16 bit
// B (byte) = 8 bit
2015-06-06 16:03:33 -07:00
// copied from ../../amd64/reg.go
var regNamesAMD64 = [ ] string {
2016-03-22 09:43:28 -07:00
"AX" ,
"CX" ,
"DX" ,
"BX" ,
"SP" ,
"BP" ,
"SI" ,
"DI" ,
"R8" ,
"R9" ,
"R10" ,
"R11" ,
"R12" ,
"R13" ,
"R14" ,
"R15" ,
"X0" ,
"X1" ,
"X2" ,
"X3" ,
"X4" ,
"X5" ,
"X6" ,
"X7" ,
"X8" ,
"X9" ,
"X10" ,
"X11" ,
"X12" ,
"X13" ,
"X14" ,
"X15" ,
2015-06-06 16:03:33 -07:00
// pseudo-registers
2016-03-22 09:43:28 -07:00
"SB" ,
2015-06-06 16:03:33 -07:00
}
func init ( ) {
// Make map from reg names to reg integers.
if len ( regNamesAMD64 ) > 64 {
panic ( "too many registers" )
}
num := map [ string ] int { }
for i , name := range regNamesAMD64 {
2016-03-22 09:43:28 -07:00
num [ name ] = i
2015-06-06 16:03:33 -07:00
}
buildReg := func ( s string ) regMask {
m := regMask ( 0 )
for _ , r := range strings . Split ( s , " " ) {
if n , ok := num [ r ] ; ok {
m |= regMask ( 1 ) << uint ( n )
continue
}
panic ( "register " + r + " not found" )
}
return m
}
2015-08-06 09:34:54 -07:00
// Common individual register masks
var (
2015-08-17 17:46:06 -05:00
ax = buildReg ( "AX" )
2015-08-11 12:51:33 -07:00
cx = buildReg ( "CX" )
2015-08-17 17:46:06 -05:00
dx = buildReg ( "DX" )
2015-08-11 12:51:33 -07:00
gp = buildReg ( "AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R14 R15" )
fp = buildReg ( "X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15" )
gpsp = gp | buildReg ( "SP" )
gpspsb = gpsp | buildReg ( "SB" )
2016-08-04 06:57:34 -04:00
callerSave = gp | fp
2015-08-06 09:34:54 -07:00
)
// Common slices of register masks
var (
2016-08-04 06:57:34 -04:00
gponly = [ ] regMask { gp }
fponly = [ ] regMask { fp }
2015-08-06 09:34:54 -07:00
)
// Common regInfo
var (
2016-08-04 06:57:34 -04:00
gp01 = regInfo { inputs : nil , outputs : gponly }
gp11 = regInfo { inputs : [ ] regMask { gp } , outputs : gponly }
gp11sp = regInfo { inputs : [ ] regMask { gpsp } , outputs : gponly }
2015-08-22 09:52:36 -07:00
gp11sb = regInfo { inputs : [ ] regMask { gpspsb } , outputs : gponly }
2016-08-04 06:57:34 -04:00
gp21 = regInfo { inputs : [ ] regMask { gp , gp } , outputs : gponly }
gp21sp = regInfo { inputs : [ ] regMask { gpsp , gp } , outputs : gponly }
2015-08-22 09:52:36 -07:00
gp21sb = regInfo { inputs : [ ] regMask { gpspsb , gpsp } , outputs : gponly }
2016-08-04 06:57:34 -04:00
gp21shift = regInfo { inputs : [ ] regMask { gp , cx } , outputs : [ ] regMask { gp } }
gp11div = regInfo { inputs : [ ] regMask { ax , gpsp &^ dx } , outputs : [ ] regMask { ax , dx } }
gp21hmul = regInfo { inputs : [ ] regMask { ax , gpsp } , outputs : [ ] regMask { dx } , clobbers : ax }
2015-08-06 09:34:54 -07:00
2018-01-03 14:38:55 -08:00
gp2flags = regInfo { inputs : [ ] regMask { gpsp , gpsp } }
gp1flags = regInfo { inputs : [ ] regMask { gpsp } }
gp0flagsLoad = regInfo { inputs : [ ] regMask { gpspsb , 0 } }
gp1flagsLoad = regInfo { inputs : [ ] regMask { gpspsb , gpsp , 0 } }
flagsgp = regInfo { inputs : nil , outputs : gponly }
2016-03-11 00:10:52 -05:00
2016-08-23 10:43:47 -07:00
gp11flags = regInfo { inputs : [ ] regMask { gp } , outputs : [ ] regMask { gp , 0 } }
2016-03-11 00:10:52 -05:00
2016-08-04 06:57:34 -04:00
readflags = regInfo { inputs : nil , outputs : gponly }
flagsgpax = regInfo { inputs : nil , clobbers : ax , outputs : [ ] regMask { gp &^ ax } }
2015-08-06 09:34:54 -07:00
gpload = regInfo { inputs : [ ] regMask { gpspsb , 0 } , outputs : gponly }
2017-02-10 13:17:20 -06:00
gp21load = regInfo { inputs : [ ] regMask { gp , gpspsb , 0 } , outputs : gponly }
2015-08-06 09:34:54 -07:00
gploadidx = regInfo { inputs : [ ] regMask { gpspsb , gpsp , 0 } , outputs : gponly }
2018-03-05 20:59:40 +01:00
gp21pax = regInfo { inputs : [ ] regMask { gp &^ ax , gp } , outputs : [ ] regMask { gp &^ ax } , clobbers : ax }
2015-08-06 09:34:54 -07:00
2016-02-04 15:53:33 -08:00
gpstore = regInfo { inputs : [ ] regMask { gpspsb , gpsp , 0 } }
gpstoreconst = regInfo { inputs : [ ] regMask { gpspsb , 0 } }
gpstoreidx = regInfo { inputs : [ ] regMask { gpspsb , gpsp , gpsp , 0 } }
gpstoreconstidx = regInfo { inputs : [ ] regMask { gpspsb , gpsp , 0 } }
2017-02-19 00:16:27 -08:00
gpstorexchg = regInfo { inputs : [ ] regMask { gp , gpspsb , 0 } , outputs : [ ] regMask { gp } }
2016-08-25 16:02:57 -07:00
cmpxchg = regInfo { inputs : [ ] regMask { gp , ax , gp , 0 } , outputs : [ ] regMask { gp , 0 } , clobbers : ax }
2015-08-12 16:38:11 -04:00
2016-08-31 12:35:32 -07:00
fp01 = regInfo { inputs : nil , outputs : fponly }
fp21 = regInfo { inputs : [ ] regMask { fp , fp } , outputs : fponly }
2017-02-10 13:17:20 -06:00
fp21load = regInfo { inputs : [ ] regMask { fp , gpspsb , 0 } , outputs : fponly }
2015-08-18 14:39:26 -04:00
fpgp = regInfo { inputs : fponly , outputs : gponly }
gpfp = regInfo { inputs : gponly , outputs : fponly }
fp11 = regInfo { inputs : fponly , outputs : fponly }
2016-08-04 06:57:34 -04:00
fp2flags = regInfo { inputs : [ ] regMask { fp , fp } }
2015-08-12 16:38:11 -04:00
fpload = regInfo { inputs : [ ] regMask { gpspsb , 0 } , outputs : fponly }
fploadidx = regInfo { inputs : [ ] regMask { gpspsb , gpsp , 0 } , outputs : fponly }
fpstore = regInfo { inputs : [ ] regMask { gpspsb , fp , 0 } }
fpstoreidx = regInfo { inputs : [ ] regMask { gpspsb , gpsp , fp , 0 } }
2015-08-06 09:34:54 -07:00
)
2015-06-06 16:03:33 -07:00
var AMD64ops = [ ] opData {
2015-08-12 16:38:11 -04:00
// fp ops
2016-03-10 13:05:56 -08:00
{ name : "ADDSS" , argLength : 2 , reg : fp21 , asm : "ADDSS" , commutative : true , resultInArg0 : true } , // fp32 add
{ name : "ADDSD" , argLength : 2 , reg : fp21 , asm : "ADDSD" , commutative : true , resultInArg0 : true } , // fp64 add
2016-08-31 12:35:32 -07:00
{ name : "SUBSS" , argLength : 2 , reg : fp21 , asm : "SUBSS" , resultInArg0 : true } , // fp32 sub
{ name : "SUBSD" , argLength : 2 , reg : fp21 , asm : "SUBSD" , resultInArg0 : true } , // fp64 sub
2016-03-10 13:05:56 -08:00
{ name : "MULSS" , argLength : 2 , reg : fp21 , asm : "MULSS" , commutative : true , resultInArg0 : true } , // fp32 mul
{ name : "MULSD" , argLength : 2 , reg : fp21 , asm : "MULSD" , commutative : true , resultInArg0 : true } , // fp64 mul
2016-08-31 12:35:32 -07:00
{ name : "DIVSS" , argLength : 2 , reg : fp21 , asm : "DIVSS" , resultInArg0 : true } , // fp32 div
{ name : "DIVSD" , argLength : 2 , reg : fp21 , asm : "DIVSD" , resultInArg0 : true } , // fp64 div
2016-02-27 08:04:48 -06:00
2017-03-09 14:46:43 -08:00
{ name : "MOVSSload" , argLength : 2 , reg : fpload , asm : "MOVSS" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Read" } , // fp32 load
{ name : "MOVSDload" , argLength : 2 , reg : fpload , asm : "MOVSD" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Read" } , // fp64 load
{ name : "MOVSSconst" , reg : fp01 , asm : "MOVSS" , aux : "Float32" , rematerializeable : true } , // fp32 constant
{ name : "MOVSDconst" , reg : fp01 , asm : "MOVSD" , aux : "Float64" , rematerializeable : true } , // fp64 constant
{ name : "MOVSSloadidx1" , argLength : 3 , reg : fploadidx , asm : "MOVSS" , aux : "SymOff" , symEffect : "Read" } , // fp32 load indexed by i
{ name : "MOVSSloadidx4" , argLength : 3 , reg : fploadidx , asm : "MOVSS" , aux : "SymOff" , symEffect : "Read" } , // fp32 load indexed by 4*i
{ name : "MOVSDloadidx1" , argLength : 3 , reg : fploadidx , asm : "MOVSD" , aux : "SymOff" , symEffect : "Read" } , // fp64 load indexed by i
{ name : "MOVSDloadidx8" , argLength : 3 , reg : fploadidx , asm : "MOVSD" , aux : "SymOff" , symEffect : "Read" } , // fp64 load indexed by 8*i
{ name : "MOVSSstore" , argLength : 3 , reg : fpstore , asm : "MOVSS" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Write" } , // fp32 store
{ name : "MOVSDstore" , argLength : 3 , reg : fpstore , asm : "MOVSD" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Write" } , // fp64 store
{ name : "MOVSSstoreidx1" , argLength : 4 , reg : fpstoreidx , asm : "MOVSS" , aux : "SymOff" , symEffect : "Write" } , // fp32 indexed by i store
{ name : "MOVSSstoreidx4" , argLength : 4 , reg : fpstoreidx , asm : "MOVSS" , aux : "SymOff" , symEffect : "Write" } , // fp32 indexed by 4i store
{ name : "MOVSDstoreidx1" , argLength : 4 , reg : fpstoreidx , asm : "MOVSD" , aux : "SymOff" , symEffect : "Write" } , // fp64 indexed by i store
{ name : "MOVSDstoreidx8" , argLength : 4 , reg : fpstoreidx , asm : "MOVSD" , aux : "SymOff" , symEffect : "Write" } , // fp64 indexed by 8i store
{ name : "ADDSSmem" , argLength : 3 , reg : fp21load , asm : "ADDSS" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , symEffect : "Read" } , // fp32 arg0 + tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
2017-07-31 11:38:13 +02:00
{ name : "ADDSDmem" , argLength : 3 , reg : fp21load , asm : "ADDSD" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , symEffect : "Read" } , // fp64 arg0 + tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
2017-03-09 14:46:43 -08:00
{ name : "SUBSSmem" , argLength : 3 , reg : fp21load , asm : "SUBSS" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , symEffect : "Read" } , // fp32 arg0 - tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "SUBSDmem" , argLength : 3 , reg : fp21load , asm : "SUBSD" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , symEffect : "Read" } , // fp64 arg0 - tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "MULSSmem" , argLength : 3 , reg : fp21load , asm : "MULSS" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , symEffect : "Read" } , // fp32 arg0 * tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "MULSDmem" , argLength : 3 , reg : fp21load , asm : "MULSD" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , symEffect : "Read" } , // fp64 arg0 * tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
2017-02-10 13:17:20 -06:00
2015-07-28 16:04:50 -07:00
// binary ops
2017-08-09 15:43:25 -05:00
{ name : "ADDQ" , argLength : 2 , reg : gp21sp , asm : "ADDQ" , commutative : true , clobberFlags : true } , // arg0 + arg1
{ name : "ADDL" , argLength : 2 , reg : gp21sp , asm : "ADDL" , commutative : true , clobberFlags : true } , // arg0 + arg1
2017-09-11 21:23:06 +01:00
{ name : "ADDQconst" , argLength : 1 , reg : gp11sp , asm : "ADDQ" , aux : "Int32" , typ : "UInt64" , clobberFlags : true } , // arg0 + auxint
{ name : "ADDLconst" , argLength : 1 , reg : gp11sp , asm : "ADDL" , aux : "Int32" , clobberFlags : true } , // arg0 + auxint
2017-08-09 15:43:25 -05:00
{ name : "ADDQconstmem" , argLength : 2 , reg : gpstoreconst , asm : "ADDQ" , aux : "SymValAndOff" , clobberFlags : true , faultOnNilArg0 : true , symEffect : "Write" } , // add ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux, arg1=mem
{ name : "ADDLconstmem" , argLength : 2 , reg : gpstoreconst , asm : "ADDL" , aux : "SymValAndOff" , clobberFlags : true , faultOnNilArg0 : true , symEffect : "Write" } , // add ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux, arg1=mem
2016-08-04 06:57:34 -04:00
{ name : "SUBQ" , argLength : 2 , reg : gp21 , asm : "SUBQ" , resultInArg0 : true , clobberFlags : true } , // arg0 - arg1
{ name : "SUBL" , argLength : 2 , reg : gp21 , asm : "SUBL" , resultInArg0 : true , clobberFlags : true } , // arg0 - arg1
2017-08-28 12:57:52 -07:00
{ name : "SUBQconst" , argLength : 1 , reg : gp11 , asm : "SUBQ" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 - auxint
2016-08-04 06:57:34 -04:00
{ name : "SUBLconst" , argLength : 1 , reg : gp11 , asm : "SUBL" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 - auxint
{ name : "MULQ" , argLength : 2 , reg : gp21 , asm : "IMULQ" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 * arg1
{ name : "MULL" , argLength : 2 , reg : gp21 , asm : "IMULL" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 * arg1
2018-03-09 23:09:46 +03:00
{ name : "MULQconst" , argLength : 1 , reg : gp11 , asm : "IMUL3Q" , aux : "Int32" , clobberFlags : true } , // arg0 * auxint
{ name : "MULLconst" , argLength : 1 , reg : gp11 , asm : "IMUL3L" , aux : "Int32" , clobberFlags : true } , // arg0 * auxint
2016-08-04 06:57:34 -04:00
2017-03-30 03:30:22 +00:00
{ name : "HMULQ" , argLength : 2 , reg : gp21hmul , commutative : true , asm : "IMULQ" , clobberFlags : true } , // (arg0 * arg1) >> width
{ name : "HMULL" , argLength : 2 , reg : gp21hmul , commutative : true , asm : "IMULL" , clobberFlags : true } , // (arg0 * arg1) >> width
{ name : "HMULQU" , argLength : 2 , reg : gp21hmul , commutative : true , asm : "MULQ" , clobberFlags : true } , // (arg0 * arg1) >> width
{ name : "HMULLU" , argLength : 2 , reg : gp21hmul , commutative : true , asm : "MULL" , clobberFlags : true } , // (arg0 * arg1) >> width
2016-08-04 06:57:34 -04:00
{ name : "AVGQU" , argLength : 2 , reg : gp21 , commutative : true , resultInArg0 : true , clobberFlags : true } , // (arg0 + arg1) / 2 as unsigned, all 64 result bits
{ name : "DIVQ" , argLength : 2 , reg : gp11div , typ : "(Int64,Int64)" , asm : "IDIVQ" , clobberFlags : true } , // [arg0 / arg1, arg0 % arg1]
{ name : "DIVL" , argLength : 2 , reg : gp11div , typ : "(Int32,Int32)" , asm : "IDIVL" , clobberFlags : true } , // [arg0 / arg1, arg0 % arg1]
{ name : "DIVW" , argLength : 2 , reg : gp11div , typ : "(Int16,Int16)" , asm : "IDIVW" , clobberFlags : true } , // [arg0 / arg1, arg0 % arg1]
{ name : "DIVQU" , argLength : 2 , reg : gp11div , typ : "(UInt64,UInt64)" , asm : "DIVQ" , clobberFlags : true } , // [arg0 / arg1, arg0 % arg1]
{ name : "DIVLU" , argLength : 2 , reg : gp11div , typ : "(UInt32,UInt32)" , asm : "DIVL" , clobberFlags : true } , // [arg0 / arg1, arg0 % arg1]
{ name : "DIVWU" , argLength : 2 , reg : gp11div , typ : "(UInt16,UInt16)" , asm : "DIVW" , clobberFlags : true } , // [arg0 / arg1, arg0 % arg1]
2017-03-30 03:30:22 +00:00
{ name : "MULQU2" , argLength : 2 , reg : regInfo { inputs : [ ] regMask { ax , gpsp } , outputs : [ ] regMask { dx , ax } } , commutative : true , asm : "MULQ" , clobberFlags : true } , // arg0 * arg1, returns (hi, lo)
{ name : "DIVQU2" , argLength : 3 , reg : regInfo { inputs : [ ] regMask { dx , ax , gpsp } , outputs : [ ] regMask { ax , dx } } , asm : "DIVQ" , clobberFlags : true } , // arg0:arg1 / arg2 (128-bit divided by 64-bit), returns (q, r)
2016-10-06 15:43:47 -04:00
2016-08-04 06:57:34 -04:00
{ name : "ANDQ" , argLength : 2 , reg : gp21 , asm : "ANDQ" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 & arg1
{ name : "ANDL" , argLength : 2 , reg : gp21 , asm : "ANDL" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 & arg1
2017-08-28 12:57:52 -07:00
{ name : "ANDQconst" , argLength : 1 , reg : gp11 , asm : "ANDQ" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 & auxint
2016-08-04 06:57:34 -04:00
{ name : "ANDLconst" , argLength : 1 , reg : gp11 , asm : "ANDL" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 & auxint
{ name : "ORQ" , argLength : 2 , reg : gp21 , asm : "ORQ" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 | arg1
{ name : "ORL" , argLength : 2 , reg : gp21 , asm : "ORL" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 | arg1
2017-08-28 12:57:52 -07:00
{ name : "ORQconst" , argLength : 1 , reg : gp11 , asm : "ORQ" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 | auxint
2016-08-04 06:57:34 -04:00
{ name : "ORLconst" , argLength : 1 , reg : gp11 , asm : "ORL" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 | auxint
{ name : "XORQ" , argLength : 2 , reg : gp21 , asm : "XORQ" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 ^ arg1
{ name : "XORL" , argLength : 2 , reg : gp21 , asm : "XORL" , commutative : true , resultInArg0 : true , clobberFlags : true } , // arg0 ^ arg1
2017-08-28 12:57:52 -07:00
{ name : "XORQconst" , argLength : 1 , reg : gp11 , asm : "XORQ" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 ^ auxint
2016-08-04 06:57:34 -04:00
{ name : "XORLconst" , argLength : 1 , reg : gp11 , asm : "XORL" , aux : "Int32" , resultInArg0 : true , clobberFlags : true } , // arg0 ^ auxint
2016-02-27 08:04:48 -06:00
{ name : "CMPQ" , argLength : 2 , reg : gp2flags , asm : "CMPQ" , typ : "Flags" } , // arg0 compare to arg1
{ name : "CMPL" , argLength : 2 , reg : gp2flags , asm : "CMPL" , typ : "Flags" } , // arg0 compare to arg1
{ name : "CMPW" , argLength : 2 , reg : gp2flags , asm : "CMPW" , typ : "Flags" } , // arg0 compare to arg1
{ name : "CMPB" , argLength : 2 , reg : gp2flags , asm : "CMPB" , typ : "Flags" } , // arg0 compare to arg1
2017-08-28 12:57:52 -07:00
{ name : "CMPQconst" , argLength : 1 , reg : gp1flags , asm : "CMPQ" , typ : "Flags" , aux : "Int32" } , // arg0 compare to auxint
2016-02-27 08:04:48 -06:00
{ name : "CMPLconst" , argLength : 1 , reg : gp1flags , asm : "CMPL" , typ : "Flags" , aux : "Int32" } , // arg0 compare to auxint
{ name : "CMPWconst" , argLength : 1 , reg : gp1flags , asm : "CMPW" , typ : "Flags" , aux : "Int16" } , // arg0 compare to auxint
{ name : "CMPBconst" , argLength : 1 , reg : gp1flags , asm : "CMPB" , typ : "Flags" , aux : "Int8" } , // arg0 compare to auxint
2018-01-03 14:38:55 -08:00
// compare *(arg0+auxint+aux) to arg1 (in that order). arg2=mem.
{ name : "CMPQmem" , argLength : 3 , reg : gp1flagsLoad , asm : "CMPQ" , aux : "SymOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
{ name : "CMPLmem" , argLength : 3 , reg : gp1flagsLoad , asm : "CMPL" , aux : "SymOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
{ name : "CMPWmem" , argLength : 3 , reg : gp1flagsLoad , asm : "CMPW" , aux : "SymOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
{ name : "CMPBmem" , argLength : 3 , reg : gp1flagsLoad , asm : "CMPB" , aux : "SymOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
// compare *(arg0+ValAndOff(AuxInt).Off()+aux) to ValAndOff(AuxInt).Val() (in that order). arg1=mem.
{ name : "CMPQconstmem" , argLength : 2 , reg : gp0flagsLoad , asm : "CMPQ" , aux : "SymValAndOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
{ name : "CMPLconstmem" , argLength : 2 , reg : gp0flagsLoad , asm : "CMPL" , aux : "SymValAndOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
{ name : "CMPWconstmem" , argLength : 2 , reg : gp0flagsLoad , asm : "CMPW" , aux : "SymValAndOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
{ name : "CMPBconstmem" , argLength : 2 , reg : gp0flagsLoad , asm : "CMPB" , aux : "SymValAndOff" , typ : "Flags" , symEffect : "Read" , faultOnNilArg0 : true } ,
2016-02-27 08:04:48 -06:00
{ name : "UCOMISS" , argLength : 2 , reg : fp2flags , asm : "UCOMISS" , typ : "Flags" } , // arg0 compare to arg1, f32
{ name : "UCOMISD" , argLength : 2 , reg : fp2flags , asm : "UCOMISD" , typ : "Flags" } , // arg0 compare to arg1, f64
2017-02-06 10:55:39 -08:00
{ name : "BTL" , argLength : 2 , reg : gp2flags , asm : "BTL" , typ : "Flags" } , // test whether bit arg0 % 32 in arg1 is set
{ name : "BTQ" , argLength : 2 , reg : gp2flags , asm : "BTQ" , typ : "Flags" } , // test whether bit arg0 % 64 in arg1 is set
{ name : "BTLconst" , argLength : 1 , reg : gp1flags , asm : "BTL" , typ : "Flags" , aux : "Int8" } , // test whether bit auxint in arg0 is set, 0 <= auxint < 32
{ name : "BTQconst" , argLength : 1 , reg : gp1flags , asm : "BTQ" , typ : "Flags" , aux : "Int8" } , // test whether bit auxint in arg0 is set, 0 <= auxint < 64
2017-03-30 03:30:22 +00:00
{ name : "TESTQ" , argLength : 2 , reg : gp2flags , commutative : true , asm : "TESTQ" , typ : "Flags" } , // (arg0 & arg1) compare to 0
{ name : "TESTL" , argLength : 2 , reg : gp2flags , commutative : true , asm : "TESTL" , typ : "Flags" } , // (arg0 & arg1) compare to 0
{ name : "TESTW" , argLength : 2 , reg : gp2flags , commutative : true , asm : "TESTW" , typ : "Flags" } , // (arg0 & arg1) compare to 0
{ name : "TESTB" , argLength : 2 , reg : gp2flags , commutative : true , asm : "TESTB" , typ : "Flags" } , // (arg0 & arg1) compare to 0
2017-08-28 12:57:52 -07:00
{ name : "TESTQconst" , argLength : 1 , reg : gp1flags , asm : "TESTQ" , typ : "Flags" , aux : "Int32" } , // (arg0 & auxint) compare to 0
2016-02-27 08:04:48 -06:00
{ name : "TESTLconst" , argLength : 1 , reg : gp1flags , asm : "TESTL" , typ : "Flags" , aux : "Int32" } , // (arg0 & auxint) compare to 0
{ name : "TESTWconst" , argLength : 1 , reg : gp1flags , asm : "TESTW" , typ : "Flags" , aux : "Int16" } , // (arg0 & auxint) compare to 0
{ name : "TESTBconst" , argLength : 1 , reg : gp1flags , asm : "TESTB" , typ : "Flags" , aux : "Int8" } , // (arg0 & auxint) compare to 0
2017-03-30 03:30:22 +00:00
{ name : "SHLQ" , argLength : 2 , reg : gp21shift , asm : "SHLQ" , resultInArg0 : true , clobberFlags : true } , // arg0 << arg1, shift amount is mod 64
{ name : "SHLL" , argLength : 2 , reg : gp21shift , asm : "SHLL" , resultInArg0 : true , clobberFlags : true } , // arg0 << arg1, shift amount is mod 32
{ name : "SHLQconst" , argLength : 1 , reg : gp11 , asm : "SHLQ" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // arg0 << auxint, shift amount 0-63
{ name : "SHLLconst" , argLength : 1 , reg : gp11 , asm : "SHLL" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // arg0 << auxint, shift amount 0-31
2015-07-28 16:04:50 -07:00
// Note: x86 is weird, the 16 and 8 byte shifts still use all 5 bits of shift amount!
2017-03-30 03:30:22 +00:00
{ name : "SHRQ" , argLength : 2 , reg : gp21shift , asm : "SHRQ" , resultInArg0 : true , clobberFlags : true } , // unsigned arg0 >> arg1, shift amount is mod 64
2018-02-13 12:33:55 -08:00
{ name : "SHRL" , argLength : 2 , reg : gp21shift , asm : "SHRL" , resultInArg0 : true , clobberFlags : true } , // unsigned uint32(arg0) >> arg1, shift amount is mod 32
{ name : "SHRW" , argLength : 2 , reg : gp21shift , asm : "SHRW" , resultInArg0 : true , clobberFlags : true } , // unsigned uint16(arg0) >> arg1, shift amount is mod 32
{ name : "SHRB" , argLength : 2 , reg : gp21shift , asm : "SHRB" , resultInArg0 : true , clobberFlags : true } , // unsigned uint8(arg0) >> arg1, shift amount is mod 32
2017-03-30 03:30:22 +00:00
{ name : "SHRQconst" , argLength : 1 , reg : gp11 , asm : "SHRQ" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // unsigned arg0 >> auxint, shift amount 0-63
2018-02-13 12:33:55 -08:00
{ name : "SHRLconst" , argLength : 1 , reg : gp11 , asm : "SHRL" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // unsigned uint32(arg0) >> auxint, shift amount 0-31
{ name : "SHRWconst" , argLength : 1 , reg : gp11 , asm : "SHRW" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // unsigned uint16(arg0) >> auxint, shift amount 0-15
{ name : "SHRBconst" , argLength : 1 , reg : gp11 , asm : "SHRB" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // unsigned uint8(arg0) >> auxint, shift amount 0-7
2017-03-30 03:30:22 +00:00
{ name : "SARQ" , argLength : 2 , reg : gp21shift , asm : "SARQ" , resultInArg0 : true , clobberFlags : true } , // signed arg0 >> arg1, shift amount is mod 64
2018-02-13 12:33:55 -08:00
{ name : "SARL" , argLength : 2 , reg : gp21shift , asm : "SARL" , resultInArg0 : true , clobberFlags : true } , // signed int32(arg0) >> arg1, shift amount is mod 32
{ name : "SARW" , argLength : 2 , reg : gp21shift , asm : "SARW" , resultInArg0 : true , clobberFlags : true } , // signed int16(arg0) >> arg1, shift amount is mod 32
{ name : "SARB" , argLength : 2 , reg : gp21shift , asm : "SARB" , resultInArg0 : true , clobberFlags : true } , // signed int8(arg0) >> arg1, shift amount is mod 32
2017-03-30 03:30:22 +00:00
{ name : "SARQconst" , argLength : 1 , reg : gp11 , asm : "SARQ" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // signed arg0 >> auxint, shift amount 0-63
2018-02-13 12:33:55 -08:00
{ name : "SARLconst" , argLength : 1 , reg : gp11 , asm : "SARL" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // signed int32(arg0) >> auxint, shift amount 0-31
{ name : "SARWconst" , argLength : 1 , reg : gp11 , asm : "SARW" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // signed int16(arg0) >> auxint, shift amount 0-15
{ name : "SARBconst" , argLength : 1 , reg : gp11 , asm : "SARB" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // signed int8(arg0) >> auxint, shift amount 0-7
2017-03-30 03:30:22 +00:00
2017-03-29 10:04:17 -07:00
{ name : "ROLQ" , argLength : 2 , reg : gp21shift , asm : "ROLQ" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left arg1 bits.
{ name : "ROLL" , argLength : 2 , reg : gp21shift , asm : "ROLL" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left arg1 bits.
{ name : "ROLW" , argLength : 2 , reg : gp21shift , asm : "ROLW" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left arg1 bits.
{ name : "ROLB" , argLength : 2 , reg : gp21shift , asm : "ROLB" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left arg1 bits.
{ name : "RORQ" , argLength : 2 , reg : gp21shift , asm : "RORQ" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate right arg1 bits.
{ name : "RORL" , argLength : 2 , reg : gp21shift , asm : "RORL" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate right arg1 bits.
{ name : "RORW" , argLength : 2 , reg : gp21shift , asm : "RORW" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate right arg1 bits.
{ name : "RORB" , argLength : 2 , reg : gp21shift , asm : "RORB" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate right arg1 bits.
2017-03-30 03:30:22 +00:00
{ name : "ROLQconst" , argLength : 1 , reg : gp11 , asm : "ROLQ" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left auxint, rotate amount 0-63
{ name : "ROLLconst" , argLength : 1 , reg : gp11 , asm : "ROLL" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left auxint, rotate amount 0-31
{ name : "ROLWconst" , argLength : 1 , reg : gp11 , asm : "ROLW" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left auxint, rotate amount 0-15
{ name : "ROLBconst" , argLength : 1 , reg : gp11 , asm : "ROLB" , aux : "Int8" , resultInArg0 : true , clobberFlags : true } , // arg0 rotate left auxint, rotate amount 0-7
2015-08-05 22:11:14 -04:00
2017-03-09 14:46:43 -08:00
{ name : "ADDLmem" , argLength : 3 , reg : gp21load , asm : "ADDL" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 + tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "ADDQmem" , argLength : 3 , reg : gp21load , asm : "ADDQ" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 + tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "SUBQmem" , argLength : 3 , reg : gp21load , asm : "SUBQ" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 - tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "SUBLmem" , argLength : 3 , reg : gp21load , asm : "SUBL" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 - tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "ANDLmem" , argLength : 3 , reg : gp21load , asm : "ANDL" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 & tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "ANDQmem" , argLength : 3 , reg : gp21load , asm : "ANDQ" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 & tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "ORQmem" , argLength : 3 , reg : gp21load , asm : "ORQ" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 | tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "ORLmem" , argLength : 3 , reg : gp21load , asm : "ORL" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 | tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "XORQmem" , argLength : 3 , reg : gp21load , asm : "XORQ" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 ^ tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
{ name : "XORLmem" , argLength : 3 , reg : gp21load , asm : "XORL" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , symEffect : "Read" } , // arg0 ^ tmp, tmp loaded from arg1+auxint+aux, arg2 = mem
2017-02-10 13:17:20 -06:00
2015-07-28 16:04:50 -07:00
// unary ops
2016-08-04 06:57:34 -04:00
{ name : "NEGQ" , argLength : 1 , reg : gp11 , asm : "NEGQ" , resultInArg0 : true , clobberFlags : true } , // -arg0
{ name : "NEGL" , argLength : 1 , reg : gp11 , asm : "NEGL" , resultInArg0 : true , clobberFlags : true } , // -arg0
2015-06-06 16:03:33 -07:00
2016-08-04 06:57:34 -04:00
{ name : "NOTQ" , argLength : 1 , reg : gp11 , asm : "NOTQ" , resultInArg0 : true , clobberFlags : true } , // ^arg0
{ name : "NOTL" , argLength : 1 , reg : gp11 , asm : "NOTL" , resultInArg0 : true , clobberFlags : true } , // ^arg0
2015-07-29 17:07:09 -07:00
2016-08-23 10:43:47 -07:00
// BSF{L,Q} returns a tuple [result, flags]
// result is undefined if the input is zero.
// flags are set to "equal" if the input is zero, "not equal" otherwise.
{ name : "BSFQ" , argLength : 1 , reg : gp11flags , asm : "BSFQ" , typ : "(UInt64,Flags)" } , // # of low-order zeroes in 64-bit arg
{ name : "BSFL" , argLength : 1 , reg : gp11flags , asm : "BSFL" , typ : "(UInt32,Flags)" } , // # of low-order zeroes in 32-bit arg
2017-03-16 14:08:31 -07:00
{ name : "BSRQ" , argLength : 1 , reg : gp11flags , asm : "BSRQ" , typ : "(UInt64,Flags)" } , // # of high-order zeroes in 64-bit arg
{ name : "BSRL" , argLength : 1 , reg : gp11flags , asm : "BSRL" , typ : "(UInt32,Flags)" } , // # of high-order zeroes in 32-bit arg
2016-03-11 00:10:52 -05:00
2018-03-05 20:59:40 +01:00
// CMOV instructions: 64, 32 and 16-bit sizes.
// if arg2 encodes a true result, return arg1, else arg0
{ name : "CMOVQEQ" , argLength : 3 , reg : gp21 , asm : "CMOVQEQ" , resultInArg0 : true } ,
{ name : "CMOVQNE" , argLength : 3 , reg : gp21 , asm : "CMOVQNE" , resultInArg0 : true } ,
{ name : "CMOVQLT" , argLength : 3 , reg : gp21 , asm : "CMOVQLT" , resultInArg0 : true } ,
{ name : "CMOVQGT" , argLength : 3 , reg : gp21 , asm : "CMOVQGT" , resultInArg0 : true } ,
{ name : "CMOVQLE" , argLength : 3 , reg : gp21 , asm : "CMOVQLE" , resultInArg0 : true } ,
{ name : "CMOVQGE" , argLength : 3 , reg : gp21 , asm : "CMOVQGE" , resultInArg0 : true } ,
{ name : "CMOVQLS" , argLength : 3 , reg : gp21 , asm : "CMOVQLS" , resultInArg0 : true } ,
{ name : "CMOVQHI" , argLength : 3 , reg : gp21 , asm : "CMOVQHI" , resultInArg0 : true } ,
{ name : "CMOVQCC" , argLength : 3 , reg : gp21 , asm : "CMOVQCC" , resultInArg0 : true } ,
{ name : "CMOVQCS" , argLength : 3 , reg : gp21 , asm : "CMOVQCS" , resultInArg0 : true } ,
{ name : "CMOVLEQ" , argLength : 3 , reg : gp21 , asm : "CMOVLEQ" , resultInArg0 : true } ,
{ name : "CMOVLNE" , argLength : 3 , reg : gp21 , asm : "CMOVLNE" , resultInArg0 : true } ,
{ name : "CMOVLLT" , argLength : 3 , reg : gp21 , asm : "CMOVLLT" , resultInArg0 : true } ,
{ name : "CMOVLGT" , argLength : 3 , reg : gp21 , asm : "CMOVLGT" , resultInArg0 : true } ,
{ name : "CMOVLLE" , argLength : 3 , reg : gp21 , asm : "CMOVLLE" , resultInArg0 : true } ,
{ name : "CMOVLGE" , argLength : 3 , reg : gp21 , asm : "CMOVLGE" , resultInArg0 : true } ,
{ name : "CMOVLLS" , argLength : 3 , reg : gp21 , asm : "CMOVLLS" , resultInArg0 : true } ,
{ name : "CMOVLHI" , argLength : 3 , reg : gp21 , asm : "CMOVLHI" , resultInArg0 : true } ,
{ name : "CMOVLCC" , argLength : 3 , reg : gp21 , asm : "CMOVLCC" , resultInArg0 : true } ,
{ name : "CMOVLCS" , argLength : 3 , reg : gp21 , asm : "CMOVLCS" , resultInArg0 : true } ,
{ name : "CMOVWEQ" , argLength : 3 , reg : gp21 , asm : "CMOVWEQ" , resultInArg0 : true } ,
{ name : "CMOVWNE" , argLength : 3 , reg : gp21 , asm : "CMOVWNE" , resultInArg0 : true } ,
{ name : "CMOVWLT" , argLength : 3 , reg : gp21 , asm : "CMOVWLT" , resultInArg0 : true } ,
{ name : "CMOVWGT" , argLength : 3 , reg : gp21 , asm : "CMOVWGT" , resultInArg0 : true } ,
{ name : "CMOVWLE" , argLength : 3 , reg : gp21 , asm : "CMOVWLE" , resultInArg0 : true } ,
{ name : "CMOVWGE" , argLength : 3 , reg : gp21 , asm : "CMOVWGE" , resultInArg0 : true } ,
{ name : "CMOVWLS" , argLength : 3 , reg : gp21 , asm : "CMOVWLS" , resultInArg0 : true } ,
{ name : "CMOVWHI" , argLength : 3 , reg : gp21 , asm : "CMOVWHI" , resultInArg0 : true } ,
{ name : "CMOVWCC" , argLength : 3 , reg : gp21 , asm : "CMOVWCC" , resultInArg0 : true } ,
{ name : "CMOVWCS" , argLength : 3 , reg : gp21 , asm : "CMOVWCS" , resultInArg0 : true } ,
// CMOV with floating point instructions. We need separate pseudo-op to handle
// InvertFlags correctly, and to generate special code that handles NaN (unordered flag).
// NOTE: the fact that CMOV*EQF here is marked to generate CMOV*NE is not a bug. See
// code generation in amd64/ssa.go.
{ name : "CMOVQEQF" , argLength : 3 , reg : gp21pax , asm : "CMOVQNE" , resultInArg0 : true } ,
{ name : "CMOVQNEF" , argLength : 3 , reg : gp21 , asm : "CMOVQNE" , resultInArg0 : true } ,
{ name : "CMOVQGTF" , argLength : 3 , reg : gp21 , asm : "CMOVQHI" , resultInArg0 : true } ,
{ name : "CMOVQGEF" , argLength : 3 , reg : gp21 , asm : "CMOVQCC" , resultInArg0 : true } ,
{ name : "CMOVLEQF" , argLength : 3 , reg : gp21 , asm : "CMOVLNE" , resultInArg0 : true } ,
{ name : "CMOVLNEF" , argLength : 3 , reg : gp21 , asm : "CMOVLNE" , resultInArg0 : true } ,
{ name : "CMOVLGTF" , argLength : 3 , reg : gp21 , asm : "CMOVLHI" , resultInArg0 : true } ,
{ name : "CMOVLGEF" , argLength : 3 , reg : gp21 , asm : "CMOVLCC" , resultInArg0 : true } ,
{ name : "CMOVWEQF" , argLength : 3 , reg : gp21 , asm : "CMOVWNE" , resultInArg0 : true } ,
{ name : "CMOVWNEF" , argLength : 3 , reg : gp21 , asm : "CMOVWNE" , resultInArg0 : true } ,
{ name : "CMOVWGTF" , argLength : 3 , reg : gp21 , asm : "CMOVWHI" , resultInArg0 : true } ,
{ name : "CMOVWGEF" , argLength : 3 , reg : gp21 , asm : "CMOVWCC" , resultInArg0 : true } ,
2016-03-11 00:10:52 -05:00
2016-08-04 06:57:34 -04:00
{ name : "BSWAPQ" , argLength : 1 , reg : gp11 , asm : "BSWAPQ" , resultInArg0 : true , clobberFlags : true } , // arg0 swap bytes
{ name : "BSWAPL" , argLength : 1 , reg : gp11 , asm : "BSWAPL" , resultInArg0 : true , clobberFlags : true } , // arg0 swap bytes
2016-03-11 00:10:52 -05:00
2017-03-16 21:33:03 -07:00
// POPCNT instructions aren't guaranteed to be on the target platform (they are SSE4).
// Any use must be preceded by a successful check of runtime.support_popcnt.
{ name : "POPCNTQ" , argLength : 1 , reg : gp11 , asm : "POPCNTQ" , clobberFlags : true } , // count number of set bits in arg0
{ name : "POPCNTL" , argLength : 1 , reg : gp11 , asm : "POPCNTL" , clobberFlags : true } , // count number of set bits in arg0
2016-02-27 08:04:48 -06:00
{ name : "SQRTSD" , argLength : 1 , reg : fp11 , asm : "SQRTSD" } , // sqrt(arg0)
2015-09-12 13:26:57 -07:00
2017-10-05 15:45:46 -05:00
// ROUNDSD instruction isn't guaranteed to be on the target platform (it is SSE4.1)
// Any use must be preceded by a successful check of runtime.support_sse41.
{ name : "ROUNDSD" , argLength : 1 , reg : fp11 , aux : "Int8" , asm : "ROUNDSD" } , // rounds arg0 depending on auxint, 1 means math.Floor, 2 Ceil, 3 Trunc
2016-02-27 08:04:48 -06:00
{ name : "SBBQcarrymask" , argLength : 1 , reg : flagsgp , asm : "SBBQ" } , // (int64)(-1) if carry is set, 0 if carry is clear.
{ name : "SBBLcarrymask" , argLength : 1 , reg : flagsgp , asm : "SBBL" } , // (int32)(-1) if carry is set, 0 if carry is clear.
2015-07-29 17:07:09 -07:00
// Note: SBBW and SBBB are subsumed by SBBL
2015-06-10 10:39:57 -07:00
2016-02-27 08:04:48 -06:00
{ name : "SETEQ" , argLength : 1 , reg : readflags , asm : "SETEQ" } , // extract == condition from arg0
{ name : "SETNE" , argLength : 1 , reg : readflags , asm : "SETNE" } , // extract != condition from arg0
{ name : "SETL" , argLength : 1 , reg : readflags , asm : "SETLT" } , // extract signed < condition from arg0
{ name : "SETLE" , argLength : 1 , reg : readflags , asm : "SETLE" } , // extract signed <= condition from arg0
{ name : "SETG" , argLength : 1 , reg : readflags , asm : "SETGT" } , // extract signed > condition from arg0
{ name : "SETGE" , argLength : 1 , reg : readflags , asm : "SETGE" } , // extract signed >= condition from arg0
{ name : "SETB" , argLength : 1 , reg : readflags , asm : "SETCS" } , // extract unsigned < condition from arg0
{ name : "SETBE" , argLength : 1 , reg : readflags , asm : "SETLS" } , // extract unsigned <= condition from arg0
{ name : "SETA" , argLength : 1 , reg : readflags , asm : "SETHI" } , // extract unsigned > condition from arg0
{ name : "SETAE" , argLength : 1 , reg : readflags , asm : "SETCC" } , // extract unsigned >= condition from arg0
2017-10-03 14:12:00 -05:00
// Variants that store result to memory
{ name : "SETEQmem" , argLength : 3 , reg : gpstoreconst , asm : "SETEQ" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract == condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETNEmem" , argLength : 3 , reg : gpstoreconst , asm : "SETNE" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract != condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETLmem" , argLength : 3 , reg : gpstoreconst , asm : "SETLT" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract signed < condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETLEmem" , argLength : 3 , reg : gpstoreconst , asm : "SETLE" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract signed <= condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETGmem" , argLength : 3 , reg : gpstoreconst , asm : "SETGT" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract signed > condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETGEmem" , argLength : 3 , reg : gpstoreconst , asm : "SETGE" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract signed >= condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETBmem" , argLength : 3 , reg : gpstoreconst , asm : "SETCS" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract unsigned < condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETBEmem" , argLength : 3 , reg : gpstoreconst , asm : "SETLS" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract unsigned <= condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETAmem" , argLength : 3 , reg : gpstoreconst , asm : "SETHI" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract unsigned > condition from arg1 to arg0+auxint+aux, arg2=mem
{ name : "SETAEmem" , argLength : 3 , reg : gpstoreconst , asm : "SETCC" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // extract unsigned >= condition from arg1 to arg0+auxint+aux, arg2=mem
2015-08-18 14:39:26 -04:00
// Need different opcodes for floating point conditions because
// any comparison involving a NaN is always FALSE and thus
// the patterns for inverting conditions cannot be used.
2016-08-04 06:57:34 -04:00
{ name : "SETEQF" , argLength : 1 , reg : flagsgpax , asm : "SETEQ" , clobberFlags : true } , // extract == condition from arg0
{ name : "SETNEF" , argLength : 1 , reg : flagsgpax , asm : "SETNE" , clobberFlags : true } , // extract != condition from arg0
{ name : "SETORD" , argLength : 1 , reg : flagsgp , asm : "SETPC" } , // extract "ordered" (No Nan present) condition from arg0
{ name : "SETNAN" , argLength : 1 , reg : flagsgp , asm : "SETPS" } , // extract "unordered" (Nan present) condition from arg0
2015-08-18 14:39:26 -04:00
2016-02-27 08:04:48 -06:00
{ name : "SETGF" , argLength : 1 , reg : flagsgp , asm : "SETHI" } , // extract floating > condition from arg0
{ name : "SETGEF" , argLength : 1 , reg : flagsgp , asm : "SETCC" } , // extract floating >= condition from arg0
2015-06-06 16:03:33 -07:00
2016-08-04 06:57:34 -04:00
{ name : "MOVBQSX" , argLength : 1 , reg : gp11 , asm : "MOVBQSX" } , // sign extend arg0 from int8 to int64
2016-06-02 12:41:42 -07:00
{ name : "MOVBQZX" , argLength : 1 , reg : gp11 , asm : "MOVBLZX" } , // zero extend arg0 from int8 to int64
2016-08-04 06:57:34 -04:00
{ name : "MOVWQSX" , argLength : 1 , reg : gp11 , asm : "MOVWQSX" } , // sign extend arg0 from int16 to int64
2016-06-02 12:41:42 -07:00
{ name : "MOVWQZX" , argLength : 1 , reg : gp11 , asm : "MOVWLZX" } , // zero extend arg0 from int16 to int64
2016-08-04 06:57:34 -04:00
{ name : "MOVLQSX" , argLength : 1 , reg : gp11 , asm : "MOVLQSX" } , // sign extend arg0 from int32 to int64
2016-06-02 12:41:42 -07:00
{ name : "MOVLQZX" , argLength : 1 , reg : gp11 , asm : "MOVL" } , // zero extend arg0 from int32 to int64
2015-06-14 11:38:46 -07:00
2016-01-31 11:39:39 -08:00
{ name : "MOVLconst" , reg : gp01 , asm : "MOVL" , typ : "UInt32" , aux : "Int32" , rematerializeable : true } , // 32 low bits of auxint
{ name : "MOVQconst" , reg : gp01 , asm : "MOVQ" , typ : "UInt64" , aux : "Int64" , rematerializeable : true } , // auxint
2015-07-28 14:19:20 -07:00
2016-02-27 08:04:48 -06:00
{ name : "CVTTSD2SL" , argLength : 1 , reg : fpgp , asm : "CVTTSD2SL" } , // convert float64 to int32
{ name : "CVTTSD2SQ" , argLength : 1 , reg : fpgp , asm : "CVTTSD2SQ" } , // convert float64 to int64
{ name : "CVTTSS2SL" , argLength : 1 , reg : fpgp , asm : "CVTTSS2SL" } , // convert float32 to int32
{ name : "CVTTSS2SQ" , argLength : 1 , reg : fpgp , asm : "CVTTSS2SQ" } , // convert float32 to int64
{ name : "CVTSL2SS" , argLength : 1 , reg : gpfp , asm : "CVTSL2SS" } , // convert int32 to float32
{ name : "CVTSL2SD" , argLength : 1 , reg : gpfp , asm : "CVTSL2SD" } , // convert int32 to float64
{ name : "CVTSQ2SS" , argLength : 1 , reg : gpfp , asm : "CVTSQ2SS" } , // convert int64 to float32
{ name : "CVTSQ2SD" , argLength : 1 , reg : gpfp , asm : "CVTSQ2SD" } , // convert int64 to float64
{ name : "CVTSD2SS" , argLength : 1 , reg : fp11 , asm : "CVTSD2SS" } , // convert float64 to float32
{ name : "CVTSS2SD" , argLength : 1 , reg : fp11 , asm : "CVTSS2SD" } , // convert float32 to float64
cmd/compile,math: improve code generation for math.Abs
Implement int reg <-> fp reg moves on amd64.
If we see a load to int reg followed by an int->fp move, then we can just
load to the fp reg instead. Same for stores.
math.Abs is now:
MOVQ "".x+8(SP), AX
SHLQ $1, AX
SHRQ $1, AX
MOVQ AX, "".~r1+16(SP)
math.Copysign is now:
MOVQ "".x+8(SP), AX
SHLQ $1, AX
SHRQ $1, AX
MOVQ "".y+16(SP), CX
SHRQ $63, CX
SHLQ $63, CX
ORQ CX, AX
MOVQ AX, "".~r2+24(SP)
math.Float64bits is now:
MOVSD "".x+8(SP), X0
MOVSD X0, "".~r1+16(SP)
(it would be nicer to use a non-SSE reg for this, nothing is perfect)
And due to the fix for #21440, the inlined version of these improve as well.
name old time/op new time/op delta
Abs 1.38ns ± 5% 0.89ns ±10% -35.54% (p=0.000 n=10+10)
Copysign 1.56ns ± 7% 1.35ns ± 6% -13.77% (p=0.000 n=9+10)
Fixes #13095
Change-Id: Ibd7f2792412a6668608780b0688a77062e1f1499
Reviewed-on: https://go-review.googlesource.com/58732
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Ilya Tocar <ilya.tocar@intel.com>
2017-08-24 13:19:40 -07:00
// Move values between int and float registers, with no conversion.
// TODO: should we have generic versions of these?
{ name : "MOVQi2f" , argLength : 1 , reg : gpfp , typ : "Float64" } , // move 64 bits from int to float reg
{ name : "MOVQf2i" , argLength : 1 , reg : fpgp , typ : "UInt64" } , // move 64 bits from float to int reg
{ name : "MOVLi2f" , argLength : 1 , reg : gpfp , typ : "Float32" } , // move 32 bits from int to float reg
{ name : "MOVLf2i" , argLength : 1 , reg : fpgp , typ : "UInt32" } , // move 32 bits from float to int reg
2016-03-10 13:05:56 -08:00
{ name : "PXOR" , argLength : 2 , reg : fp21 , asm : "PXOR" , commutative : true , resultInArg0 : true } , // exclusive or, applied to X regs for float negation.
2016-02-27 08:04:48 -06:00
2017-03-09 14:46:43 -08:00
{ name : "LEAQ" , argLength : 1 , reg : gp11sb , asm : "LEAQ" , aux : "SymOff" , rematerializeable : true , symEffect : "Addr" } , // arg0 + auxint + offset encoded in aux
2017-03-30 03:30:22 +00:00
{ name : "LEAQ1" , argLength : 2 , reg : gp21sb , commutative : true , aux : "SymOff" , symEffect : "Addr" } , // arg0 + arg1 + auxint + aux
2017-03-09 14:46:43 -08:00
{ name : "LEAQ2" , argLength : 2 , reg : gp21sb , aux : "SymOff" , symEffect : "Addr" } , // arg0 + 2*arg1 + auxint + aux
{ name : "LEAQ4" , argLength : 2 , reg : gp21sb , aux : "SymOff" , symEffect : "Addr" } , // arg0 + 4*arg1 + auxint + aux
{ name : "LEAQ8" , argLength : 2 , reg : gp21sb , aux : "SymOff" , symEffect : "Addr" } , // arg0 + 8*arg1 + auxint + aux
2016-02-02 11:13:50 -08:00
// Note: LEAQ{1,2,4,8} must not have OpSB as either argument.
2015-06-06 16:03:33 -07:00
2017-03-09 14:46:43 -08:00
{ name : "LEAL" , argLength : 1 , reg : gp11sb , asm : "LEAL" , aux : "SymOff" , rematerializeable : true , symEffect : "Addr" } , // arg0 + auxint + offset encoded in aux
2016-08-08 11:26:25 -07:00
2015-08-23 21:14:25 -07:00
// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
2017-03-09 14:46:43 -08:00
{ name : "MOVBload" , argLength : 2 , reg : gpload , asm : "MOVBLZX" , aux : "SymOff" , typ : "UInt8" , faultOnNilArg0 : true , symEffect : "Read" } , // load byte from arg0+auxint+aux. arg1=mem. Zero extend.
{ name : "MOVBQSXload" , argLength : 2 , reg : gpload , asm : "MOVBQSX" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Read" } , // ditto, sign extend to int64
{ name : "MOVWload" , argLength : 2 , reg : gpload , asm : "MOVWLZX" , aux : "SymOff" , typ : "UInt16" , faultOnNilArg0 : true , symEffect : "Read" } , // load 2 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
{ name : "MOVWQSXload" , argLength : 2 , reg : gpload , asm : "MOVWQSX" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Read" } , // ditto, sign extend to int64
{ name : "MOVLload" , argLength : 2 , reg : gpload , asm : "MOVL" , aux : "SymOff" , typ : "UInt32" , faultOnNilArg0 : true , symEffect : "Read" } , // load 4 bytes from arg0+auxint+aux. arg1=mem. Zero extend.
{ name : "MOVLQSXload" , argLength : 2 , reg : gpload , asm : "MOVLQSX" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Read" } , // ditto, sign extend to int64
{ name : "MOVQload" , argLength : 2 , reg : gpload , asm : "MOVQ" , aux : "SymOff" , typ : "UInt64" , faultOnNilArg0 : true , symEffect : "Read" } , // load 8 bytes from arg0+auxint+aux. arg1=mem
{ name : "MOVBstore" , argLength : 3 , reg : gpstore , asm : "MOVB" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store byte in arg1 to arg0+auxint+aux. arg2=mem
{ name : "MOVWstore" , argLength : 3 , reg : gpstore , asm : "MOVW" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
{ name : "MOVLstore" , argLength : 3 , reg : gpstore , asm : "MOVL" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
{ name : "MOVQstore" , argLength : 3 , reg : gpstore , asm : "MOVQ" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
{ name : "MOVOload" , argLength : 2 , reg : fpload , asm : "MOVUPS" , aux : "SymOff" , typ : "Int128" , faultOnNilArg0 : true , symEffect : "Read" } , // load 16 bytes from arg0+auxint+aux. arg1=mem
{ name : "MOVOstore" , argLength : 3 , reg : fpstore , asm : "MOVUPS" , aux : "SymOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store 16 bytes in arg1 to arg0+auxint+aux. arg2=mem
2016-01-31 11:39:39 -08:00
2016-02-02 11:13:50 -08:00
// indexed loads/stores
2017-03-30 03:30:22 +00:00
{ name : "MOVBloadidx1" , argLength : 3 , reg : gploadidx , commutative : true , asm : "MOVBLZX" , aux : "SymOff" , typ : "UInt8" , symEffect : "Read" } , // load a byte from arg0+arg1+auxint+aux. arg2=mem
{ name : "MOVWloadidx1" , argLength : 3 , reg : gploadidx , commutative : true , asm : "MOVWLZX" , aux : "SymOff" , typ : "UInt16" , symEffect : "Read" } , // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem
{ name : "MOVWloadidx2" , argLength : 3 , reg : gploadidx , asm : "MOVWLZX" , aux : "SymOff" , typ : "UInt16" , symEffect : "Read" } , // load 2 bytes from arg0+2*arg1+auxint+aux. arg2=mem
{ name : "MOVLloadidx1" , argLength : 3 , reg : gploadidx , commutative : true , asm : "MOVL" , aux : "SymOff" , typ : "UInt32" , symEffect : "Read" } , // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem
{ name : "MOVLloadidx4" , argLength : 3 , reg : gploadidx , asm : "MOVL" , aux : "SymOff" , typ : "UInt32" , symEffect : "Read" } , // load 4 bytes from arg0+4*arg1+auxint+aux. arg2=mem
2017-06-20 15:36:34 -05:00
{ name : "MOVLloadidx8" , argLength : 3 , reg : gploadidx , asm : "MOVL" , aux : "SymOff" , typ : "UInt32" , symEffect : "Read" } , // load 4 bytes from arg0+8*arg1+auxint+aux. arg2=mem
2017-03-30 03:30:22 +00:00
{ name : "MOVQloadidx1" , argLength : 3 , reg : gploadidx , commutative : true , asm : "MOVQ" , aux : "SymOff" , typ : "UInt64" , symEffect : "Read" } , // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem
{ name : "MOVQloadidx8" , argLength : 3 , reg : gploadidx , asm : "MOVQ" , aux : "SymOff" , typ : "UInt64" , symEffect : "Read" } , // load 8 bytes from arg0+8*arg1+auxint+aux. arg2=mem
2016-02-02 11:13:50 -08:00
// TODO: sign-extending indexed loads
2017-03-30 03:30:22 +00:00
// TODO: mark the MOVXstoreidx1 ops as commutative. Generates too many rewrite rules at the moment.
2017-03-29 18:06:04 +00:00
{ name : "MOVBstoreidx1" , argLength : 4 , reg : gpstoreidx , asm : "MOVB" , aux : "SymOff" , symEffect : "Write" } , // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
{ name : "MOVWstoreidx1" , argLength : 4 , reg : gpstoreidx , asm : "MOVW" , aux : "SymOff" , symEffect : "Write" } , // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
{ name : "MOVWstoreidx2" , argLength : 4 , reg : gpstoreidx , asm : "MOVW" , aux : "SymOff" , symEffect : "Write" } , // store 2 bytes in arg2 to arg0+2*arg1+auxint+aux. arg3=mem
{ name : "MOVLstoreidx1" , argLength : 4 , reg : gpstoreidx , asm : "MOVL" , aux : "SymOff" , symEffect : "Write" } , // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
{ name : "MOVLstoreidx4" , argLength : 4 , reg : gpstoreidx , asm : "MOVL" , aux : "SymOff" , symEffect : "Write" } , // store 4 bytes in arg2 to arg0+4*arg1+auxint+aux. arg3=mem
2017-06-20 15:36:34 -05:00
{ name : "MOVLstoreidx8" , argLength : 4 , reg : gpstoreidx , asm : "MOVL" , aux : "SymOff" , symEffect : "Write" } , // store 4 bytes in arg2 to arg0+8*arg1+auxint+aux. arg3=mem
2017-03-29 18:06:04 +00:00
{ name : "MOVQstoreidx1" , argLength : 4 , reg : gpstoreidx , asm : "MOVQ" , aux : "SymOff" , symEffect : "Write" } , // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
{ name : "MOVQstoreidx8" , argLength : 4 , reg : gpstoreidx , asm : "MOVQ" , aux : "SymOff" , symEffect : "Write" } , // store 8 bytes in arg2 to arg0+8*arg1+auxint+aux. arg3=mem
2016-02-02 11:13:50 -08:00
// TODO: add size-mismatched indexed loads, like MOVBstoreidx4.
2015-09-18 18:23:34 -07:00
2015-10-21 13:13:56 -07:00
// For storeconst ops, the AuxInt field encodes both
// the value to store and an address offset of the store.
2016-01-26 15:47:08 -08:00
// Cast AuxInt to a ValAndOff to extract Val and Off fields.
2017-03-09 14:46:43 -08:00
{ name : "MOVBstoreconst" , argLength : 2 , reg : gpstoreconst , asm : "MOVB" , aux : "SymValAndOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store low byte of ValAndOff(AuxInt).Val() to arg0+ValAndOff(AuxInt).Off()+aux. arg1=mem
{ name : "MOVWstoreconst" , argLength : 2 , reg : gpstoreconst , asm : "MOVW" , aux : "SymValAndOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store low 2 bytes of ...
{ name : "MOVLstoreconst" , argLength : 2 , reg : gpstoreconst , asm : "MOVL" , aux : "SymValAndOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store low 4 bytes of ...
{ name : "MOVQstoreconst" , argLength : 2 , reg : gpstoreconst , asm : "MOVQ" , aux : "SymValAndOff" , typ : "Mem" , faultOnNilArg0 : true , symEffect : "Write" } , // store 8 bytes of ...
{ name : "MOVBstoreconstidx1" , argLength : 3 , reg : gpstoreconstidx , asm : "MOVB" , aux : "SymValAndOff" , typ : "Mem" , symEffect : "Write" } , // store low byte of ValAndOff(AuxInt).Val() to arg0+1*arg1+ValAndOff(AuxInt).Off()+aux. arg2=mem
{ name : "MOVWstoreconstidx1" , argLength : 3 , reg : gpstoreconstidx , asm : "MOVW" , aux : "SymValAndOff" , typ : "Mem" , symEffect : "Write" } , // store low 2 bytes of ... arg1 ...
{ name : "MOVWstoreconstidx2" , argLength : 3 , reg : gpstoreconstidx , asm : "MOVW" , aux : "SymValAndOff" , typ : "Mem" , symEffect : "Write" } , // store low 2 bytes of ... 2*arg1 ...
{ name : "MOVLstoreconstidx1" , argLength : 3 , reg : gpstoreconstidx , asm : "MOVL" , aux : "SymValAndOff" , typ : "Mem" , symEffect : "Write" } , // store low 4 bytes of ... arg1 ...
{ name : "MOVLstoreconstidx4" , argLength : 3 , reg : gpstoreconstidx , asm : "MOVL" , aux : "SymValAndOff" , typ : "Mem" , symEffect : "Write" } , // store low 4 bytes of ... 4*arg1 ...
{ name : "MOVQstoreconstidx1" , argLength : 3 , reg : gpstoreconstidx , asm : "MOVQ" , aux : "SymValAndOff" , typ : "Mem" , symEffect : "Write" } , // store 8 bytes of ... arg1 ...
{ name : "MOVQstoreconstidx8" , argLength : 3 , reg : gpstoreconstidx , asm : "MOVQ" , aux : "SymValAndOff" , typ : "Mem" , symEffect : "Write" } , // store 8 bytes of ... 8*arg1 ...
2016-02-04 15:53:33 -08:00
2016-07-28 12:22:49 -04:00
// arg0 = pointer to start of memory to zero
2015-09-18 18:23:34 -07:00
// arg1 = value to store (will always be zero)
// arg2 = mem
2016-07-28 12:22:49 -04:00
// auxint = # of bytes to zero
2015-09-18 18:23:34 -07:00
// returns mem
{
2016-02-27 08:04:48 -06:00
name : "DUFFZERO" ,
aux : "Int64" ,
argLength : 3 ,
2015-09-18 18:23:34 -07:00
reg : regInfo {
2015-10-19 13:56:55 -07:00
inputs : [ ] regMask { buildReg ( "DI" ) , buildReg ( "X0" ) } ,
2016-08-04 06:57:34 -04:00
clobbers : buildReg ( "DI" ) ,
2015-09-18 18:23:34 -07:00
} ,
2016-11-21 11:31:39 -05:00
faultOnNilArg0 : true ,
2015-09-18 18:23:34 -07:00
} ,
2016-04-20 11:17:41 -07:00
{ name : "MOVOconst" , reg : regInfo { nil , 0 , [ ] regMask { fp } } , typ : "Int128" , aux : "Int128" , rematerializeable : true } ,
2015-09-18 18:23:34 -07:00
// arg0 = address of memory to zero
// arg1 = # of 8-byte words to zero
// arg2 = value to store (will always be zero)
// arg3 = mem
// returns mem
{
2016-02-27 08:04:48 -06:00
name : "REPSTOSQ" ,
argLength : 4 ,
2015-09-18 18:23:34 -07:00
reg : regInfo {
inputs : [ ] regMask { buildReg ( "DI" ) , buildReg ( "CX" ) , buildReg ( "AX" ) } ,
2016-03-01 15:59:15 -08:00
clobbers : buildReg ( "DI CX" ) ,
2015-09-18 18:23:34 -07:00
} ,
2016-11-21 11:31:39 -05:00
faultOnNilArg0 : true ,
2015-09-18 18:23:34 -07:00
} ,
2016-06-08 22:02:08 -07:00
{ name : "CALLstatic" , argLength : 1 , reg : regInfo { clobbers : callerSave } , aux : "SymOff" , clobberFlags : true , call : true , symEffect : "None" } , // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem
2016-09-09 13:11:07 -07:00
{ name : "CALLclosure" , argLength : 3 , reg : regInfo { inputs : [ ] regMask { gpsp , buildReg ( "DX" ) , 0 } , clobbers : callerSave } , aux : "Int64" , clobberFlags : true , call : true } , // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
{ name : "CALLinter" , argLength : 2 , reg : regInfo { inputs : [ ] regMask { gp } , clobbers : callerSave } , aux : "Int64" , clobberFlags : true , call : true } , // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
2015-06-10 15:03:06 -07:00
2015-10-21 17:18:07 -07:00
// arg0 = destination pointer
// arg1 = source pointer
// arg2 = mem
// auxint = offset from duffcopy symbol to call
// returns memory
{
2016-02-27 08:04:48 -06:00
name : "DUFFCOPY" ,
aux : "Int64" ,
argLength : 3 ,
2015-10-21 17:18:07 -07:00
reg : regInfo {
inputs : [ ] regMask { buildReg ( "DI" ) , buildReg ( "SI" ) } ,
2016-08-04 06:57:34 -04:00
clobbers : buildReg ( "DI SI X0" ) , // uses X0 as a temporary
2015-10-21 17:18:07 -07:00
} ,
2016-11-21 11:31:39 -05:00
clobberFlags : true ,
faultOnNilArg0 : true ,
faultOnNilArg1 : true ,
2015-10-21 17:18:07 -07:00
} ,
// arg0 = destination pointer
// arg1 = source pointer
// arg2 = # of 8-byte words to copy
// arg3 = mem
// returns memory
{
2016-02-27 08:04:48 -06:00
name : "REPMOVSQ" ,
argLength : 4 ,
2015-10-21 17:18:07 -07:00
reg : regInfo {
inputs : [ ] regMask { buildReg ( "DI" ) , buildReg ( "SI" ) , buildReg ( "CX" ) } ,
clobbers : buildReg ( "DI SI CX" ) ,
} ,
2016-11-21 11:31:39 -05:00
faultOnNilArg0 : true ,
faultOnNilArg1 : true ,
2015-10-21 17:18:07 -07:00
} ,
2015-06-06 16:03:33 -07:00
// (InvertFlags (CMPQ a b)) == (CMPQ b a)
// So if we want (SETL (CMPQ a b)) but we can't do that because a is a constant,
// then we do (SETL (InvertFlags (CMPQ b a))) instead.
// Rewrites will convert this to (SETG (CMPQ b a)).
// InvertFlags is a pseudo-op which can't appear in assembly output.
2016-02-27 08:04:48 -06:00
{ name : "InvertFlags" , argLength : 1 } , // reverse direction of arg0
2015-08-11 09:47:45 -07:00
2015-08-12 11:22:16 -07:00
// Pseudo-ops
2016-02-27 08:04:48 -06:00
{ name : "LoweredGetG" , argLength : 1 , reg : gp01 } , // arg0=mem
2015-09-11 16:40:05 -04:00
// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
// and sorts it to the very beginning of the block to prevent other
// use of DX (the closure pointer)
2018-02-28 16:30:07 -05:00
{ name : "LoweredGetClosurePtr" , reg : regInfo { outputs : [ ] regMask { buildReg ( "DX" ) } } , zeroWidth : true } ,
2016-10-24 10:25:05 -04:00
// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
// I.e., if f calls g "calls" getcallerpc,
// the result should be the PC within f that g will return to.
// See runtime/stubs.go for a more detailed discussion.
2017-10-13 11:05:12 -04:00
{ name : "LoweredGetCallerPC" , reg : gp01 , rematerializeable : true } ,
2017-10-09 15:33:29 -04:00
// LoweredGetCallerSP returns the SP of the caller of the current function.
{ name : "LoweredGetCallerSP" , reg : gp01 , rematerializeable : true } ,
2015-10-23 19:12:49 -07:00
//arg0=ptr,arg1=mem, returns void. Faults if ptr is nil.
2016-09-27 14:39:27 -07:00
{ name : "LoweredNilCheck" , argLength : 2 , reg : regInfo { inputs : [ ] regMask { gpsp } } , clobberFlags : true , nilCheck : true , faultOnNilArg0 : true } ,
2017-10-26 12:33:04 -04:00
// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
// It saves all GP registers if necessary, but may clobber others.
2017-11-15 14:54:24 -08:00
{ name : "LoweredWB" , argLength : 3 , reg : regInfo { inputs : [ ] regMask { buildReg ( "DI" ) , ax } , clobbers : callerSave &^ gp } , clobberFlags : true , aux : "Sym" , symEffect : "None" } ,
2017-10-26 12:33:04 -04:00
2015-11-10 15:35:36 -08:00
// MOVQconvert 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
2018-02-28 16:30:07 -05:00
{ name : "MOVQconvert" , argLength : 2 , reg : gp11 , asm : "MOVQ" , resultInArg0 : true , zeroWidth : true } ,
{ name : "MOVLconvert" , argLength : 2 , reg : gp11 , asm : "MOVL" , resultInArg0 : true , zeroWidth : true } , // amd64p32 equivalent
2016-01-05 14:56:26 -08:00
2016-03-01 23:21:55 +00:00
// Constant flag values. For any comparison, there are 5 possible
2016-01-05 14:56:26 -08:00
// outcomes: the three from the signed total order (<,==,>) and the
2016-03-01 23:21:55 +00:00
// three from the unsigned total order. The == cases overlap.
2016-01-05 14:56:26 -08:00
// Note: there's a sixth "unordered" outcome for floating-point
// comparisons, but we don't use such a beast yet.
2016-03-01 23:21:55 +00:00
// These ops are for temporary use by rewrite rules. They
2016-01-05 14:56:26 -08:00
// cannot appear in the generated assembly.
{ name : "FlagEQ" } , // equal
{ name : "FlagLT_ULT" } , // signed < and unsigned <
{ name : "FlagLT_UGT" } , // signed < and unsigned >
2017-07-15 12:20:25 -06:00
{ name : "FlagGT_UGT" } , // signed > and unsigned >
{ name : "FlagGT_ULT" } , // signed > and unsigned <
2016-08-23 16:49:28 -07:00
// Atomic loads. These are just normal loads but return <value,memory> tuples
// so they can be properly ordered with other loads.
// load from arg0+auxint+aux. arg1=mem.
2017-03-09 14:46:43 -08:00
{ name : "MOVLatomicload" , argLength : 2 , reg : gpload , asm : "MOVL" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Read" } ,
{ name : "MOVQatomicload" , argLength : 2 , reg : gpload , asm : "MOVQ" , aux : "SymOff" , faultOnNilArg0 : true , symEffect : "Read" } ,
2016-08-25 16:02:57 -07:00
// Atomic stores and exchanges. Stores use XCHG to get the right memory ordering semantics.
2016-08-23 16:49:28 -07:00
// store arg0 to arg1+auxint+aux, arg2=mem.
2016-08-25 16:02:57 -07:00
// These ops return a tuple of <old contents of *(arg1+auxint+aux), memory>.
2016-08-23 16:49:28 -07:00
// Note: arg0 and arg1 are backwards compared to MOVLstore (to facilitate resultInArg0)!
2017-03-09 14:46:43 -08:00
{ name : "XCHGL" , argLength : 3 , reg : gpstorexchg , asm : "XCHGL" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , hasSideEffects : true , symEffect : "RdWr" } ,
{ name : "XCHGQ" , argLength : 3 , reg : gpstorexchg , asm : "XCHGQ" , aux : "SymOff" , resultInArg0 : true , faultOnNilArg1 : true , hasSideEffects : true , symEffect : "RdWr" } ,
2016-08-25 16:02:57 -07:00
// Atomic adds.
// *(arg1+auxint+aux) += arg0. arg2=mem.
// Returns a tuple of <old contents of *(arg1+auxint+aux), memory>.
// Note: arg0 and arg1 are backwards compared to MOVLstore (to facilitate resultInArg0)!
2017-03-09 14:46:43 -08:00
{ name : "XADDLlock" , argLength : 3 , reg : gpstorexchg , asm : "XADDL" , typ : "(UInt32,Mem)" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , hasSideEffects : true , symEffect : "RdWr" } ,
{ name : "XADDQlock" , argLength : 3 , reg : gpstorexchg , asm : "XADDQ" , typ : "(UInt64,Mem)" , aux : "SymOff" , resultInArg0 : true , clobberFlags : true , faultOnNilArg1 : true , hasSideEffects : true , symEffect : "RdWr" } ,
2017-05-15 09:00:55 -07:00
{ name : "AddTupleFirst32" , argLength : 2 } , // arg1=tuple <x,y>. Returns <x+arg0,y>.
{ name : "AddTupleFirst64" , argLength : 2 } , // arg1=tuple <x,y>. Returns <x+arg0,y>.
2016-08-25 16:02:57 -07:00
// Compare and swap.
// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
// if *(arg0+auxint+aux) == arg1 {
// *(arg0+auxint+aux) = arg2
// return (true, memory)
// } else {
// return (false, memory)
// }
// Note that these instructions also return the old value in AX, but we ignore it.
// TODO: have these return flags instead of bool. The current system generates:
// CMPXCHGQ ...
// SETEQ AX
// CMPB AX, $0
// JNE ...
// instead of just
// CMPXCHGQ ...
// JEQ ...
// but we can't do that because memory-using ops can't generate flags yet
// (flagalloc wants to move flag-generating instructions around).
2017-03-09 14:46:43 -08:00
{ name : "CMPXCHGLlock" , argLength : 4 , reg : cmpxchg , asm : "CMPXCHGL" , aux : "SymOff" , clobberFlags : true , faultOnNilArg0 : true , hasSideEffects : true , symEffect : "RdWr" } ,
{ name : "CMPXCHGQlock" , argLength : 4 , reg : cmpxchg , asm : "CMPXCHGQ" , aux : "SymOff" , clobberFlags : true , faultOnNilArg0 : true , hasSideEffects : true , symEffect : "RdWr" } ,
2016-08-25 16:02:57 -07:00
// Atomic memory updates.
2017-03-09 14:46:43 -08:00
{ name : "ANDBlock" , argLength : 3 , reg : gpstore , asm : "ANDB" , aux : "SymOff" , clobberFlags : true , faultOnNilArg0 : true , hasSideEffects : true , symEffect : "RdWr" } , // *(arg0+auxint+aux) &= arg1
{ name : "ORBlock" , argLength : 3 , reg : gpstore , asm : "ORB" , aux : "SymOff" , clobberFlags : true , faultOnNilArg0 : true , hasSideEffects : true , symEffect : "RdWr" } , // *(arg0+auxint+aux) |= arg1
2015-06-06 16:03:33 -07:00
}
var AMD64blocks = [ ] blockData {
{ name : "EQ" } ,
{ name : "NE" } ,
{ name : "LT" } ,
{ name : "LE" } ,
{ name : "GT" } ,
{ name : "GE" } ,
{ name : "ULT" } ,
{ name : "ULE" } ,
{ name : "UGT" } ,
{ name : "UGE" } ,
2015-08-18 14:39:26 -04:00
{ name : "EQF" } ,
{ name : "NEF" } ,
{ name : "ORD" } , // FP, ordered comparison (parity zero)
{ name : "NAN" } , // FP, unordered comparison (parity one)
2015-06-06 16:03:33 -07:00
}
2016-03-12 14:07:40 -08:00
archs = append ( archs , arch {
2016-05-19 12:33:30 -04:00
name : "AMD64" ,
pkg : "cmd/internal/obj/x86" ,
genfile : "../../amd64/ssa.go" ,
ops : AMD64ops ,
blocks : AMD64blocks ,
regnames : regNamesAMD64 ,
gpregmask : gp ,
fpregmask : fp ,
framepointerreg : int8 ( num [ "BP" ] ) ,
2016-10-06 15:06:45 -04:00
linkreg : - 1 , // not used
2016-03-12 14:07:40 -08:00
} )
2015-06-06 16:03:33 -07:00
}