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

View as plain text