...

Source file src/cmd/compile/internal/ssa/gen/S390XOps.go

     1	// Copyright 2016 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	// +build ignore
     6	
     7	package main
     8	
     9	import "strings"
    10	
    11	// Notes:
    12	//  - Integer types live in the low portion of registers. Upper portions are junk.
    13	//  - Boolean types use the low-order byte of a register. 0=false, 1=true.
    14	//    Upper bytes are junk.
    15	//  - When doing sub-register operations, we try to write the whole
    16	//    destination register to avoid a partial-register write.
    17	//  - Unused portions of AuxInt (or the Val portion of ValAndOff) are
    18	//    filled by sign-extending the used portion. Users of AuxInt which interpret
    19	//    AuxInt as unsigned (e.g. shifts) must be careful.
    20	//  - The SB 'register' is implemented using instruction-relative addressing. This
    21	//    places some limitations on when and how memory operands that are addressed
    22	//    relative to SB can be used:
    23	//
    24	//     1. Pseudo-instructions do not always map to a single machine instruction when
    25	//        using the SB 'register' to address data. This is because many machine
    26	//        instructions do not have relative long (RL suffix) equivalents. For example,
    27	//        ADDload, which is assembled as AG.
    28	//
    29	//     2. Loads and stores using relative addressing require the data be aligned
    30	//        according to its size (8-bytes for double words, 4-bytes for words
    31	//        and so on).
    32	//
    33	//    We can always work around these by inserting LARL instructions (load address
    34	//    relative long) in the assembler, but typically this results in worse code
    35	//    generation because the address can't be re-used. Inserting instructions in the
    36	//    assembler also means clobbering the temp register and it is a long-term goal
    37	//    to prevent the compiler doing this so that it can be allocated as a normal
    38	//    register.
    39	//
    40	// For more information about the z/Architecture, the instruction set and the
    41	// addressing modes it supports take a look at the z/Architecture Principles of
    42	// Operation: http://publibfp.boulder.ibm.com/epubs/pdf/dz9zr010.pdf
    43	//
    44	// Suffixes encode the bit width of pseudo-instructions.
    45	// D (double word)  = 64 bit (frequently omitted)
    46	// W (word)         = 32 bit
    47	// H (half word)    = 16 bit
    48	// B (byte)         = 8 bit
    49	// S (single prec.) = 32 bit (double precision is omitted)
    50	
    51	// copied from ../../s390x/reg.go
    52	var regNamesS390X = []string{
    53		"R0",
    54		"R1",
    55		"R2",
    56		"R3",
    57		"R4",
    58		"R5",
    59		"R6",
    60		"R7",
    61		"R8",
    62		"R9",
    63		"R10",
    64		"R11",
    65		"R12",
    66		"g", // R13
    67		"R14",
    68		"SP", // R15
    69		"F0",
    70		"F1",
    71		"F2",
    72		"F3",
    73		"F4",
    74		"F5",
    75		"F6",
    76		"F7",
    77		"F8",
    78		"F9",
    79		"F10",
    80		"F11",
    81		"F12",
    82		"F13",
    83		"F14",
    84		"F15",
    85	
    86		//pseudo-registers
    87		"SB",
    88	}
    89	
    90	func init() {
    91		// Make map from reg names to reg integers.
    92		if len(regNamesS390X) > 64 {
    93			panic("too many registers")
    94		}
    95		num := map[string]int{}
    96		for i, name := range regNamesS390X {
    97			num[name] = i
    98		}
    99		buildReg := func(s string) regMask {
   100			m := regMask(0)
   101			for _, r := range strings.Split(s, " ") {
   102				if n, ok := num[r]; ok {
   103					m |= regMask(1) << uint(n)
   104					continue
   105				}
   106				panic("register " + r + " not found")
   107			}
   108			return m
   109		}
   110	
   111		// Common individual register masks
   112		var (
   113			sp  = buildReg("SP")
   114			sb  = buildReg("SB")
   115			r0  = buildReg("R0")
   116			tmp = buildReg("R11") // R11 is used as a temporary in a small number of instructions.
   117	
   118			// R10 is reserved by the assembler.
   119			gp   = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14")
   120			gpg  = gp | buildReg("g")
   121			gpsp = gp | sp
   122	
   123			// R0 is considered to contain the value 0 in address calculations.
   124			ptr     = gp &^ r0
   125			ptrsp   = ptr | sp
   126			ptrspsb = ptrsp | sb
   127	
   128			fp         = buildReg("F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15")
   129			callerSave = gp | fp | buildReg("g") // runtime.setg (and anything calling it) may clobber g
   130			r1         = buildReg("R1")
   131			r2         = buildReg("R2")
   132			r3         = buildReg("R3")
   133		)
   134		// Common slices of register masks
   135		var (
   136			gponly = []regMask{gp}
   137			fponly = []regMask{fp}
   138		)
   139	
   140		// Common regInfo
   141		var (
   142			gp01    = regInfo{inputs: []regMask{}, outputs: gponly}
   143			gp11    = regInfo{inputs: []regMask{gp}, outputs: gponly}
   144			gp11sp  = regInfo{inputs: []regMask{gpsp}, outputs: gponly}
   145			gp21    = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   146			gp21sp  = regInfo{inputs: []regMask{gpsp, gp}, outputs: gponly}
   147			gp21tmp = regInfo{inputs: []regMask{gp &^ tmp, gp &^ tmp}, outputs: []regMask{gp &^ tmp}, clobbers: tmp}
   148	
   149			// R0 evaluates to 0 when used as the number of bits to shift
   150			// so we need to exclude it from that operand.
   151			sh21 = regInfo{inputs: []regMask{gp, ptr}, outputs: gponly}
   152	
   153			addr    = regInfo{inputs: []regMask{sp | sb}, outputs: gponly}
   154			addridx = regInfo{inputs: []regMask{sp | sb, ptrsp}, outputs: gponly}
   155	
   156			gp2flags       = regInfo{inputs: []regMask{gpsp, gpsp}}
   157			gp1flags       = regInfo{inputs: []regMask{gpsp}}
   158			gp2flags1      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   159			gp11flags      = regInfo{inputs: []regMask{gp}, outputs: gponly}
   160			gp21flags      = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   161			gp2flags1flags = regInfo{inputs: []regMask{gp, gp}, outputs: gponly}
   162	
   163			gpload       = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: gponly}
   164			gploadidx    = regInfo{inputs: []regMask{ptrspsb, ptrsp, 0}, outputs: gponly}
   165			gpopload     = regInfo{inputs: []regMask{gp, ptrsp, 0}, outputs: gponly}
   166			gpstore      = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}}
   167			gpstoreconst = regInfo{inputs: []regMask{ptrspsb, 0}}
   168			gpstoreidx   = regInfo{inputs: []regMask{ptrsp, ptrsp, gpsp, 0}}
   169			gpstorebr    = regInfo{inputs: []regMask{ptrsp, gpsp, 0}}
   170			gpstorelaa   = regInfo{inputs: []regMask{ptrspsb, gpsp, 0}, outputs: gponly}
   171	
   172			gpmvc = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}}
   173	
   174			fp01        = regInfo{inputs: []regMask{}, outputs: fponly}
   175			fp21        = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   176			fp31        = regInfo{inputs: []regMask{fp, fp, fp}, outputs: fponly}
   177			fp21clobber = regInfo{inputs: []regMask{fp, fp}, outputs: fponly}
   178			fpgp        = regInfo{inputs: fponly, outputs: gponly}
   179			gpfp        = regInfo{inputs: gponly, outputs: fponly}
   180			fp11        = regInfo{inputs: fponly, outputs: fponly}
   181			fp11clobber = regInfo{inputs: fponly, outputs: fponly}
   182			fp2flags    = regInfo{inputs: []regMask{fp, fp}}
   183	
   184			fpload    = regInfo{inputs: []regMask{ptrspsb, 0}, outputs: fponly}
   185			fploadidx = regInfo{inputs: []regMask{ptrsp, ptrsp, 0}, outputs: fponly}
   186	
   187			fpstore    = regInfo{inputs: []regMask{ptrspsb, fp, 0}}
   188			fpstoreidx = regInfo{inputs: []regMask{ptrsp, ptrsp, fp, 0}}
   189	
   190			sync = regInfo{inputs: []regMask{0}}
   191	
   192			// LoweredAtomicCas may overwrite arg1, so force it to R0 for now.
   193			cas = regInfo{inputs: []regMask{ptrsp, r0, gpsp, 0}, outputs: []regMask{gp, 0}, clobbers: r0}
   194	
   195			// LoweredAtomicExchange overwrites the output before executing
   196			// CS{,G}, so the output register must not be the same as the
   197			// input register. For now we just force the output register to
   198			// R0.
   199			exchange = regInfo{inputs: []regMask{ptrsp, gpsp &^ r0, 0}, outputs: []regMask{r0, 0}}
   200		)
   201	
   202		var S390Xops = []opData{
   203			// fp ops
   204			{name: "FADDS", argLength: 2, reg: fp21clobber, asm: "FADDS", commutative: true, resultInArg0: true, clobberFlags: true}, // fp32 arg0 + arg1
   205			{name: "FADD", argLength: 2, reg: fp21clobber, asm: "FADD", commutative: true, resultInArg0: true, clobberFlags: true},   // fp64 arg0 + arg1
   206			{name: "FSUBS", argLength: 2, reg: fp21clobber, asm: "FSUBS", resultInArg0: true, clobberFlags: true},                    // fp32 arg0 - arg1
   207			{name: "FSUB", argLength: 2, reg: fp21clobber, asm: "FSUB", resultInArg0: true, clobberFlags: true},                      // fp64 arg0 - arg1
   208			{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, resultInArg0: true},                            // fp32 arg0 * arg1
   209			{name: "FMUL", argLength: 2, reg: fp21, asm: "FMUL", commutative: true, resultInArg0: true},                              // fp64 arg0 * arg1
   210			{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", resultInArg0: true},                                               // fp32 arg0 / arg1
   211			{name: "FDIV", argLength: 2, reg: fp21, asm: "FDIV", resultInArg0: true},                                                 // fp64 arg0 / arg1
   212			{name: "FNEGS", argLength: 1, reg: fp11clobber, asm: "FNEGS", clobberFlags: true},                                        // fp32 -arg0
   213			{name: "FNEG", argLength: 1, reg: fp11clobber, asm: "FNEG", clobberFlags: true},                                          // fp64 -arg0
   214			{name: "FMADDS", argLength: 3, reg: fp31, asm: "FMADDS", resultInArg0: true},                                             // fp32 arg1 * arg2 + arg0
   215			{name: "FMADD", argLength: 3, reg: fp31, asm: "FMADD", resultInArg0: true},                                               // fp64 arg1 * arg2 + arg0
   216			{name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS", resultInArg0: true},                                             // fp32 arg1 * arg2 - arg0
   217			{name: "FMSUB", argLength: 3, reg: fp31, asm: "FMSUB", resultInArg0: true},                                               // fp64 arg1 * arg2 - arg0
   218			{name: "LPDFR", argLength: 1, reg: fp11, asm: "LPDFR"},                                                                   // fp64/fp32 set sign bit
   219			{name: "LNDFR", argLength: 1, reg: fp11, asm: "LNDFR"},                                                                   // fp64/fp32 clear sign bit
   220			{name: "CPSDR", argLength: 2, reg: fp21, asm: "CPSDR"},                                                                   // fp64/fp32 copy arg1 sign bit to arg0
   221	
   222			// Round to integer, float64 only.
   223			//
   224			// aux | rounding mode
   225			// ----+-----------------------------------
   226			//   1 | round to nearest, ties away from 0
   227			//   4 | round to nearest, ties to even
   228			//   5 | round toward 0
   229			//   6 | round toward +∞
   230			//   7 | round toward -∞
   231			{name: "FIDBR", argLength: 1, reg: fp11, asm: "FIDBR", aux: "Int8"},
   232	
   233			{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // fp32 load
   234			{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"}, // fp64 load
   235			{name: "FMOVSconst", reg: fp01, asm: "FMOVS", aux: "Float32", rematerializeable: true},                               // fp32 constant
   236			{name: "FMOVDconst", reg: fp01, asm: "FMOVD", aux: "Float64", rematerializeable: true},                               // fp64 constant
   237			{name: "FMOVSloadidx", argLength: 3, reg: fploadidx, asm: "FMOVS", aux: "SymOff", symEffect: "Read"},                 // fp32 load indexed by i
   238			{name: "FMOVDloadidx", argLength: 3, reg: fploadidx, asm: "FMOVD", aux: "SymOff", symEffect: "Read"},                 // fp64 load indexed by i
   239	
   240			{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff", faultOnNilArg0: true, symEffect: "Write"}, // fp32 store
   241			{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Write"}, // fp64 store
   242			{name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", aux: "SymOff", symEffect: "Write"},                 // fp32 indexed by i store
   243			{name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", aux: "SymOff", symEffect: "Write"},                 // fp64 indexed by i store
   244	
   245			// binary ops
   246			{name: "ADD", argLength: 2, reg: gp21sp, asm: "ADD", commutative: true, clobberFlags: true},                                                                  // arg0 + arg1
   247			{name: "ADDW", argLength: 2, reg: gp21sp, asm: "ADDW", commutative: true, clobberFlags: true},                                                                // arg0 + arg1
   248			{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADD", aux: "Int32", typ: "UInt64", clobberFlags: true},                                                   // arg0 + auxint
   249			{name: "ADDWconst", argLength: 1, reg: gp11sp, asm: "ADDW", aux: "Int32", clobberFlags: true},                                                                // arg0 + auxint
   250			{name: "ADDload", argLength: 3, reg: gpopload, asm: "ADD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 + *arg1. arg2=mem
   251			{name: "ADDWload", argLength: 3, reg: gpopload, asm: "ADDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 + *arg1. arg2=mem
   252	
   253			{name: "SUB", argLength: 2, reg: gp21, asm: "SUB", clobberFlags: true},                                                                                       // arg0 - arg1
   254			{name: "SUBW", argLength: 2, reg: gp21, asm: "SUBW", clobberFlags: true},                                                                                     // arg0 - arg1
   255			{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUB", aux: "Int32", resultInArg0: true, clobberFlags: true},                                                // arg0 - auxint
   256			{name: "SUBWconst", argLength: 1, reg: gp11, asm: "SUBW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 - auxint
   257			{name: "SUBload", argLength: 3, reg: gpopload, asm: "SUB", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 - *arg1. arg2=mem
   258			{name: "SUBWload", argLength: 3, reg: gpopload, asm: "SUBW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 - *arg1. arg2=mem
   259	
   260			{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},                                // arg0 * arg1
   261			{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true, resultInArg0: true, clobberFlags: true},                                // arg0 * arg1
   262			{name: "MULLDconst", argLength: 1, reg: gp11, asm: "MULLD", aux: "Int32", typ: "Int64", resultInArg0: true, clobberFlags: true},                                // arg0 * auxint
   263			{name: "MULLWconst", argLength: 1, reg: gp11, asm: "MULLW", aux: "Int32", typ: "Int32", resultInArg0: true, clobberFlags: true},                                // arg0 * auxint
   264			{name: "MULLDload", argLength: 3, reg: gpopload, asm: "MULLD", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
   265			{name: "MULLWload", argLength: 3, reg: gpopload, asm: "MULLW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 * *arg1. arg2=mem
   266	
   267			{name: "MULHD", argLength: 2, reg: gp21tmp, asm: "MULHD", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true},   // (arg0 * arg1) >> width
   268			{name: "MULHDU", argLength: 2, reg: gp21tmp, asm: "MULHDU", typ: "Int64", commutative: true, resultInArg0: true, clobberFlags: true}, // (arg0 * arg1) >> width
   269	
   270			{name: "DIVD", argLength: 2, reg: gp21tmp, asm: "DIVD", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   271			{name: "DIVW", argLength: 2, reg: gp21tmp, asm: "DIVW", resultInArg0: true, clobberFlags: true},   // arg0 / arg1
   272			{name: "DIVDU", argLength: 2, reg: gp21tmp, asm: "DIVDU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   273			{name: "DIVWU", argLength: 2, reg: gp21tmp, asm: "DIVWU", resultInArg0: true, clobberFlags: true}, // arg0 / arg1
   274	
   275			{name: "MODD", argLength: 2, reg: gp21tmp, asm: "MODD", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   276			{name: "MODW", argLength: 2, reg: gp21tmp, asm: "MODW", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   277	
   278			{name: "MODDU", argLength: 2, reg: gp21tmp, asm: "MODDU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   279			{name: "MODWU", argLength: 2, reg: gp21tmp, asm: "MODWU", resultInArg0: true, clobberFlags: true}, // arg0 % arg1
   280	
   281			{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true, clobberFlags: true},                                                                    // arg0 & arg1
   282			{name: "ANDW", argLength: 2, reg: gp21, asm: "ANDW", commutative: true, clobberFlags: true},                                                                  // arg0 & arg1
   283			{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 & auxint
   284			{name: "ANDWconst", argLength: 1, reg: gp11, asm: "ANDW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 & auxint
   285			{name: "ANDload", argLength: 3, reg: gpopload, asm: "AND", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 & *arg1. arg2=mem
   286			{name: "ANDWload", argLength: 3, reg: gpopload, asm: "ANDW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 & *arg1. arg2=mem
   287	
   288			{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true, clobberFlags: true},                                                                    // arg0 | arg1
   289			{name: "ORW", argLength: 2, reg: gp21, asm: "ORW", commutative: true, clobberFlags: true},                                                                  // arg0 | arg1
   290			{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 | auxint
   291			{name: "ORWconst", argLength: 1, reg: gp11, asm: "ORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 | auxint
   292			{name: "ORload", argLength: 3, reg: gpopload, asm: "OR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 | *arg1. arg2=mem
   293			{name: "ORWload", argLength: 3, reg: gpopload, asm: "ORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 | *arg1. arg2=mem
   294	
   295			{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, clobberFlags: true},                                                                    // arg0 ^ arg1
   296			{name: "XORW", argLength: 2, reg: gp21, asm: "XORW", commutative: true, clobberFlags: true},                                                                  // arg0 ^ arg1
   297			{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64", resultInArg0: true, clobberFlags: true},                                                // arg0 ^ auxint
   298			{name: "XORWconst", argLength: 1, reg: gp11, asm: "XORW", aux: "Int32", resultInArg0: true, clobberFlags: true},                                              // arg0 ^ auxint
   299			{name: "XORload", argLength: 3, reg: gpopload, asm: "XOR", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"},   // arg0 ^ *arg1. arg2=mem
   300			{name: "XORWload", argLength: 3, reg: gpopload, asm: "XORW", aux: "SymOff", resultInArg0: true, clobberFlags: true, faultOnNilArg1: true, symEffect: "Read"}, // arg0 ^ *arg1. arg2=mem
   301	
   302			// Arithmetic ops with carry/borrow chain.
   303			//
   304			// A carry is represented by a condition code of 2 or 3 (GT or OV).
   305			// A borrow is represented by a condition code of 0 or 1 (EQ or LT).
   306			{name: "ADDC", argLength: 2, reg: gp21flags, asm: "ADDC", typ: "(UInt64,Flags)", commutative: true},                          // (arg0 + arg1, carry out)
   307			{name: "ADDCconst", argLength: 1, reg: gp11flags, asm: "ADDC", typ: "(UInt64,Flags)", aux: "Int16"},                          // (arg0 + auxint, carry out)
   308			{name: "ADDE", argLength: 3, reg: gp2flags1flags, asm: "ADDE", typ: "(UInt64,Flags)", commutative: true, resultInArg0: true}, // (arg0 + arg1 + arg2 (carry in), carry out)
   309			{name: "SUBC", argLength: 2, reg: gp21flags, asm: "SUBC", typ: "(UInt64,Flags)"},                                             // (arg0 - arg1, borrow out)
   310			{name: "SUBE", argLength: 3, reg: gp2flags1flags, asm: "SUBE", typ: "(UInt64,Flags)", resultInArg0: true},                    // (arg0 - arg1 - arg2 (borrow in), borrow out)
   311	
   312			// Comparisons.
   313			{name: "CMP", argLength: 2, reg: gp2flags, asm: "CMP", typ: "Flags"},   // arg0 compare to arg1
   314			{name: "CMPW", argLength: 2, reg: gp2flags, asm: "CMPW", typ: "Flags"}, // arg0 compare to arg1
   315	
   316			{name: "CMPU", argLength: 2, reg: gp2flags, asm: "CMPU", typ: "Flags"},   // arg0 compare to arg1
   317			{name: "CMPWU", argLength: 2, reg: gp2flags, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
   318	
   319			{name: "CMPconst", argLength: 1, reg: gp1flags, asm: "CMP", typ: "Flags", aux: "Int32"},     // arg0 compare to auxint
   320			{name: "CMPWconst", argLength: 1, reg: gp1flags, asm: "CMPW", typ: "Flags", aux: "Int32"},   // arg0 compare to auxint
   321			{name: "CMPUconst", argLength: 1, reg: gp1flags, asm: "CMPU", typ: "Flags", aux: "Int32"},   // arg0 compare to auxint
   322			{name: "CMPWUconst", argLength: 1, reg: gp1flags, asm: "CMPWU", typ: "Flags", aux: "Int32"}, // arg0 compare to auxint
   323	
   324			{name: "FCMPS", argLength: 2, reg: fp2flags, asm: "CEBR", typ: "Flags"}, // arg0 compare to arg1, f32
   325			{name: "FCMP", argLength: 2, reg: fp2flags, asm: "FCMPU", typ: "Flags"}, // arg0 compare to arg1, f64
   326	
   327			{name: "SLD", argLength: 2, reg: sh21, asm: "SLD"},                   // arg0 << arg1, shift amount is mod 64
   328			{name: "SLW", argLength: 2, reg: sh21, asm: "SLW"},                   // arg0 << arg1, shift amount is mod 32
   329			{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int8"}, // arg0 << auxint, shift amount 0-63
   330			{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int8"}, // arg0 << auxint, shift amount 0-31
   331	
   332			{name: "SRD", argLength: 2, reg: sh21, asm: "SRD"},                   // unsigned arg0 >> arg1, shift amount is mod 64
   333			{name: "SRW", argLength: 2, reg: sh21, asm: "SRW"},                   // unsigned uint32(arg0) >> arg1, shift amount is mod 32
   334			{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int8"}, // unsigned arg0 >> auxint, shift amount 0-63
   335			{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int8"}, // unsigned uint32(arg0) >> auxint, shift amount 0-31
   336	
   337			// Arithmetic shifts clobber flags.
   338			{name: "SRAD", argLength: 2, reg: sh21, asm: "SRAD", clobberFlags: true},                   // signed arg0 >> arg1, shift amount is mod 64
   339			{name: "SRAW", argLength: 2, reg: sh21, asm: "SRAW", clobberFlags: true},                   // signed int32(arg0) >> arg1, shift amount is mod 32
   340			{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int8", clobberFlags: true}, // signed arg0 >> auxint, shift amount 0-63
   341			{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int8", clobberFlags: true}, // signed int32(arg0) >> auxint, shift amount 0-31
   342	
   343			{name: "RLLG", argLength: 2, reg: sh21, asm: "RLLG"},                   // arg0 rotate left arg1, rotate amount 0-63
   344			{name: "RLL", argLength: 2, reg: sh21, asm: "RLL"},                     // arg0 rotate left arg1, rotate amount 0-31
   345			{name: "RLLGconst", argLength: 1, reg: gp11, asm: "RLLG", aux: "Int8"}, // arg0 rotate left auxint, rotate amount 0-63
   346			{name: "RLLconst", argLength: 1, reg: gp11, asm: "RLL", aux: "Int8"},   // arg0 rotate left auxint, rotate amount 0-31
   347	
   348			// unary ops
   349			{name: "NEG", argLength: 1, reg: gp11, asm: "NEG", clobberFlags: true},   // -arg0
   350			{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW", clobberFlags: true}, // -arg0
   351	
   352			{name: "NOT", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true},  // ^arg0
   353			{name: "NOTW", argLength: 1, reg: gp11, resultInArg0: true, clobberFlags: true}, // ^arg0
   354	
   355			{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"}, // sqrt(arg0)
   356	
   357			{name: "MOVDEQ", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDEQ"}, // extract == condition from arg0
   358			{name: "MOVDNE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDNE"}, // extract != condition from arg0
   359			{name: "MOVDLT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLT"}, // extract signed < condition from arg0
   360			{name: "MOVDLE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDLE"}, // extract signed <= condition from arg0
   361			{name: "MOVDGT", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract signed > condition from arg0
   362			{name: "MOVDGE", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract signed >= condition from arg0
   363	
   364			// Different rules for floating point conditions because
   365			// any comparison involving a NaN is always false and thus
   366			// the patterns for inverting conditions cannot be used.
   367			{name: "MOVDGTnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGT"}, // extract floating > condition from arg0
   368			{name: "MOVDGEnoinv", argLength: 3, reg: gp2flags1, resultInArg0: true, asm: "MOVDGE"}, // extract floating >= condition from arg0
   369	
   370			{name: "MOVBreg", argLength: 1, reg: gp11sp, asm: "MOVB", typ: "Int64"},    // sign extend arg0 from int8 to int64
   371			{name: "MOVBZreg", argLength: 1, reg: gp11sp, asm: "MOVBZ", typ: "UInt64"}, // zero extend arg0 from int8 to int64
   372			{name: "MOVHreg", argLength: 1, reg: gp11sp, asm: "MOVH", typ: "Int64"},    // sign extend arg0 from int16 to int64
   373			{name: "MOVHZreg", argLength: 1, reg: gp11sp, asm: "MOVHZ", typ: "UInt64"}, // zero extend arg0 from int16 to int64
   374			{name: "MOVWreg", argLength: 1, reg: gp11sp, asm: "MOVW", typ: "Int64"},    // sign extend arg0 from int32 to int64
   375			{name: "MOVWZreg", argLength: 1, reg: gp11sp, asm: "MOVWZ", typ: "UInt64"}, // zero extend arg0 from int32 to int64
   376			{name: "MOVDreg", argLength: 1, reg: gp11sp, asm: "MOVD"},                  // move from arg0
   377	
   378			{name: "MOVDnop", argLength: 1, reg: gp11, resultInArg0: true}, // nop, return arg0 in same register
   379	
   380			{name: "MOVDconst", reg: gp01, asm: "MOVD", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
   381	
   382			{name: "LDGR", argLength: 1, reg: gpfp, asm: "LDGR"},     // move int64 to float64 (no conversion)
   383			{name: "LGDR", argLength: 1, reg: fpgp, asm: "LGDR"},     // move float64 to int64 (no conversion)
   384			{name: "CFDBRA", argLength: 1, reg: fpgp, asm: "CFDBRA"}, // convert float64 to int32
   385			{name: "CGDBRA", argLength: 1, reg: fpgp, asm: "CGDBRA"}, // convert float64 to int64
   386			{name: "CFEBRA", argLength: 1, reg: fpgp, asm: "CFEBRA"}, // convert float32 to int32
   387			{name: "CGEBRA", argLength: 1, reg: fpgp, asm: "CGEBRA"}, // convert float32 to int64
   388			{name: "CEFBRA", argLength: 1, reg: gpfp, asm: "CEFBRA"}, // convert int32 to float32
   389			{name: "CDFBRA", argLength: 1, reg: gpfp, asm: "CDFBRA"}, // convert int32 to float64
   390			{name: "CEGBRA", argLength: 1, reg: gpfp, asm: "CEGBRA"}, // convert int64 to float32
   391			{name: "CDGBRA", argLength: 1, reg: gpfp, asm: "CDGBRA"}, // convert int64 to float64
   392			{name: "LEDBR", argLength: 1, reg: fp11, asm: "LEDBR"},   // convert float64 to float32
   393			{name: "LDEBR", argLength: 1, reg: fp11, asm: "LDEBR"},   // convert float32 to float64
   394	
   395			{name: "MOVDaddr", argLength: 1, reg: addr, aux: "SymOff", rematerializeable: true, symEffect: "Read"}, // arg0 + auxint + offset encoded in aux
   396			{name: "MOVDaddridx", argLength: 2, reg: addridx, aux: "SymOff", symEffect: "Read"},                    // arg0 + arg1 + auxint + aux
   397	
   398			// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   399			{name: "MOVBZload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // load byte from arg0+auxint+aux. arg1=mem.  Zero extend.
   400			{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   401			{name: "MOVHZload", argLength: 2, reg: gpload, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
   402			{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   403			{name: "MOVWZload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem.  Zero extend.
   404			{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},                  // ditto, sign extend to int64
   405			{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"},   // load 8 bytes from arg0+auxint+aux. arg1=mem
   406	
   407			{name: "MOVWBR", argLength: 1, reg: gp11, asm: "MOVWBR"}, // arg0 swap bytes
   408			{name: "MOVDBR", argLength: 1, reg: gp11, asm: "MOVDBR"}, // arg0 swap bytes
   409	
   410			{name: "MOVHBRload", argLength: 2, reg: gpload, asm: "MOVHBR", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load 2 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   411			{name: "MOVWBRload", argLength: 2, reg: gpload, asm: "MOVWBR", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load 4 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   412			{name: "MOVDBRload", argLength: 2, reg: gpload, asm: "MOVDBR", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load 8 bytes from arg0+auxint+aux. arg1=mem. Reverse bytes.
   413	
   414			{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
   415			{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},       // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem
   416			{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},       // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
   417			{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},       // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
   418			{name: "MOVHBRstore", argLength: 3, reg: gpstorebr, asm: "MOVHBR", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   419			{name: "MOVWBRstore", argLength: 3, reg: gpstorebr, asm: "MOVWBR", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   420			{name: "MOVDBRstore", argLength: 3, reg: gpstorebr, asm: "MOVDBR", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem. Reverse bytes.
   421	
   422			{name: "MVC", argLength: 3, reg: gpmvc, asm: "MVC", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, faultOnNilArg1: true, symEffect: "None"}, // arg0=destptr, arg1=srcptr, arg2=mem, auxint=size,off
   423	
   424			// indexed loads/stores
   425			{name: "MOVBZloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", symEffect: "Read"},   // load a byte from arg0+arg1+auxint+aux. arg2=mem. Zero extend.
   426			{name: "MOVBloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVB", aux: "SymOff", typ: "Int8", symEffect: "Read"},      // load a byte from arg0+arg1+auxint+aux. arg2=mem. Sign extend.
   427			{name: "MOVHZloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", symEffect: "Read"},  // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Zero extend.
   428			{name: "MOVHloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVH", aux: "SymOff", typ: "Int16", symEffect: "Read"},     // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Sign extend.
   429			{name: "MOVWZloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", symEffect: "Read"},  // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Zero extend.
   430			{name: "MOVWloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVW", aux: "SymOff", typ: "Int32", symEffect: "Read"},     // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Sign extend.
   431			{name: "MOVDloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVD", aux: "SymOff", typ: "UInt64", symEffect: "Read"},    // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem
   432			{name: "MOVHBRloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVHBR", aux: "SymOff", typ: "Int16", symEffect: "Read"}, // load 2 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   433			{name: "MOVWBRloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVWBR", aux: "SymOff", typ: "Int32", symEffect: "Read"}, // load 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   434			{name: "MOVDBRloadidx", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVDBR", aux: "SymOff", typ: "Int64", symEffect: "Read"}, // load 8 bytes from arg0+arg1+auxint+aux. arg2=mem. Reverse bytes.
   435			{name: "MOVBstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVB", aux: "SymOff", symEffect: "Write"},                // store byte in arg2 to arg0+arg1+auxint+aux. arg3=mem
   436			{name: "MOVHstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVH", aux: "SymOff", symEffect: "Write"},                // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   437			{name: "MOVWstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVW", aux: "SymOff", symEffect: "Write"},                // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   438			{name: "MOVDstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVD", aux: "SymOff", symEffect: "Write"},                // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
   439			{name: "MOVHBRstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVHBR", aux: "SymOff", symEffect: "Write"},            // store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   440			{name: "MOVWBRstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVWBR", aux: "SymOff", symEffect: "Write"},            // store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   441			{name: "MOVDBRstoreidx", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVDBR", aux: "SymOff", symEffect: "Write"},            // store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem. Reverse bytes.
   442	
   443			// For storeconst ops, the AuxInt field encodes both
   444			// the value to store and an address offset of the store.
   445			// Cast AuxInt to a ValAndOff to extract Val and Off fields.
   446			{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
   447			{name: "MOVHstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVH", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 2 bytes of ...
   448			{name: "MOVWstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVW", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store low 4 bytes of ...
   449			{name: "MOVDstoreconst", argLength: 2, reg: gpstoreconst, asm: "MOVD", aux: "SymValAndOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of ...
   450	
   451			{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
   452	
   453			{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
   454			{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   455			{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                         // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   456	
   457			// (InvertFlags (CMP a b)) == (CMP b a)
   458			// InvertFlags is a pseudo-op which can't appear in assembly output.
   459			{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   460	
   461			// Pseudo-ops
   462			{name: "LoweredGetG", argLength: 1, reg: gp01}, // arg0=mem
   463			// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   464			// and sorts it to the very beginning of the block to prevent other
   465			// use of R12 (the closure pointer)
   466			{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R12")}}, zeroWidth: true},
   467			// arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   468			// LoweredGetCallerSP returns the SP of the caller of the current function.
   469			{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},
   470			// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   471			// I.e., if f calls g "calls" getcallerpc,
   472			// the result should be the PC within f that g will return to.
   473			// See runtime/stubs.go for a more detailed discussion.
   474			{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   475			{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{ptrsp}}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
   476			// Round ops to block fused-multiply-add extraction.
   477			{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
   478			{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true, zeroWidth: true},
   479	
   480			// LoweredWB invokes runtime.gcWriteBarrier. arg0=destptr, arg1=srcptr, arg2=mem, aux=runtime.gcWriteBarrier
   481			// It saves all GP registers if necessary,
   482			// but clobbers R14 (LR) because it's a call.
   483			{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R2"), buildReg("R3")}, clobbers: (callerSave &^ gpg) | buildReg("R14")}, clobberFlags: true, aux: "Sym", symEffect: "None"},
   484	
   485			// There are three of these functions so that they can have three different register inputs.
   486			// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the
   487			// default registers to match so we don't need to copy registers around unnecessarily.
   488			{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem"}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   489			{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem"}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   490			{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem"}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go).
   491	
   492			// Constant conditon code values. The condition code can be 0, 1, 2 or 3.
   493			{name: "FlagEQ"}, // CC=0 (equal)
   494			{name: "FlagLT"}, // CC=1 (less than)
   495			{name: "FlagGT"}, // CC=2 (greater than)
   496			{name: "FlagOV"}, // CC=3 (overflow)
   497	
   498			// Fast-BCR-serialization to ensure store-load ordering.
   499			{name: "SYNC", argLength: 1, reg: sync, asm: "SYNC", typ: "Mem"},
   500	
   501			// Atomic loads. These are just normal loads but return <value,memory> tuples
   502			// so they can be properly ordered with other loads.
   503			// load from arg0+auxint+aux.  arg1=mem.
   504			{name: "MOVBZatomicload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   505			{name: "MOVWZatomicload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   506			{name: "MOVDatomicload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", faultOnNilArg0: true, symEffect: "Read"},
   507	
   508			// Atomic stores. These are just normal stores.
   509			// store arg1 to arg0+auxint+aux. arg2=mem.
   510			{name: "MOVWatomicstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   511			{name: "MOVDatomicstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "Write"},
   512	
   513			// Atomic adds.
   514			// *(arg0+auxint+aux) += arg1.  arg2=mem.
   515			// Returns a tuple of <old contents of *(arg0+auxint+aux), memory>.
   516			{name: "LAA", argLength: 3, reg: gpstorelaa, asm: "LAA", typ: "(UInt32,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   517			{name: "LAAG", argLength: 3, reg: gpstorelaa, asm: "LAAG", typ: "(UInt64,Mem)", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   518			{name: "AddTupleFirst32", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   519			{name: "AddTupleFirst64", argLength: 2}, // arg1=tuple <x,y>.  Returns <x+arg0,y>.
   520	
   521			// Compare and swap.
   522			// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   523			// if *(arg0+auxint+aux) == arg1 {
   524			//   *(arg0+auxint+aux) = arg2
   525			//   return (true, memory)
   526			// } else {
   527			//   return (false, memory)
   528			// }
   529			// Note that these instructions also return the old value in arg1, but we ignore it.
   530			// TODO: have these return flags instead of bool.  The current system generates:
   531			//    CS ...
   532			//    MOVD  $0, ret
   533			//    BNE   2(PC)
   534			//    MOVD  $1, ret
   535			//    CMPW  ret, $0
   536			//    BNE ...
   537			// instead of just
   538			//    CS ...
   539			//    BEQ ...
   540			// but we can't do that because memory-using ops can't generate flags yet
   541			// (flagalloc wants to move flag-generating instructions around).
   542			{name: "LoweredAtomicCas32", argLength: 4, reg: cas, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   543			{name: "LoweredAtomicCas64", argLength: 4, reg: cas, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   544	
   545			// Lowered atomic swaps, emulated using compare-and-swap.
   546			// store arg1 to arg0+auxint+aux, arg2=mem.
   547			{name: "LoweredAtomicExchange32", argLength: 3, reg: exchange, asm: "CS", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   548			{name: "LoweredAtomicExchange64", argLength: 3, reg: exchange, asm: "CSG", aux: "SymOff", clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true, symEffect: "RdWr"},
   549	
   550			// find leftmost one
   551			{
   552				name:         "FLOGR",
   553				argLength:    1,
   554				reg:          regInfo{inputs: gponly, outputs: []regMask{buildReg("R0")}, clobbers: buildReg("R1")},
   555				asm:          "FLOGR",
   556				typ:          "UInt64",
   557				clobberFlags: true,
   558			},
   559	
   560			// population count
   561			//
   562			// Counts the number of ones in each byte of arg0
   563			// and places the result into the corresponding byte
   564			// of the result.
   565			{
   566				name:         "POPCNT",
   567				argLength:    1,
   568				reg:          gp11,
   569				asm:          "POPCNT",
   570				typ:          "UInt64",
   571				clobberFlags: true,
   572			},
   573	
   574			// pseudo operations to sum the output of the POPCNT instruction
   575			{name: "SumBytes2", argLength: 1, typ: "UInt8"}, // sum the rightmost 2 bytes in arg0 ignoring overflow
   576			{name: "SumBytes4", argLength: 1, typ: "UInt8"}, // sum the rightmost 4 bytes in arg0 ignoring overflow
   577			{name: "SumBytes8", argLength: 1, typ: "UInt8"}, // sum all the bytes in arg0 ignoring overflow
   578	
   579			// store multiple
   580			{
   581				name:           "STMG2",
   582				argLength:      4,
   583				reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   584				aux:            "SymOff",
   585				typ:            "Mem",
   586				asm:            "STMG",
   587				faultOnNilArg0: true,
   588				symEffect:      "Write",
   589			},
   590			{
   591				name:           "STMG3",
   592				argLength:      5,
   593				reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   594				aux:            "SymOff",
   595				typ:            "Mem",
   596				asm:            "STMG",
   597				faultOnNilArg0: true,
   598				symEffect:      "Write",
   599			},
   600			{
   601				name:      "STMG4",
   602				argLength: 6,
   603				reg: regInfo{inputs: []regMask{
   604					ptrsp,
   605					buildReg("R1"),
   606					buildReg("R2"),
   607					buildReg("R3"),
   608					buildReg("R4"),
   609					0,
   610				}},
   611				aux:            "SymOff",
   612				typ:            "Mem",
   613				asm:            "STMG",
   614				faultOnNilArg0: true,
   615				symEffect:      "Write",
   616			},
   617			{
   618				name:           "STM2",
   619				argLength:      4,
   620				reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), 0}},
   621				aux:            "SymOff",
   622				typ:            "Mem",
   623				asm:            "STMY",
   624				faultOnNilArg0: true,
   625				symEffect:      "Write",
   626			},
   627			{
   628				name:           "STM3",
   629				argLength:      5,
   630				reg:            regInfo{inputs: []regMask{ptrsp, buildReg("R1"), buildReg("R2"), buildReg("R3"), 0}},
   631				aux:            "SymOff",
   632				typ:            "Mem",
   633				asm:            "STMY",
   634				faultOnNilArg0: true,
   635				symEffect:      "Write",
   636			},
   637			{
   638				name:      "STM4",
   639				argLength: 6,
   640				reg: regInfo{inputs: []regMask{
   641					ptrsp,
   642					buildReg("R1"),
   643					buildReg("R2"),
   644					buildReg("R3"),
   645					buildReg("R4"),
   646					0,
   647				}},
   648				aux:            "SymOff",
   649				typ:            "Mem",
   650				asm:            "STMY",
   651				faultOnNilArg0: true,
   652				symEffect:      "Write",
   653			},
   654	
   655			// large move
   656			// auxint = remaining bytes after loop (rem)
   657			// arg0 = address of dst memory (in R1, changed as a side effect)
   658			// arg1 = address of src memory (in R2, changed as a side effect)
   659			// arg2 = pointer to last address to move in loop + 256
   660			// arg3 = mem
   661			// returns mem
   662			//
   663			// mvc: MVC  $256, 0(R2), 0(R1)
   664			//      MOVD $256(R1), R1
   665			//      MOVD $256(R2), R2
   666			//      CMP  R2, Rarg2
   667			//      BNE  mvc
   668			//	MVC  $rem, 0(R2), 0(R1) // if rem > 0
   669			{
   670				name:      "LoweredMove",
   671				aux:       "Int64",
   672				argLength: 4,
   673				reg: regInfo{
   674					inputs:   []regMask{buildReg("R1"), buildReg("R2"), gpsp},
   675					clobbers: buildReg("R1 R2"),
   676				},
   677				clobberFlags:   true,
   678				typ:            "Mem",
   679				faultOnNilArg0: true,
   680				faultOnNilArg1: true,
   681			},
   682	
   683			// large clear
   684			// auxint = remaining bytes after loop (rem)
   685			// arg0 = address of dst memory (in R1, changed as a side effect)
   686			// arg1 = pointer to last address to zero in loop + 256
   687			// arg2 = mem
   688			// returns mem
   689			//
   690			// clear: CLEAR $256, 0(R1)
   691			//        MOVD  $256(R1), R1
   692			//        CMP   R1, Rarg2
   693			//        BNE   clear
   694			//	  CLEAR $rem, 0(R1) // if rem > 0
   695			{
   696				name:      "LoweredZero",
   697				aux:       "Int64",
   698				argLength: 3,
   699				reg: regInfo{
   700					inputs:   []regMask{buildReg("R1"), gpsp},
   701					clobbers: buildReg("R1"),
   702				},
   703				clobberFlags:   true,
   704				typ:            "Mem",
   705				faultOnNilArg0: true,
   706			},
   707		}
   708	
   709		var S390Xblocks = []blockData{
   710			{name: "EQ"},
   711			{name: "NE"},
   712			{name: "LT"},
   713			{name: "LE"},
   714			{name: "GT"},
   715			{name: "GE"},
   716			{name: "GTF"}, // FP comparison
   717			{name: "GEF"}, // FP comparison
   718		}
   719	
   720		archs = append(archs, arch{
   721			name:            "S390X",
   722			pkg:             "cmd/internal/obj/s390x",
   723			genfile:         "../../s390x/ssa.go",
   724			ops:             S390Xops,
   725			blocks:          S390Xblocks,
   726			regnames:        regNamesS390X,
   727			gpregmask:       gp,
   728			fpregmask:       fp,
   729			framepointerreg: -1, // not used
   730			linkreg:         int8(num["R14"]),
   731		})
   732	}
   733	

View as plain text