Source file src/cmd/compile/internal/ssa/_gen/RISCV64Ops.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  package main
     6  
     7  import (
     8  	"fmt"
     9  )
    10  
    11  // Notes:
    12  //  - Boolean types occupy the entire register. 0=false, 1=true.
    13  
    14  // Suffixes encode the bit width of various instructions:
    15  //
    16  // D (double word) = 64 bit int
    17  // W (word)        = 32 bit int
    18  // H (half word)   = 16 bit int
    19  // B (byte)        = 8 bit int
    20  // S (single)      = 32 bit float
    21  // D (double)      = 64 bit float
    22  // L               = 64 bit int, used when the opcode starts with F
    23  
    24  const (
    25  	riscv64REG_G    = 27
    26  	riscv64REG_CTXT = 26
    27  	riscv64REG_LR   = 1
    28  	riscv64REG_SP   = 2
    29  	riscv64REG_GP   = 3
    30  	riscv64REG_TP   = 4
    31  	riscv64REG_TMP  = 31
    32  	riscv64REG_ZERO = 0
    33  )
    34  
    35  func riscv64RegName(r int) string {
    36  	switch {
    37  	case r == riscv64REG_G:
    38  		return "g"
    39  	case r == riscv64REG_SP:
    40  		return "SP"
    41  	case 0 <= r && r <= 31:
    42  		return fmt.Sprintf("X%d", r)
    43  	case 32 <= r && r <= 63:
    44  		return fmt.Sprintf("F%d", r-32)
    45  	default:
    46  		panic(fmt.Sprintf("unknown register %d", r))
    47  	}
    48  }
    49  
    50  func init() {
    51  	var regNamesRISCV64 []string
    52  	var gpMask, fpMask, gpgMask, gpspMask, gpspsbMask, gpspsbgMask, first16Mask regMask
    53  	regNamed := make(map[string]regMask)
    54  
    55  	// Build the list of register names, creating an appropriately indexed
    56  	// regMask for the gp and fp registers as we go.
    57  	//
    58  	// If name is specified, use it rather than the riscv reg number.
    59  	addreg := func(r int, name string) regMask {
    60  		mask := regMask(1) << uint(len(regNamesRISCV64))
    61  		if name == "" {
    62  			name = riscv64RegName(r)
    63  		}
    64  		regNamesRISCV64 = append(regNamesRISCV64, name)
    65  		regNamed[name] = mask
    66  		return mask
    67  	}
    68  
    69  	// General purpose registers.
    70  	for r := 0; r <= 31; r++ {
    71  		if r == riscv64REG_LR {
    72  			// LR is not used by regalloc, so we skip it to leave
    73  			// room for pseudo-register SB.
    74  			continue
    75  		}
    76  
    77  		mask := addreg(r, "")
    78  
    79  		// Add general purpose registers to gpMask.
    80  		switch r {
    81  		// ZERO, GP, TP and TMP are not in any gp mask.
    82  		case riscv64REG_ZERO, riscv64REG_GP, riscv64REG_TP, riscv64REG_TMP:
    83  		case riscv64REG_G:
    84  			gpgMask |= mask
    85  			gpspsbgMask |= mask
    86  		case riscv64REG_SP:
    87  			gpspMask |= mask
    88  			gpspsbMask |= mask
    89  			gpspsbgMask |= mask
    90  		default:
    91  			gpMask |= mask
    92  			gpgMask |= mask
    93  			gpspMask |= mask
    94  			gpspsbMask |= mask
    95  			gpspsbgMask |= mask
    96  			if r >= 5 && r < 5+16 {
    97  				first16Mask |= mask
    98  			}
    99  		}
   100  	}
   101  
   102  	// Floating pointer registers.
   103  	for r := 32; r <= 63; r++ {
   104  		mask := addreg(r, "")
   105  		fpMask |= mask
   106  	}
   107  
   108  	// Pseudo-register: SB
   109  	mask := addreg(-1, "SB")
   110  	gpspsbMask |= mask
   111  	gpspsbgMask |= mask
   112  
   113  	if len(regNamesRISCV64) > 64 {
   114  		// regMask is only 64 bits.
   115  		panic("Too many RISCV64 registers")
   116  	}
   117  
   118  	regCtxt := regNamed["X26"]
   119  	callerSave := gpMask | fpMask | regNamed["g"]
   120  
   121  	var (
   122  		gpstore  = regInfo{inputs: []regMask{gpspsbMask, gpspMask, 0}} // SB in first input so we can load from a global, but not in second to avoid using SB as a temporary register
   123  		gpstore0 = regInfo{inputs: []regMask{gpspsbMask}}
   124  		gp01     = regInfo{outputs: []regMask{gpMask}}
   125  		gp11     = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{gpMask}}
   126  		gp21     = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask}}
   127  		gp22     = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask, gpMask}}
   128  		gpload   = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{gpMask}}
   129  		gp11sb   = regInfo{inputs: []regMask{gpspsbMask}, outputs: []regMask{gpMask}}
   130  		gpxchg   = regInfo{inputs: []regMask{gpspsbgMask, gpgMask}, outputs: []regMask{gpMask}}
   131  		gpcas    = regInfo{inputs: []regMask{gpspsbgMask, gpgMask, gpgMask}, outputs: []regMask{gpMask}}
   132  		gpatomic = regInfo{inputs: []regMask{gpspsbgMask, gpgMask}}
   133  
   134  		fp11    = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{fpMask}}
   135  		fp21    = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{fpMask}}
   136  		fp31    = regInfo{inputs: []regMask{fpMask, fpMask, fpMask}, outputs: []regMask{fpMask}}
   137  		gpfp    = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{fpMask}}
   138  		fpgp    = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{gpMask}}
   139  		fpstore = regInfo{inputs: []regMask{gpspsbMask, fpMask, 0}}
   140  		fpload  = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{fpMask}}
   141  		fp2gp   = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{gpMask}}
   142  
   143  		call        = regInfo{clobbers: callerSave}
   144  		callClosure = regInfo{inputs: []regMask{gpspMask, regCtxt, 0}, clobbers: callerSave}
   145  		callInter   = regInfo{inputs: []regMask{gpMask}, clobbers: callerSave}
   146  	)
   147  
   148  	RISCV64ops := []opData{
   149  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1
   150  		{name: "ADDI", argLength: 1, reg: gp11sb, asm: "ADDI", aux: "Int64"},  // arg0 + auxint
   151  		{name: "ADDIW", argLength: 1, reg: gp11, asm: "ADDIW", aux: "Int64"},  // 32 low bits of arg0 + auxint, sign extended to 64 bits
   152  		{name: "NEG", argLength: 1, reg: gp11, asm: "NEG"},                    // -arg0
   153  		{name: "NEGW", argLength: 1, reg: gp11, asm: "NEGW"},                  // -arg0 of 32 bits, sign extended to 64 bits
   154  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                    // arg0 - arg1
   155  		{name: "SUBW", argLength: 2, reg: gp21, asm: "SUBW"},                  // 32 low bits of arg 0 - 32 low bits of arg 1, sign extended to 64 bits
   156  
   157  		// M extension. H means high (i.e., it returns the top bits of
   158  		// the result). U means unsigned. W means word (i.e., 32-bit).
   159  		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true, typ: "Int64"}, // arg0 * arg1
   160  		{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true, typ: "Int32"},
   161  		{name: "MULH", argLength: 2, reg: gp21, asm: "MULH", commutative: true, typ: "Int64"},
   162  		{name: "MULHU", argLength: 2, reg: gp21, asm: "MULHU", commutative: true, typ: "UInt64"},
   163  		{name: "LoweredMuluhilo", argLength: 2, reg: gp22, resultNotInArgs: true}, // arg0 * arg1, return (hi, lo)
   164  		{name: "LoweredMuluover", argLength: 2, reg: gp22, resultNotInArgs: true}, // arg0 * arg1, return (64 bits of arg0*arg1, overflow)
   165  
   166  		{name: "DIV", argLength: 2, reg: gp21, asm: "DIV", typ: "Int64"}, // arg0 / arg1
   167  		{name: "DIVU", argLength: 2, reg: gp21, asm: "DIVU", typ: "UInt64"},
   168  		{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", typ: "Int32"},
   169  		{name: "DIVUW", argLength: 2, reg: gp21, asm: "DIVUW", typ: "UInt32"},
   170  		{name: "REM", argLength: 2, reg: gp21, asm: "REM", typ: "Int64"}, // arg0 % arg1
   171  		{name: "REMU", argLength: 2, reg: gp21, asm: "REMU", typ: "UInt64"},
   172  		{name: "REMW", argLength: 2, reg: gp21, asm: "REMW", typ: "Int32"},
   173  		{name: "REMUW", argLength: 2, reg: gp21, asm: "REMUW", typ: "UInt32"},
   174  
   175  		{name: "MOVaddr", argLength: 1, reg: gp11sb, asm: "MOV", aux: "SymOff", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxint + offset encoded in aux
   176  		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   177  
   178  		{name: "MOVDconst", reg: gp01, asm: "MOV", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint
   179  
   180  		// Loads: load <size> bits from arg0+auxint+aux and extend to 64 bits; arg1=mem
   181  		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     //  8 bits, sign extend
   182  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // 16 bits, sign extend
   183  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"},    // 32 bits, sign extend
   184  		{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOV", aux: "SymOff", typ: "Int64", faultOnNilArg0: true, symEffect: "Read"},     // 64 bits
   185  		{name: "MOVBUload", argLength: 2, reg: gpload, asm: "MOVBU", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  //  8 bits, zero extend
   186  		{name: "MOVHUload", argLength: 2, reg: gpload, asm: "MOVHU", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // 16 bits, zero extend
   187  		{name: "MOVWUload", argLength: 2, reg: gpload, asm: "MOVWU", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // 32 bits, zero extend
   188  
   189  		// Stores: store <size> lowest bits in arg1 to arg0+auxint+aux; arg2=mem
   190  		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, //  8 bits
   191  		{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 16 bits
   192  		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 32 bits
   193  		{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOV", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},  // 64 bits
   194  
   195  		// Stores: store <size> of zero in arg0+auxint+aux; arg1=mem
   196  		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, //  8 bits
   197  		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 16 bits
   198  		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 32 bits
   199  		{name: "MOVDstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOV", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},  // 64 bits
   200  
   201  		// Conversions
   202  		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // move from arg0, sign-extended from byte
   203  		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // move from arg0, sign-extended from half
   204  		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // move from arg0, sign-extended from word
   205  		{name: "MOVDreg", argLength: 1, reg: gp11, asm: "MOV"},    // move from arg0
   206  		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // move from arg0, unsign-extended from byte
   207  		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // move from arg0, unsign-extended from half
   208  		{name: "MOVWUreg", argLength: 1, reg: gp11, asm: "MOVWU"}, // move from arg0, unsign-extended from word
   209  
   210  		{name: "MOVDnop", argLength: 1, reg: regInfo{inputs: []regMask{gpMask}, outputs: []regMask{gpMask}}, resultInArg0: true}, // nop, return arg0 in same register
   211  
   212  		// Shift ops
   213  		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                   // arg0 << (aux1 & 63), logical left shift
   214  		{name: "SLLW", argLength: 2, reg: gp21, asm: "SLLW"},                 // arg0 << (aux1 & 31), logical left shift of 32 bit value, sign extended to 64 bits
   215  		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                   // arg0 >> (aux1 & 63), arithmetic right shift
   216  		{name: "SRAW", argLength: 2, reg: gp21, asm: "SRAW"},                 // arg0 >> (aux1 & 31), arithmetic right shift of 32 bit value, sign extended to 64 bits
   217  		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                   // arg0 >> (aux1 & 63), logical right shift
   218  		{name: "SRLW", argLength: 2, reg: gp21, asm: "SRLW"},                 // arg0 >> (aux1 & 31), logical right shift of 32 bit value, sign extended to 64 bits
   219  		{name: "SLLI", argLength: 1, reg: gp11, asm: "SLLI", aux: "Int64"},   // arg0 << auxint, shift amount 0-63, logical left shift
   220  		{name: "SLLIW", argLength: 1, reg: gp11, asm: "SLLIW", aux: "Int64"}, // arg0 << auxint, shift amount 0-31, logical left shift of 32 bit value, sign extended to 64 bits
   221  		{name: "SRAI", argLength: 1, reg: gp11, asm: "SRAI", aux: "Int64"},   // arg0 >> auxint, shift amount 0-63, arithmetic right shift
   222  		{name: "SRAIW", argLength: 1, reg: gp11, asm: "SRAIW", aux: "Int64"}, // arg0 >> auxint, shift amount 0-31, arithmetic right shift of 32 bit value, sign extended to 64 bits
   223  		{name: "SRLI", argLength: 1, reg: gp11, asm: "SRLI", aux: "Int64"},   // arg0 >> auxint, shift amount 0-63, logical right shift
   224  		{name: "SRLIW", argLength: 1, reg: gp11, asm: "SRLIW", aux: "Int64"}, // arg0 >> auxint, shift amount 0-31, logical right shift of 32 bit value, sign extended to 64 bits
   225  
   226  		// Shift and add
   227  		{name: "SH1ADD", argLength: 2, reg: gp21, asm: "SH1ADD"}, // arg0 << 1 + arg1
   228  		{name: "SH2ADD", argLength: 2, reg: gp21, asm: "SH2ADD"}, // arg0 << 2 + arg1
   229  		{name: "SH3ADD", argLength: 2, reg: gp21, asm: "SH3ADD"}, // arg0 << 3 + arg1
   230  
   231  		// Bitwise ops
   232  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},   // arg0 & arg1
   233  		{name: "ANDN", argLength: 2, reg: gp21, asm: "ANDN"},                    // ^arg0 & arg1
   234  		{name: "ANDI", argLength: 1, reg: gp11, asm: "ANDI", aux: "Int64"},      // arg0 & auxint
   235  		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},                      // count leading zeros
   236  		{name: "CLZW", argLength: 1, reg: gp11, asm: "CLZW"},                    // count leading zeros of least significant word
   237  		{name: "CPOP", argLength: 1, reg: gp11, asm: "CPOP"},                    // count set bits
   238  		{name: "CPOPW", argLength: 1, reg: gp11, asm: "CPOPW"},                  // count set bits in least significant word
   239  		{name: "CTZ", argLength: 1, reg: gp11, asm: "CTZ"},                      // count trailing zeros
   240  		{name: "CTZW", argLength: 1, reg: gp11, asm: "CTZW"},                    // count trailing zeros of least significant word
   241  		{name: "NOT", argLength: 1, reg: gp11, asm: "NOT"},                      // ^arg0
   242  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},     // arg0 | arg1
   243  		{name: "ORN", argLength: 2, reg: gp21, asm: "ORN"},                      // ^arg0 | arg1
   244  		{name: "ORI", argLength: 1, reg: gp11, asm: "ORI", aux: "Int64"},        // arg0 | auxint
   245  		{name: "REV8", argLength: 1, reg: gp11, asm: "REV8"},                    // reverse bytes
   246  		{name: "ROL", argLength: 2, reg: gp21, asm: "ROL"},                      // rotate left arg0 by (arg1 & 63)
   247  		{name: "ROLW", argLength: 2, reg: gp21, asm: "ROLW"},                    // rotate left least significant word of arg0 by (arg1 & 31), sign extended
   248  		{name: "ROR", argLength: 2, reg: gp21, asm: "ROR"},                      // rotate right arg0 by (arg1 & 63)
   249  		{name: "RORI", argLength: 1, reg: gp11, asm: "RORI", aux: "Int64"},      // rotate right arg0 by auxint, shift amount 0-63
   250  		{name: "RORIW", argLength: 1, reg: gp11, asm: "RORIW", aux: "Int64"},    // rotate right least significant word of arg0 by auxint, shift amount 0-31, sign extended
   251  		{name: "RORW", argLength: 2, reg: gp21, asm: "RORW"},                    // rotate right least significant word of arg0 by (arg1 & 31), sign extended
   252  		{name: "XNOR", argLength: 2, reg: gp21, asm: "XNOR", commutative: true}, // ^(arg0 ^ arg1)
   253  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true},   // arg0 ^ arg1
   254  		{name: "XORI", argLength: 1, reg: gp11, asm: "XORI", aux: "Int64"},      // arg0 ^ auxint
   255  
   256  		// Minimum and maximum
   257  		{name: "MIN", argLength: 2, reg: gp21, asm: "MIN", commutative: true},   // min(arg0,arg1), signed
   258  		{name: "MAX", argLength: 2, reg: gp21, asm: "MAX", commutative: true},   // max(arg0,arg1), signed
   259  		{name: "MINU", argLength: 2, reg: gp21, asm: "MINU", commutative: true}, // min(arg0,arg1), unsigned
   260  		{name: "MAXU", argLength: 2, reg: gp21, asm: "MAXU", commutative: true}, // max(arg0,arg1), unsigned
   261  
   262  		// Generate boolean values
   263  		{name: "SEQZ", argLength: 1, reg: gp11, asm: "SEQZ"},                 // arg0 == 0, result is 0 or 1
   264  		{name: "SNEZ", argLength: 1, reg: gp11, asm: "SNEZ"},                 // arg0 != 0, result is 0 or 1
   265  		{name: "SLT", argLength: 2, reg: gp21, asm: "SLT"},                   // arg0 < arg1, result is 0 or 1
   266  		{name: "SLTI", argLength: 1, reg: gp11, asm: "SLTI", aux: "Int64"},   // arg0 < auxint, result is 0 or 1
   267  		{name: "SLTU", argLength: 2, reg: gp21, asm: "SLTU"},                 // arg0 < arg1, unsigned, result is 0 or 1
   268  		{name: "SLTIU", argLength: 1, reg: gp11, asm: "SLTIU", aux: "Int64"}, // arg0 < auxint, unsigned, result is 0 or 1
   269  
   270  		// Round ops to block fused-multiply-add extraction.
   271  		{name: "LoweredRound32F", argLength: 1, reg: fp11, resultInArg0: true},
   272  		{name: "LoweredRound64F", argLength: 1, reg: fp11, resultInArg0: true},
   273  
   274  		// Calls
   275  		{name: "CALLstatic", argLength: -1, reg: call, aux: "CallOff", call: true},               // call static function aux.(*gc.Sym). last arg=mem, auxint=argsize, returns mem
   276  		{name: "CALLtail", argLength: -1, reg: call, aux: "CallOff", call: true, tailCall: true}, // tail call static function aux.(*gc.Sym). last arg=mem, auxint=argsize, returns mem
   277  		{name: "CALLclosure", argLength: -1, reg: callClosure, aux: "CallOff", call: true},       // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
   278  		{name: "CALLinter", argLength: -1, reg: callInter, aux: "CallOff", call: true},           // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem
   279  
   280  		// duffzero
   281  		// arg0 = address of memory to zero (in X25, changed as side effect)
   282  		// arg1 = mem
   283  		// auxint = offset into duffzero code to start executing
   284  		// X1 (link register) changed because of function call
   285  		// returns mem
   286  		{
   287  			name:      "DUFFZERO",
   288  			aux:       "Int64",
   289  			argLength: 2,
   290  			reg: regInfo{
   291  				inputs:   []regMask{regNamed["X25"]},
   292  				clobbers: regNamed["X1"] | regNamed["X25"],
   293  			},
   294  			typ:            "Mem",
   295  			faultOnNilArg0: true,
   296  		},
   297  
   298  		// duffcopy
   299  		// arg0 = address of dst memory (in X25, changed as side effect)
   300  		// arg1 = address of src memory (in X24, changed as side effect)
   301  		// arg2 = mem
   302  		// auxint = offset into duffcopy code to start executing
   303  		// X1 (link register) changed because of function call
   304  		// returns mem
   305  		{
   306  			name:      "DUFFCOPY",
   307  			aux:       "Int64",
   308  			argLength: 3,
   309  			reg: regInfo{
   310  				inputs:   []regMask{regNamed["X25"], regNamed["X24"]},
   311  				clobbers: regNamed["X1"] | regNamed["X24"] | regNamed["X25"],
   312  			},
   313  			typ:            "Mem",
   314  			faultOnNilArg0: true,
   315  			faultOnNilArg1: true,
   316  		},
   317  
   318  		// Generic moves and zeros
   319  
   320  		// general unaligned zeroing
   321  		// arg0 = address of memory to zero (in X5, changed as side effect)
   322  		// arg1 = address of the last element to zero (inclusive)
   323  		// arg2 = mem
   324  		// auxint = element size
   325  		// returns mem
   326  		//	mov	ZERO, (X5)
   327  		//	ADD	$sz, X5
   328  		//	BGEU	Rarg1, X5, -2(PC)
   329  		{
   330  			name:      "LoweredZero",
   331  			aux:       "Int64",
   332  			argLength: 3,
   333  			reg: regInfo{
   334  				inputs:   []regMask{regNamed["X5"], gpMask},
   335  				clobbers: regNamed["X5"],
   336  			},
   337  			typ:            "Mem",
   338  			faultOnNilArg0: true,
   339  		},
   340  
   341  		// general unaligned move
   342  		// arg0 = address of dst memory (in X5, changed as side effect)
   343  		// arg1 = address of src memory (in X6, changed as side effect)
   344  		// arg2 = address of the last element of src (can't be X7 as we clobber it before using arg2)
   345  		// arg3 = mem
   346  		// auxint = alignment
   347  		// clobbers X7 as a tmp register.
   348  		// returns mem
   349  		//	mov	(X6), X7
   350  		//	mov	X7, (X5)
   351  		//	ADD	$sz, X5
   352  		//	ADD	$sz, X6
   353  		//	BGEU	Rarg2, X5, -4(PC)
   354  		{
   355  			name:      "LoweredMove",
   356  			aux:       "Int64",
   357  			argLength: 4,
   358  			reg: regInfo{
   359  				inputs:   []regMask{regNamed["X5"], regNamed["X6"], gpMask &^ regNamed["X7"]},
   360  				clobbers: regNamed["X5"] | regNamed["X6"] | regNamed["X7"],
   361  			},
   362  			typ:            "Mem",
   363  			faultOnNilArg0: true,
   364  			faultOnNilArg1: true,
   365  		},
   366  
   367  		// Atomic loads.
   368  		// load from arg0. arg1=mem.
   369  		// returns <value,memory> so they can be properly ordered with other loads.
   370  		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
   371  		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},
   372  		{name: "LoweredAtomicLoad64", argLength: 2, reg: gpload, faultOnNilArg0: true},
   373  
   374  		// Atomic stores.
   375  		// store arg1 to *arg0. arg2=mem. returns memory.
   376  		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   377  		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   378  		{name: "LoweredAtomicStore64", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
   379  
   380  		// Atomic exchange.
   381  		// store arg1 to *arg0. arg2=mem. returns <old content of *arg0, memory>.
   382  		{name: "LoweredAtomicExchange32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   383  		{name: "LoweredAtomicExchange64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true},
   384  
   385  		// Atomic add.
   386  		// *arg0 += arg1. arg2=mem. returns <new content of *arg0, memory>.
   387  		{name: "LoweredAtomicAdd32", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   388  		{name: "LoweredAtomicAdd64", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   389  
   390  		// Atomic compare and swap.
   391  		// arg0 = pointer, arg1 = old value, arg2 = new value, arg3 = memory.
   392  		// if *arg0 == arg1 {
   393  		//   *arg0 = arg2
   394  		//   return (true, memory)
   395  		// } else {
   396  		//   return (false, memory)
   397  		// }
   398  		// MOV  $0, Rout
   399  		// LR	(Rarg0), Rtmp
   400  		// BNE	Rtmp, Rarg1, 3(PC)
   401  		// SC	Rarg2, (Rarg0), Rtmp
   402  		// BNE	Rtmp, ZERO, -3(PC)
   403  		// MOV  $1, Rout
   404  		{name: "LoweredAtomicCas32", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   405  		{name: "LoweredAtomicCas64", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
   406  
   407  		// Atomic 32 bit AND/OR.
   408  		// *arg0 &= (|=) arg1. arg2=mem. returns nil.
   409  		{name: "LoweredAtomicAnd32", argLength: 3, reg: gpatomic, asm: "AMOANDW", faultOnNilArg0: true, hasSideEffects: true},
   410  		{name: "LoweredAtomicOr32", argLength: 3, reg: gpatomic, asm: "AMOORW", faultOnNilArg0: true, hasSideEffects: true},
   411  
   412  		// Lowering pass-throughs
   413  		{name: "LoweredNilCheck", argLength: 2, faultOnNilArg0: true, nilCheck: true, reg: regInfo{inputs: []regMask{gpspMask}}}, // arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   414  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{regCtxt}}},                                                // scheduler ensures only at beginning of entry block
   415  
   416  		// LoweredGetCallerSP returns the SP of the caller of the current function. arg0=mem.
   417  		{name: "LoweredGetCallerSP", argLength: 1, reg: gp01, rematerializeable: true},
   418  
   419  		// LoweredGetCallerPC evaluates to the PC to which its "caller" will return.
   420  		// I.e., if f calls g "calls" sys.GetCallerPC,
   421  		// the result should be the PC within f that g will return to.
   422  		// See runtime/stubs.go for a more detailed discussion.
   423  		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},
   424  
   425  		// LoweredWB invokes runtime.gcWriteBarrier. arg0=mem, auxint=# of buffer entries needed
   426  		// It saves all GP registers if necessary,
   427  		// but clobbers RA (LR) because it's a call
   428  		// and T6 (REG_TMP).
   429  		// Returns a pointer to a write barrier buffer in X24.
   430  		{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ (gpMask | regNamed["g"])) | regNamed["X1"], outputs: []regMask{regNamed["X24"]}}, clobberFlags: true, aux: "Int64"},
   431  
   432  		// Do data barrier. arg0=memorys
   433  		{name: "LoweredPubBarrier", argLength: 1, asm: "FENCE", hasSideEffects: true},
   434  
   435  		// LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
   436  		// the RC and CR versions are used when one of the arguments is a constant. CC is used
   437  		// when both are constant (normally both 0, as prove derives the fact that a [0] bounds
   438  		// failure means the length must have also been 0).
   439  		// AuxInt contains a report code (see PanicBounds in genericOps.go).
   440  		{name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{first16Mask, first16Mask}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
   441  		{name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16Mask}}, typ: "Mem", call: true},       // arg0=x, arg1=mem, returns memory.
   442  		{name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{first16Mask}}, typ: "Mem", call: true},       // arg0=y, arg1=mem, returns memory.
   443  		{name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true},                                    // arg0=mem, returns memory.
   444  
   445  		// F extension.
   446  		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true, typ: "Float32"},                                           // arg0 + arg1
   447  		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS", commutative: false, typ: "Float32"},                                          // arg0 - arg1
   448  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, typ: "Float32"},                                           // arg0 * arg1
   449  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", commutative: false, typ: "Float32"},                                          // arg0 / arg1
   450  		{name: "FMADDS", argLength: 3, reg: fp31, asm: "FMADDS", commutative: true, typ: "Float32"},                                         // (arg0 * arg1) + arg2
   451  		{name: "FMSUBS", argLength: 3, reg: fp31, asm: "FMSUBS", commutative: true, typ: "Float32"},                                         // (arg0 * arg1) - arg2
   452  		{name: "FNMADDS", argLength: 3, reg: fp31, asm: "FNMADDS", commutative: true, typ: "Float32"},                                       // -(arg0 * arg1) + arg2
   453  		{name: "FNMSUBS", argLength: 3, reg: fp31, asm: "FNMSUBS", commutative: true, typ: "Float32"},                                       // -(arg0 * arg1) - arg2
   454  		{name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS", typ: "Float32"},                                                            // sqrt(arg0)
   455  		{name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS", typ: "Float32"},                                                              // -arg0
   456  		{name: "FMVSX", argLength: 1, reg: gpfp, asm: "FMVSX", typ: "Float32"},                                                              // reinterpret arg0 as float32
   457  		{name: "FMVXS", argLength: 1, reg: fpgp, asm: "FMVXS", typ: "Int32"},                                                                // reinterpret arg0 as int32, sign extended to 64 bits
   458  		{name: "FCVTSW", argLength: 1, reg: gpfp, asm: "FCVTSW", typ: "Float32"},                                                            // float32(low 32 bits of arg0)
   459  		{name: "FCVTSL", argLength: 1, reg: gpfp, asm: "FCVTSL", typ: "Float32"},                                                            // float32(arg0)
   460  		{name: "FCVTWS", argLength: 1, reg: fpgp, asm: "FCVTWS", typ: "Int32"},                                                              // int32(arg0)
   461  		{name: "FCVTLS", argLength: 1, reg: fpgp, asm: "FCVTLS", typ: "Int64"},                                                              // int64(arg0)
   462  		{name: "FMOVWload", argLength: 2, reg: fpload, asm: "MOVF", aux: "SymOff", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load float32 from arg0+auxint+aux
   463  		{name: "FMOVWstore", argLength: 3, reg: fpstore, asm: "MOVF", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},  // store float32 to arg0+auxint+aux
   464  		{name: "FEQS", argLength: 2, reg: fp2gp, asm: "FEQS", commutative: true},                                                            // arg0 == arg1
   465  		{name: "FNES", argLength: 2, reg: fp2gp, asm: "FNES", commutative: true},                                                            // arg0 != arg1
   466  		{name: "FLTS", argLength: 2, reg: fp2gp, asm: "FLTS"},                                                                               // arg0 < arg1
   467  		{name: "FLES", argLength: 2, reg: fp2gp, asm: "FLES"},                                                                               // arg0 <= arg1
   468  		{name: "LoweredFMAXS", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMAXS", commutative: true, typ: "Float32"},             // max(arg0, arg1)
   469  		{name: "LoweredFMINS", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMINS", commutative: true, typ: "Float32"},             // min(arg0, arg1)
   470  
   471  		// D extension.
   472  		{name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true, typ: "Float64"},                                           // arg0 + arg1
   473  		{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD", commutative: false, typ: "Float64"},                                          // arg0 - arg1
   474  		{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true, typ: "Float64"},                                           // arg0 * arg1
   475  		{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD", commutative: false, typ: "Float64"},                                          // arg0 / arg1
   476  		{name: "FMADDD", argLength: 3, reg: fp31, asm: "FMADDD", commutative: true, typ: "Float64"},                                         // (arg0 * arg1) + arg2
   477  		{name: "FMSUBD", argLength: 3, reg: fp31, asm: "FMSUBD", commutative: true, typ: "Float64"},                                         // (arg0 * arg1) - arg2
   478  		{name: "FNMADDD", argLength: 3, reg: fp31, asm: "FNMADDD", commutative: true, typ: "Float64"},                                       // -(arg0 * arg1) + arg2
   479  		{name: "FNMSUBD", argLength: 3, reg: fp31, asm: "FNMSUBD", commutative: true, typ: "Float64"},                                       // -(arg0 * arg1) - arg2
   480  		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", typ: "Float64"},                                                            // sqrt(arg0)
   481  		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", typ: "Float64"},                                                              // -arg0
   482  		{name: "FABSD", argLength: 1, reg: fp11, asm: "FABSD", typ: "Float64"},                                                              // abs(arg0)
   483  		{name: "FSGNJD", argLength: 2, reg: fp21, asm: "FSGNJD", typ: "Float64"},                                                            // copy sign of arg1 to arg0
   484  		{name: "FMVDX", argLength: 1, reg: gpfp, asm: "FMVDX", typ: "Float64"},                                                              // reinterpret arg0 as float64
   485  		{name: "FMVXD", argLength: 1, reg: fpgp, asm: "FMVXD", typ: "Int64"},                                                                // reinterpret arg0 as int64
   486  		{name: "FCVTDW", argLength: 1, reg: gpfp, asm: "FCVTDW", typ: "Float64"},                                                            // float64(low 32 bits of arg0)
   487  		{name: "FCVTDL", argLength: 1, reg: gpfp, asm: "FCVTDL", typ: "Float64"},                                                            // float64(arg0)
   488  		{name: "FCVTWD", argLength: 1, reg: fpgp, asm: "FCVTWD", typ: "Int32"},                                                              // int32(arg0)
   489  		{name: "FCVTLD", argLength: 1, reg: fpgp, asm: "FCVTLD", typ: "Int64"},                                                              // int64(arg0)
   490  		{name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS", typ: "Float64"},                                                            // float64(arg0)
   491  		{name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD", typ: "Float32"},                                                            // float32(arg0)
   492  		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "MOVD", aux: "SymOff", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load float64 from arg0+auxint+aux
   493  		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"},  // store float6 to arg0+auxint+aux
   494  		{name: "FEQD", argLength: 2, reg: fp2gp, asm: "FEQD", commutative: true},                                                            // arg0 == arg1
   495  		{name: "FNED", argLength: 2, reg: fp2gp, asm: "FNED", commutative: true},                                                            // arg0 != arg1
   496  		{name: "FLTD", argLength: 2, reg: fp2gp, asm: "FLTD"},                                                                               // arg0 < arg1
   497  		{name: "FLED", argLength: 2, reg: fp2gp, asm: "FLED"},                                                                               // arg0 <= arg1
   498  		{name: "LoweredFMIND", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMIND", commutative: true, typ: "Float64"},             // min(arg0, arg1)
   499  		{name: "LoweredFMAXD", argLength: 2, reg: fp21, resultNotInArgs: true, asm: "FMAXD", commutative: true, typ: "Float64"},             // max(arg0, arg1)
   500  	}
   501  
   502  	RISCV64blocks := []blockData{
   503  		{name: "BEQ", controls: 2},
   504  		{name: "BNE", controls: 2},
   505  		{name: "BLT", controls: 2},
   506  		{name: "BGE", controls: 2},
   507  		{name: "BLTU", controls: 2},
   508  		{name: "BGEU", controls: 2},
   509  
   510  		{name: "BEQZ", controls: 1},
   511  		{name: "BNEZ", controls: 1},
   512  		{name: "BLEZ", controls: 1},
   513  		{name: "BGEZ", controls: 1},
   514  		{name: "BLTZ", controls: 1},
   515  		{name: "BGTZ", controls: 1},
   516  	}
   517  
   518  	archs = append(archs, arch{
   519  		name:            "RISCV64",
   520  		pkg:             "cmd/internal/obj/riscv",
   521  		genfile:         "../../riscv64/ssa.go",
   522  		ops:             RISCV64ops,
   523  		blocks:          RISCV64blocks,
   524  		regnames:        regNamesRISCV64,
   525  		gpregmask:       gpMask,
   526  		fpregmask:       fpMask,
   527  		framepointerreg: -1, // not used
   528  		// Integer parameters passed in register X10-X17, X8-X9, X18-X23
   529  		ParamIntRegNames: "X10 X11 X12 X13 X14 X15 X16 X17 X8 X9 X18 X19 X20 X21 X22 X23",
   530  		// Float parameters passed in register F10-F17, F8-F9, F18-F23
   531  		ParamFloatRegNames: "F10 F11 F12 F13 F14 F15 F16 F17 F8 F9 F18 F19 F20 F21 F22 F23",
   532  	})
   533  }
   534  

View as plain text