github.com/Filosottile/go@v0.0.0-20170906193555-dbed9972d994/src/cmd/compile/internal/ssa/gen/PPC64Ops.go (about)

     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  //  - Less-than-64-bit integer types live in the low portion of registers.
    13  //    For now, the upper portion is junk; sign/zero-extension might be optimized in the future, but not yet.
    14  //  - Boolean types are zero or 1; stored in a byte, but loaded with AMOVBZ so the upper bytes of a register are zero.
    15  //  - *const instructions may use a constant larger than the instruction can encode.
    16  //    In this case the assembler expands to multiple instructions and uses tmp
    17  //    register (R31).
    18  
    19  var regNamesPPC64 = []string{
    20  	"R0", // REGZERO, not used, but simplifies counting in regalloc
    21  	"SP", // REGSP
    22  	"SB", // REGSB
    23  	"R3",
    24  	"R4",
    25  	"R5",
    26  	"R6",
    27  	"R7",
    28  	"R8",
    29  	"R9",
    30  	"R10",
    31  	"R11", // REGCTXT for closures
    32  	"R12",
    33  	"R13", // REGTLS
    34  	"R14",
    35  	"R15",
    36  	"R16",
    37  	"R17",
    38  	"R18",
    39  	"R19",
    40  	"R20",
    41  	"R21",
    42  	"R22",
    43  	"R23",
    44  	"R24",
    45  	"R25",
    46  	"R26",
    47  	"R27",
    48  	"R28",
    49  	"R29",
    50  	"g",   // REGG.  Using name "g" and setting Config.hasGReg makes it "just happen".
    51  	"R31", // REGTMP
    52  
    53  	"F0",
    54  	"F1",
    55  	"F2",
    56  	"F3",
    57  	"F4",
    58  	"F5",
    59  	"F6",
    60  	"F7",
    61  	"F8",
    62  	"F9",
    63  	"F10",
    64  	"F11",
    65  	"F12",
    66  	"F13",
    67  	"F14",
    68  	"F15",
    69  	"F16",
    70  	"F17",
    71  	"F18",
    72  	"F19",
    73  	"F20",
    74  	"F21",
    75  	"F22",
    76  	"F23",
    77  	"F24",
    78  	"F25",
    79  	"F26",
    80  	"F27",
    81  	"F28",
    82  	"F29",
    83  	"F30",
    84  	"F31",
    85  
    86  	// "CR0",
    87  	// "CR1",
    88  	// "CR2",
    89  	// "CR3",
    90  	// "CR4",
    91  	// "CR5",
    92  	// "CR6",
    93  	// "CR7",
    94  
    95  	// "CR",
    96  	// "XER",
    97  	// "LR",
    98  	// "CTR",
    99  }
   100  
   101  func init() {
   102  	// Make map from reg names to reg integers.
   103  	if len(regNamesPPC64) > 64 {
   104  		panic("too many registers")
   105  	}
   106  	num := map[string]int{}
   107  	for i, name := range regNamesPPC64 {
   108  		num[name] = i
   109  	}
   110  	buildReg := func(s string) regMask {
   111  		m := regMask(0)
   112  		for _, r := range strings.Split(s, " ") {
   113  			if n, ok := num[r]; ok {
   114  				m |= regMask(1) << uint(n)
   115  				continue
   116  			}
   117  			panic("register " + r + " not found")
   118  		}
   119  		return m
   120  	}
   121  
   122  	var (
   123  		gp = buildReg("R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 R25 R26 R27 R28 R29")
   124  		fp = buildReg("F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26")
   125  		sp = buildReg("SP")
   126  		sb = buildReg("SB")
   127  		gr = buildReg("g")
   128  		// cr  = buildReg("CR")
   129  		// ctr = buildReg("CTR")
   130  		// lr  = buildReg("LR")
   131  		tmp  = buildReg("R31")
   132  		ctxt = buildReg("R11")
   133  		// tls = buildReg("R13")
   134  		gp01        = regInfo{inputs: nil, outputs: []regMask{gp}}
   135  		gp11        = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}
   136  		gp21        = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}, outputs: []regMask{gp}}
   137  		gp1cr       = regInfo{inputs: []regMask{gp | sp | sb}}
   138  		gp2cr       = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
   139  		crgp        = regInfo{inputs: nil, outputs: []regMask{gp}}
   140  		gpload      = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}
   141  		gpstore     = regInfo{inputs: []regMask{gp | sp | sb, gp | sp | sb}}
   142  		gpstorezero = regInfo{inputs: []regMask{gp | sp | sb}} // ppc64.REGZERO is reserved zero value
   143  		gpxchg      = regInfo{inputs: []regMask{gp | sp | sb, gp}, outputs: []regMask{gp}}
   144  		gpcas       = regInfo{inputs: []regMask{gp | sp | sb, gp, gp}, outputs: []regMask{gp}}
   145  		fp01        = regInfo{inputs: nil, outputs: []regMask{fp}}
   146  		fp11        = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
   147  		fpgp        = regInfo{inputs: []regMask{fp}, outputs: []regMask{gp}}
   148  		gpfp        = regInfo{inputs: []regMask{gp}, outputs: []regMask{fp}}
   149  		fp21        = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
   150  		fp31        = regInfo{inputs: []regMask{fp, fp, fp}, outputs: []regMask{fp}}
   151  		fp2cr       = regInfo{inputs: []regMask{fp, fp}}
   152  		fpload      = regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{fp}}
   153  		fpstore     = regInfo{inputs: []regMask{gp | sp | sb, fp}}
   154  		callerSave  = regMask(gp | fp | gr)
   155  	)
   156  	ops := []opData{
   157  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true},                     // arg0 + arg1
   158  		{name: "ADDconst", argLength: 1, reg: gp11, asm: "ADD", aux: "SymOff", symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym)
   159  		{name: "FADD", argLength: 2, reg: fp21, asm: "FADD", commutative: true},                   // arg0+arg1
   160  		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true},                 // arg0+arg1
   161  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                                        // arg0-arg1
   162  		{name: "FSUB", argLength: 2, reg: fp21, asm: "FSUB"},                                      // arg0-arg1
   163  		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS"},                                    // arg0-arg1
   164  
   165  		{name: "MULLD", argLength: 2, reg: gp21, asm: "MULLD", typ: "Int64", commutative: true}, // arg0*arg1 (signed 64-bit)
   166  		{name: "MULLW", argLength: 2, reg: gp21, asm: "MULLW", typ: "Int32", commutative: true}, // arg0*arg1 (signed 32-bit)
   167  
   168  		{name: "MULHD", argLength: 2, reg: gp21, asm: "MULHD", commutative: true},   // (arg0 * arg1) >> 64, signed
   169  		{name: "MULHW", argLength: 2, reg: gp21, asm: "MULHW", commutative: true},   // (arg0 * arg1) >> 32, signed
   170  		{name: "MULHDU", argLength: 2, reg: gp21, asm: "MULHDU", commutative: true}, // (arg0 * arg1) >> 64, unsigned
   171  		{name: "MULHWU", argLength: 2, reg: gp21, asm: "MULHWU", commutative: true}, // (arg0 * arg1) >> 32, unsigned
   172  
   173  		{name: "FMUL", argLength: 2, reg: fp21, asm: "FMUL", commutative: true},   // arg0*arg1
   174  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true}, // arg0*arg1
   175  
   176  		{name: "FMADD", argLength: 3, reg: fp31, asm: "FMADD"},   // arg0*arg1 + arg2
   177  		{name: "FMADDS", argLength: 3, reg: fp31, asm: "FMADDS"}, // arg0*arg1 + arg2
   178  		{name: "FMSUB", argLength: 3, reg: fp31, asm: "FMSUB"},   // arg0*arg1 - arg2
   179  		{name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS"}, // arg0*arg1 - arg2
   180  
   181  		{name: "SRAD", argLength: 2, reg: gp21, asm: "SRAD"}, // arg0 >>a arg1, 64 bits (all sign if arg1 & 64 != 0)
   182  		{name: "SRAW", argLength: 2, reg: gp21, asm: "SRAW"}, // arg0 >>a arg1, 32 bits (all sign if arg1 & 32 != 0)
   183  		{name: "SRD", argLength: 2, reg: gp21, asm: "SRD"},   // arg0 >> arg1, 64 bits  (0 if arg1 & 64 != 0)
   184  		{name: "SRW", argLength: 2, reg: gp21, asm: "SRW"},   // arg0 >> arg1, 32 bits  (0 if arg1 & 32 != 0)
   185  		{name: "SLD", argLength: 2, reg: gp21, asm: "SLD"},   // arg0 << arg1, 64 bits  (0 if arg1 & 64 != 0)
   186  		{name: "SLW", argLength: 2, reg: gp21, asm: "SLW"},   // arg0 << arg1, 32 bits  (0 if arg1 & 32 != 0)
   187  
   188  		{name: "ADDconstForCarry", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}, clobbers: tmp}, aux: "Int16", asm: "ADDC", typ: "Flags"}, // _, carry := arg0 + aux
   189  		{name: "MaskIfNotCarry", argLength: 1, reg: crgp, asm: "ADDME", typ: "Int64"},                                                                   // carry - 1 (if carry then 0 else -1)
   190  
   191  		{name: "SRADconst", argLength: 1, reg: gp11, asm: "SRAD", aux: "Int64"}, // arg0 >>a aux, 64 bits
   192  		{name: "SRAWconst", argLength: 1, reg: gp11, asm: "SRAW", aux: "Int64"}, // arg0 >>a aux, 32 bits
   193  		{name: "SRDconst", argLength: 1, reg: gp11, asm: "SRD", aux: "Int64"},   // arg0 >> aux, 64 bits
   194  		{name: "SRWconst", argLength: 1, reg: gp11, asm: "SRW", aux: "Int64"},   // arg0 >> aux, 32 bits
   195  		{name: "SLDconst", argLength: 1, reg: gp11, asm: "SLD", aux: "Int64"},   // arg0 << aux, 64 bits
   196  		{name: "SLWconst", argLength: 1, reg: gp11, asm: "SLW", aux: "Int64"},   // arg0 << aux, 32 bits
   197  
   198  		{name: "ROTLconst", argLength: 1, reg: gp11, asm: "ROTL", aux: "Int64"},   // arg0 rotate left by auxInt bits
   199  		{name: "ROTLWconst", argLength: 1, reg: gp11, asm: "ROTLW", aux: "Int64"}, // uint32(arg0) rotate left by auxInt bits
   200  
   201  		{name: "CNTLZD", argLength: 1, reg: gp11, asm: "CNTLZD", clobberFlags: true}, // count leading zeros
   202  		{name: "CNTLZW", argLength: 1, reg: gp11, asm: "CNTLZW", clobberFlags: true}, // count leading zeros (32 bit)
   203  
   204  		{name: "POPCNTD", argLength: 1, reg: gp11, asm: "POPCNTD"}, // number of set bits in arg0
   205  		{name: "POPCNTW", argLength: 1, reg: gp11, asm: "POPCNTW"}, // number of set bits in each word of arg0 placed in corresponding word
   206  		{name: "POPCNTB", argLength: 1, reg: gp11, asm: "POPCNTB"}, // number of set bits in each byte of arg0 placed in corresonding byte
   207  
   208  		{name: "FDIV", argLength: 2, reg: fp21, asm: "FDIV"},   // arg0/arg1
   209  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS"}, // arg0/arg1
   210  
   211  		{name: "DIVD", argLength: 2, reg: gp21, asm: "DIVD", typ: "Int64"},   // arg0/arg1 (signed 64-bit)
   212  		{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", typ: "Int32"},   // arg0/arg1 (signed 32-bit)
   213  		{name: "DIVDU", argLength: 2, reg: gp21, asm: "DIVDU", typ: "Int64"}, // arg0/arg1 (unsigned 64-bit)
   214  		{name: "DIVWU", argLength: 2, reg: gp21, asm: "DIVWU", typ: "Int32"}, // arg0/arg1 (unsigned 32-bit)
   215  
   216  		// MOD is implemented as rem := arg0 - (arg0/arg1) * arg1
   217  
   218  		// Conversions are all float-to-float register operations.  "Integer" refers to encoding in the FP register.
   219  		{name: "FCTIDZ", argLength: 1, reg: fp11, asm: "FCTIDZ", typ: "Float64"}, // convert float to 64-bit int round towards zero
   220  		{name: "FCTIWZ", argLength: 1, reg: fp11, asm: "FCTIWZ", typ: "Float64"}, // convert float to 32-bit int round towards zero
   221  		{name: "FCFID", argLength: 1, reg: fp11, asm: "FCFID", typ: "Float64"},   // convert 64-bit integer to float
   222  		{name: "FRSP", argLength: 1, reg: fp11, asm: "FRSP", typ: "Float64"},     // round float to 32-bit value
   223  
   224  		// Movement between float and integer registers with no change in bits; accomplished with stores+loads on PPC.
   225  		// Because the 32-bit load-literal-bits instructions have impoverished addressability, always widen the
   226  		// data instead and use FMOVDload and FMOVDstore instead (this will also dodge endianess issues).
   227  		// There are optimizations that should apply -- (Xi2f64 (MOVWload (not-ADD-ptr+offset) ) ) could use
   228  		// the word-load instructions.  (Xi2f64 (MOVDload ptr )) can be (FMOVDload ptr)
   229  
   230  		{name: "Xf2i64", argLength: 1, reg: fpgp, typ: "Int64"},   // move 64 bits of F register into G register
   231  		{name: "Xi2f64", argLength: 1, reg: gpfp, typ: "Float64"}, // move 64 bits of G register into F register
   232  
   233  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},               // arg0&arg1
   234  		{name: "ANDN", argLength: 2, reg: gp21, asm: "ANDN"},                                // arg0&^arg1
   235  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                 // arg0|arg1
   236  		{name: "ORN", argLength: 2, reg: gp21, asm: "ORN"},                                  // arg0|^arg1
   237  		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},               // ^(arg0|arg1)
   238  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", typ: "Int64", commutative: true}, // arg0^arg1
   239  		{name: "EQV", argLength: 2, reg: gp21, asm: "EQV", typ: "Int64", commutative: true}, // arg0^^arg1
   240  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG"},                                  // -arg0 (integer)
   241  		{name: "FNEG", argLength: 1, reg: fp11, asm: "FNEG"},                                // -arg0 (floating point)
   242  		{name: "FSQRT", argLength: 1, reg: fp11, asm: "FSQRT"},                              // sqrt(arg0) (floating point)
   243  		{name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS"},                            // sqrt(arg0) (floating point, single precision)
   244  		{name: "FFLOOR", argLength: 1, reg: fp11, asm: "FRIM"},                              // floor(arg0), float64
   245  		{name: "FCEIL", argLength: 1, reg: fp11, asm: "FRIP"},                               // ceil(arg0), float64
   246  		{name: "FTRUNC", argLength: 1, reg: fp11, asm: "FRIZ"},                              // trunc(arg0), float64
   247  
   248  		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int64"},                                                                                     // arg0|aux
   249  		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int64"},                                                                                   // arg0^aux
   250  		{name: "ANDconst", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}, outputs: []regMask{gp}}, asm: "ANDCC", aux: "Int64", clobberFlags: true}, // arg0&aux // and-immediate sets CC on PPC, always.
   251  		{name: "ANDCCconst", argLength: 1, reg: regInfo{inputs: []regMask{gp | sp | sb}}, asm: "ANDCC", aux: "Int64", typ: "Flags"},                             // arg0&aux == 0 // and-immediate sets CC on PPC, always.
   252  
   253  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB", typ: "Int64"},                                                               // sign extend int8 to int64
   254  		{name: "MOVBZreg", argLength: 1, reg: gp11, asm: "MOVBZ", typ: "Int64"},                                                             // zero extend uint8 to uint64
   255  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH", typ: "Int64"},                                                               // sign extend int16 to int64
   256  		{name: "MOVHZreg", argLength: 1, reg: gp11, asm: "MOVHZ", typ: "Int64"},                                                             // zero extend uint16 to uint64
   257  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW", typ: "Int64"},                                                               // sign extend int32 to int64
   258  		{name: "MOVWZreg", argLength: 1, reg: gp11, asm: "MOVWZ", typ: "Int64"},                                                             // zero extend uint32 to uint64
   259  		{name: "MOVBZload", argLength: 2, reg: gpload, asm: "MOVBZ", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // zero extend uint8 to uint64
   260  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // sign extend int16 to int64
   261  		{name: "MOVHZload", argLength: 2, reg: gpload, asm: "MOVHZ", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // zero extend uint16 to uint64
   262  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},    // sign extend int32 to int64
   263  		{name: "MOVWZload", argLength: 2, reg: gpload, asm: "MOVWZ", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // zero extend uint32 to uint64
   264  		{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOVD", aux: "SymOff", typ: "Int64", faultOnNilArg0: true, symEffect: "Read"},
   265  
   266  		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "FMOVD", aux: "SymOff", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},
   267  		{name: "FMOVSload", argLength: 2, reg: fpload, asm: "FMOVS", aux: "SymOff", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},
   268  		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},
   269  		{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},
   270  		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},
   271  		{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},
   272  		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "FMOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},
   273  		{name: "FMOVSstore", argLength: 3, reg: fpstore, asm: "FMOVS", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},
   274  
   275  		{name: "MOVBstorezero", argLength: 2, reg: gpstorezero, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store zero byte to arg0+aux.  arg1=mem
   276  		{name: "MOVHstorezero", argLength: 2, reg: gpstorezero, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store zero 2 bytes to ...
   277  		{name: "MOVWstorezero", argLength: 2, reg: gpstorezero, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store zero 4 bytes to ...
   278  		{name: "MOVDstorezero", argLength: 2, reg: gpstorezero, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store zero 8 bytes to ...
   279  
   280  		{name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{sp | sb}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB
   281  
   282  		{name: "MOVDconst", argLength: 0, reg: gp01, aux: "Int64", asm: "MOVD", typ: "Int64", rematerializeable: true}, //
   283  		{name: "FMOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "FMOVD", rematerializeable: true},           //
   284  		{name: "FMOVSconst", argLength: 0, reg: fp01, aux: "Float32", asm: "FMOVS", rematerializeable: true},           //
   285  		{name: "FCMPU", argLength: 2, reg: fp2cr, asm: "FCMPU", typ: "Flags"},
   286  
   287  		{name: "CMP", argLength: 2, reg: gp2cr, asm: "CMP", typ: "Flags"},     // arg0 compare to arg1
   288  		{name: "CMPU", argLength: 2, reg: gp2cr, asm: "CMPU", typ: "Flags"},   // arg0 compare to arg1
   289  		{name: "CMPW", argLength: 2, reg: gp2cr, asm: "CMPW", typ: "Flags"},   // arg0 compare to arg1
   290  		{name: "CMPWU", argLength: 2, reg: gp2cr, asm: "CMPWU", typ: "Flags"}, // arg0 compare to arg1
   291  		{name: "CMPconst", argLength: 1, reg: gp1cr, asm: "CMP", aux: "Int64", typ: "Flags"},
   292  		{name: "CMPUconst", argLength: 1, reg: gp1cr, asm: "CMPU", aux: "Int64", typ: "Flags"},
   293  		{name: "CMPWconst", argLength: 1, reg: gp1cr, asm: "CMPW", aux: "Int32", typ: "Flags"},
   294  		{name: "CMPWUconst", argLength: 1, reg: gp1cr, asm: "CMPWU", aux: "Int32", typ: "Flags"},
   295  
   296  		// pseudo-ops
   297  		{name: "Equal", argLength: 1, reg: crgp},         // bool, true flags encode x==y false otherwise.
   298  		{name: "NotEqual", argLength: 1, reg: crgp},      // bool, true flags encode x!=y false otherwise.
   299  		{name: "LessThan", argLength: 1, reg: crgp},      // bool, true flags encode  x<y false otherwise.
   300  		{name: "FLessThan", argLength: 1, reg: crgp},     // bool, true flags encode  x<y false otherwise.
   301  		{name: "LessEqual", argLength: 1, reg: crgp},     // bool, true flags encode  x<=y false otherwise.
   302  		{name: "FLessEqual", argLength: 1, reg: crgp},    // bool, true flags encode  x<=y false otherwise; PPC <= === !> which is wrong for NaN
   303  		{name: "GreaterThan", argLength: 1, reg: crgp},   // bool, true flags encode  x>y false otherwise.
   304  		{name: "FGreaterThan", argLength: 1, reg: crgp},  // bool, true flags encode  x>y false otherwise.
   305  		{name: "GreaterEqual", argLength: 1, reg: crgp},  // bool, true flags encode  x>=y false otherwise.
   306  		{name: "FGreaterEqual", argLength: 1, reg: crgp}, // bool, true flags encode  x>=y false otherwise.; PPC >= === !< which is wrong for NaN
   307  
   308  		// Scheduler ensures LoweredGetClosurePtr occurs only in entry block,
   309  		// and sorts it to the very beginning of the block to prevent other
   310  		// use of the closure pointer.
   311  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{ctxt}}},
   312  
   313  		//arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   314  		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gp | sp | sb}, clobbers: tmp}, clobberFlags: true, nilCheck: true, faultOnNilArg0: true},
   315  		// Round ops to block fused-multiply-add extraction.
   316  		{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true},
   317  		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true},
   318  
   319  		// Convert pointer to integer, takes a memory operand for ordering.
   320  		{name: "MOVDconvert", argLength: 2, reg: gp11, asm: "MOVD"},
   321  
   322  		{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
   323  		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gp | sp, ctxt, 0}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true}, // call function via closure.  arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   324  		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "Int64", clobberFlags: true, call: true},                 // call fn by pointer.  arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   325  
   326  		// large or unaligned zeroing
   327  		// arg0 = address of memory to zero (in R3, changed as side effect)
   328  		// returns mem
   329  		//
   330  		// a loop is generated when there is more than one iteration
   331  		// needed to clear 4 doublewords
   332  		//
   333  		// 	MOVD	$len/32,R31
   334  		//	MOVD	R31,CTR
   335  		//	loop:
   336  		//	MOVD	R0,(R3)
   337  		//	MOVD	R0,8(R3)
   338  		//	MOVD	R0,16(R3)
   339  		//	MOVD	R0,24(R3)
   340  		//	ADD	R3,32
   341  		//	BC	loop
   342  
   343  		// remaining doubleword clears generated as needed
   344  		//	MOVD	R0,(R3)
   345  		//	MOVD	R0,8(R3)
   346  		//	MOVD	R0,16(R3)
   347  		//	MOVD	R0,24(R3)
   348  
   349  		// one or more of these to clear remainder < 8 bytes
   350  		//	MOVW	R0,n1(R3)
   351  		//	MOVH	R0,n2(R3)
   352  		//	MOVB	R0,n3(R3)
   353  		{
   354  			name:      "LoweredZero",
   355  			aux:       "Int64",
   356  			argLength: 2,
   357  			reg: regInfo{
   358  				inputs:   []regMask{buildReg("R3")},
   359  				clobbers: buildReg("R3"),
   360  			},
   361  			clobberFlags:   true,
   362  			typ:            "Mem",
   363  			faultOnNilArg0: true,
   364  		},
   365  		// Loop code:
   366  		//	MOVD len/32,REG_TMP  only for loop
   367  		//	MOVD REG_TMP,CTR     only for loop
   368  		// loop:
   369  		//	MOVD (R4),R7
   370  		//	MOVD 8(R4),R8
   371  		//	MOVD 16(R4),R9
   372  		//	MOVD 24(R4),R10
   373  		//	ADD  R4,$32          only with loop
   374  		//	MOVD R7,(R3)
   375  		//	MOVD R8,8(R3)
   376  		//	MOVD R9,16(R3)
   377  		//	MOVD R10,24(R3)
   378  		//	ADD  R3,$32          only with loop
   379  		//	BC 16,0,loop         only with loop
   380  		// Bytes not moved by this loop are moved
   381  		// with a combination of the following instructions,
   382  		// starting with the largest sizes and generating as
   383  		// many as needed, using the appropriate offset value.
   384  		//	MOVD  n(R4),R7
   385  		//	MOVD  R7,n(R3)
   386  		//	MOVW  n1(R4),R7
   387  		//	MOVW  R7,n1(R3)
   388  		//	MOVH  n2(R4),R7
   389  		//	MOVH  R7,n2(R3)
   390  		//	MOVB  n3(R4),R7
   391  		//	MOVB  R7,n3(R3)
   392  
   393  		{
   394  			name:      "LoweredMove",
   395  			aux:       "Int64",
   396  			argLength: 3,
   397  			reg: regInfo{
   398  				inputs:   []regMask{buildReg("R3"), buildReg("R4")},
   399  				clobbers: buildReg("R3 R4 R7 R8 R9 R10"),
   400  			},
   401  			clobberFlags:   true,
   402  			typ:            "Mem",
   403  			faultOnNilArg0: true,
   404  			faultOnNilArg1: true,
   405  		},
   406  
   407  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, typ: "Mem", faultOnNilArg0: true, hasSideEffects: true},
   408  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, typ: "Mem", faultOnNilArg0: true, hasSideEffects: true},
   409  
   410  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, typ: "UInt32", clobberFlags: true, faultOnNilArg0: true},
   411  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, typ: "Int64", clobberFlags: true, faultOnNilArg0: true},
   412  		{name: "LoweredAtomicLoadPtr", argLength: 2, reg: gpload, typ: "Int64", clobberFlags: true, faultOnNilArg0: true},
   413  
   414  		// atomic add32, 64
   415  		// SYNC
   416  		// LDAR         (Rarg0), Rout
   417  		// ADD		Rarg1, Rout
   418  		// STDCCC       Rout, (Rarg0)
   419  		// BNE          -3(PC)
   420  		// ISYNC
   421  		// return new sum
   422  
   423  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   424  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   425  
   426  		// atomic exchange32, 64
   427  		// SYNC
   428  		// LDAR         (Rarg0), Rout
   429  		// STDCCC       Rarg1, (Rarg0)
   430  		// BNE          -2(PC)
   431  		// ISYNC
   432  		// return old val
   433  
   434  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   435  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   436  
   437  		// atomic compare and swap.
   438  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory. auxint must be zero.
   439  		// if *arg0 == arg1 {
   440  		//   *arg0 = arg2
   441  		//   return (true, memory)
   442  		// } else {
   443  		//   return (false, memory)
   444  		// }
   445  		// SYNC
   446  		// LDAR		(Rarg0), Rtmp
   447  		// CMP		Rarg1, Rtmp
   448  		// BNE		3(PC)
   449  		// STDCCC	Rarg2, (Rarg0)
   450  		// BNE		-4(PC)
   451  		// CBNZ         Rtmp, -4(PC)
   452  		// CSET         EQ, Rout
   453  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   454  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, clobberFlags: true, faultOnNilArg0: true, hasSideEffects: true},
   455  
   456  		// atomic 8 and/or.
   457  		// *arg0 &= (|=) arg1. arg2=mem. returns memory. auxint must be zero.
   458  		// LBAR		(Rarg0), Rtmp
   459  		// AND/OR	Rarg1, Rtmp
   460  		// STBCCC	Rtmp, (Rarg0), Rtmp
   461  		// BNE		Rtmp, -3(PC)
   462  
   463  		{name: "LoweredAtomicAnd8", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true},
   464  		{name: "LoweredAtomicOr8", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true},
   465  
   466  		// (InvertFlags (CMP a b)) == (CMP b a)
   467  		// So if we want (LessThan (CMP a b)) but we can't do that because a is a constant,
   468  		// then we do (LessThan (InvertFlags (CMP b a))) instead.
   469  		// Rewrites will convert this to (GreaterThan (CMP b a)).
   470  		// InvertFlags is a pseudo-op which can't appear in assembly output.
   471  		{name: "InvertFlags", argLength: 1}, // reverse direction of arg0
   472  
   473  		// Constant flag values. For any comparison, there are 3 possible
   474  		// outcomes: either the three from the signed total order (<,==,>)
   475  		// or the three from the unsigned total order, depending on which
   476  		// comparison operation was used (CMP or CMPU -- PPC is different from
   477  		// the other architectures, which have a single comparison producing
   478  		// both signed and unsigned comparison results.)
   479  
   480  		// These ops are for temporary use by rewrite rules. They
   481  		// cannot appear in the generated assembly.
   482  		{name: "FlagEQ"}, // equal
   483  		{name: "FlagLT"}, // signed < or unsigned <
   484  		{name: "FlagGT"}, // signed > or unsigned >
   485  
   486  	}
   487  
   488  	blocks := []blockData{
   489  		{name: "EQ"},
   490  		{name: "NE"},
   491  		{name: "LT"},
   492  		{name: "LE"},
   493  		{name: "GT"},
   494  		{name: "GE"},
   495  		{name: "FLT"},
   496  		{name: "FLE"},
   497  		{name: "FGT"},
   498  		{name: "FGE"},
   499  	}
   500  
   501  	archs = append(archs, arch{
   502  		name:            "PPC64",
   503  		pkg:             "cmd/internal/obj/ppc64",
   504  		genfile:         "../../ppc64/ssa.go",
   505  		ops:             ops,
   506  		blocks:          blocks,
   507  		regnames:        regNamesPPC64,
   508  		gpregmask:       gp,
   509  		fpregmask:       fp,
   510  		framepointerreg: int8(num["SP"]),
   511  		linkreg:         -1, // not used
   512  	})
   513  }