github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/cmd/compile/internal/ssa/gen/RISCVOps.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 "cmd/internal/obj/riscv"
    10  
    11  func init() {
    12  	var regNamesRISCV []string
    13  	var gpMask, fpMask, gpspMask, gpspsbMask regMask
    14  	regNamed := make(map[string]regMask)
    15  
    16  	// Build the list of register names, creating an appropriately indexed
    17  	// regMask for the gp and fp registers as we go.
    18  	//
    19  	// If name is specified, use it rather than the riscv reg number.
    20  	addreg := func(r int, name string) regMask {
    21  		mask := regMask(1) << uint(len(regNamesRISCV))
    22  		if name == "" {
    23  			name = riscv.RegNames[int16(r)]
    24  		}
    25  		regNamesRISCV = append(regNamesRISCV, name)
    26  		regNamed[name] = mask
    27  		return mask
    28  	}
    29  
    30  	// General purpose registers.
    31  	for r := riscv.REG_X0; r <= riscv.REG_X31; r++ {
    32  		if r == riscv.REG_RA {
    33  			// RA is not used by regalloc, so we skip it to leave
    34  			// room for pseudo-register SB.
    35  			continue
    36  		}
    37  
    38  		mask := addreg(r, "")
    39  
    40  		// Add general purpose registers to gpMask.
    41  		switch r {
    42  		// ZERO, g, and TMP are not in any gp mask.
    43  		case riscv.REG_ZERO, riscv.REG_G, riscv.REG_TMP:
    44  		case riscv.REG_SP:
    45  			gpspMask |= mask
    46  			gpspsbMask |= mask
    47  		default:
    48  			gpMask |= mask
    49  			gpspMask |= mask
    50  			gpspsbMask |= mask
    51  		}
    52  	}
    53  
    54  	// Floating pointer registers.
    55  	for r := riscv.REG_F0; r <= riscv.REG_F31; r++ {
    56  		mask := addreg(r, "")
    57  		fpMask |= mask
    58  	}
    59  
    60  	// Pseudo-register: SB
    61  	mask := addreg(-1, "SB")
    62  	gpspsbMask |= mask
    63  
    64  	if len(regNamesRISCV) > 64 {
    65  		// regMask is only 64 bits.
    66  		panic("Too many RISCV registers")
    67  	}
    68  
    69  	regCtxt := regNamed["CTXT"]
    70  	callerSave := gpMask | fpMask | regNamed["g"]
    71  
    72  	var (
    73  		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
    74  		gp01    = regInfo{outputs: []regMask{gpMask}}
    75  		gp11    = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{gpMask}}
    76  		gp21    = regInfo{inputs: []regMask{gpMask, gpMask}, outputs: []regMask{gpMask}}
    77  		gpload  = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{gpMask}}
    78  		gp11sb  = regInfo{inputs: []regMask{gpspsbMask}, outputs: []regMask{gpMask}}
    79  
    80  		fp11    = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{fpMask}}
    81  		fp21    = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{fpMask}}
    82  		gpfp    = regInfo{inputs: []regMask{gpMask}, outputs: []regMask{fpMask}}
    83  		fpgp    = regInfo{inputs: []regMask{fpMask}, outputs: []regMask{gpMask}}
    84  		fpstore = regInfo{inputs: []regMask{gpspsbMask, fpMask, 0}}
    85  		fpload  = regInfo{inputs: []regMask{gpspsbMask, 0}, outputs: []regMask{fpMask}}
    86  		fp2gp   = regInfo{inputs: []regMask{fpMask, fpMask}, outputs: []regMask{gpMask}}
    87  
    88  		call        = regInfo{clobbers: callerSave}
    89  		callClosure = regInfo{inputs: []regMask{gpspMask, regCtxt, 0}, clobbers: callerSave}
    90  		callInter   = regInfo{inputs: []regMask{gpMask}, clobbers: callerSave}
    91  	)
    92  
    93  	RISCVops := []opData{
    94  		{name: "ADD", argLength: 2, reg: gp21, asm: "ADD", commutative: true}, // arg0 + arg1
    95  		{name: "ADDI", argLength: 1, reg: gp11sb, asm: "ADDI", aux: "Int64"},  // arg0 + auxint
    96  		{name: "SUB", argLength: 2, reg: gp21, asm: "SUB"},                    // arg0 - arg1
    97  
    98  		// M extension. H means high (i.e., it returns the top bits of
    99  		// the result). U means unsigned. W means word (i.e., 32-bit).
   100  		{name: "MUL", argLength: 2, reg: gp21, asm: "MUL", commutative: true, typ: "Int64"}, // arg0 * arg1
   101  		{name: "MULW", argLength: 2, reg: gp21, asm: "MULW", commutative: true, typ: "Int32"},
   102  		{name: "MULH", argLength: 2, reg: gp21, asm: "MULH", commutative: true, typ: "Int64"},
   103  		{name: "MULHU", argLength: 2, reg: gp21, asm: "MULHU", commutative: true, typ: "UInt64"},
   104  		{name: "DIV", argLength: 2, reg: gp21, asm: "DIV", typ: "Int64"}, // arg0 / arg1
   105  		{name: "DIVU", argLength: 2, reg: gp21, asm: "DIVU", typ: "UInt64"},
   106  		{name: "DIVW", argLength: 2, reg: gp21, asm: "DIVW", typ: "Int32"},
   107  		{name: "DIVUW", argLength: 2, reg: gp21, asm: "DIVUW", typ: "UInt32"},
   108  		{name: "REM", argLength: 2, reg: gp21, asm: "REM", typ: "Int64"}, // arg0 % arg1
   109  		{name: "REMU", argLength: 2, reg: gp21, asm: "REMU", typ: "UInt64"},
   110  		{name: "REMW", argLength: 2, reg: gp21, asm: "REMW", typ: "Int32"},
   111  		{name: "REMUW", argLength: 2, reg: gp21, asm: "REMUW", typ: "UInt32"},
   112  
   113  		{name: "MOVaddr", argLength: 1, reg: gp11sb, asm: "MOV", aux: "SymOff", rematerializeable: true}, // arg0 + auxint + offset encoded in aux
   114  		// auxint+aux == add auxint and the offset of the symbol in aux (if any) to the effective address
   115  
   116  		{name: "MOVBconst", reg: gp01, asm: "MOV", typ: "UInt8", aux: "Int8", rematerializeable: true},    // 8 low bits of auxint
   117  		{name: "MOVHconst", reg: gp01, asm: "MOV", typ: "UInt16", aux: "Int16", rematerializeable: true},  // 16 low bits of auxint
   118  		{name: "MOVWconst", reg: gp01, asm: "MOV", typ: "UInt32", aux: "Int32", rematerializeable: true},  // 32 low bits of auxint
   119  		{name: "MOVDconst", reg: gp01, asm: "MOV", typ: "UInt64", aux: "Int64", rematerializeable: true},  // auxint
   120  		{name: "MOVSconst", reg: gp01, asm: "MOV", typ: "Float32", aux: "Int32", rematerializeable: true}, // auxint as float
   121  
   122  		// Loads: load <size> bits from arg0+auxint+aux and extend to 64 bits; arg1=mem
   123  		{name: "MOVBload", argLength: 2, reg: gpload, asm: "MOVB", aux: "SymOff", typ: "Int8", faultOnNilArg0: true},     //  8 bits, sign extend
   124  		{name: "MOVHload", argLength: 2, reg: gpload, asm: "MOVH", aux: "SymOff", typ: "Int16", faultOnNilArg0: true},    // 16 bits, sign extend
   125  		{name: "MOVWload", argLength: 2, reg: gpload, asm: "MOVW", aux: "SymOff", typ: "Int32", faultOnNilArg0: true},    // 32 bits, sign extend
   126  		{name: "MOVDload", argLength: 2, reg: gpload, asm: "MOV", aux: "SymOff", typ: "Int64", faultOnNilArg0: true},     // 64 bits
   127  		{name: "MOVBUload", argLength: 2, reg: gpload, asm: "MOVBU", aux: "SymOff", typ: "UInt8", faultOnNilArg0: true},  //  8 bits, zero extend
   128  		{name: "MOVHUload", argLength: 2, reg: gpload, asm: "MOVHU", aux: "SymOff", typ: "UInt16", faultOnNilArg0: true}, // 16 bits, zero extend
   129  		{name: "MOVWUload", argLength: 2, reg: gpload, asm: "MOVWU", aux: "SymOff", typ: "UInt32", faultOnNilArg0: true}, // 32 bits, zero extend
   130  
   131  		// Stores: store <size> lowest bits in arg1 to arg0+auxint+aux; arg2=mem
   132  		{name: "MOVBstore", argLength: 3, reg: gpstore, asm: "MOVB", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, //  8 bits
   133  		{name: "MOVHstore", argLength: 3, reg: gpstore, asm: "MOVH", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // 16 bits
   134  		{name: "MOVWstore", argLength: 3, reg: gpstore, asm: "MOVW", aux: "SymOff", typ: "Mem", faultOnNilArg0: true}, // 32 bits
   135  		{name: "MOVDstore", argLength: 3, reg: gpstore, asm: "MOV", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},  // 64 bits
   136  
   137  		// Shift ops
   138  		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                 // arg0 << aux1
   139  		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                 // arg0 >> aux1, signed
   140  		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                 // arg0 >> aux1, unsigned
   141  		{name: "SLLI", argLength: 1, reg: gp11, asm: "SLLI", aux: "Int64"}, // arg0 << auxint
   142  		{name: "SRAI", argLength: 1, reg: gp11, asm: "SRAI", aux: "Int64"}, // arg0 >> auxint, signed
   143  		{name: "SRLI", argLength: 1, reg: gp11, asm: "SRLI", aux: "Int64"}, // arg0 >> auxint, unsigned
   144  
   145  		// Bitwise ops
   146  		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true}, // arg0 ^ arg1
   147  		{name: "XORI", argLength: 1, reg: gp11, asm: "XORI", aux: "Int64"},    // arg0 ^ auxint
   148  		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},   // arg0 | arg1
   149  		{name: "ORI", argLength: 1, reg: gp11, asm: "ORI", aux: "Int64"},      // arg0 | auxint
   150  		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true}, // arg0 & arg1
   151  		{name: "ANDI", argLength: 1, reg: gp11, asm: "ANDI", aux: "Int64"},    // arg0 & auxint
   152  
   153  		// Generate boolean values
   154  		{name: "SEQZ", argLength: 1, reg: gp11, asm: "SEQZ"},                 // arg0 == 0, result is 0 or 1
   155  		{name: "SNEZ", argLength: 1, reg: gp11, asm: "SNEZ"},                 // arg0 != 0, result is 0 or 1
   156  		{name: "SLT", argLength: 2, reg: gp21, asm: "SLT"},                   // arg0 < arg1, result is 0 or 1
   157  		{name: "SLTI", argLength: 1, reg: gp11, asm: "SLTI", aux: "Int64"},   // arg0 < auxint, result is 0 or 1
   158  		{name: "SLTU", argLength: 2, reg: gp21, asm: "SLTU"},                 // arg0 < arg1, unsigned, result is 0 or 1
   159  		{name: "SLTIU", argLength: 1, reg: gp11, asm: "SLTIU", aux: "Int64"}, // arg0 < auxint, unsigned, result is 0 or 1
   160  
   161  		// MOVconvert converts between pointers and integers.
   162  		// We have a special op for this so as to not confuse GC
   163  		// (particularly stack maps). It takes a memory arg so it
   164  		// gets correctly ordered with respect to GC safepoints.
   165  		{name: "MOVconvert", argLength: 2, reg: gp11, asm: "MOV"}, // arg0, but converted to int/ptr as appropriate; arg1=mem
   166  
   167  		// Calls
   168  		{name: "CALLstatic", argLength: 1, reg: call, aux: "SymOff", call: true},        // call static function aux.(*gc.Sym). arg0=mem, auxint=argsize, returns mem
   169  		{name: "CALLclosure", argLength: 3, reg: callClosure, aux: "Int64", call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem
   170  		{name: "CALLdefer", argLength: 1, reg: call, aux: "Int64", call: true},          // call deferproc. arg0=mem, auxint=argsize, returns mem
   171  		{name: "CALLgo", argLength: 1, reg: call, aux: "Int64", call: true},             // call newproc. arg0=mem, auxint=argsize, returns mem
   172  		{name: "CALLinter", argLength: 2, reg: callInter, aux: "Int64", call: true},     // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem
   173  
   174  		// Generic moves and zeros
   175  
   176  		// general unaligned zeroing
   177  		// arg0 = address of memory to zero (in T0, changed as side effect)
   178  		// arg1 = address of the last element to zero
   179  		// arg2 = mem
   180  		// auxint = alignment
   181  		// returns mem
   182  		//	mov	ZERO, (T0)
   183  		//	ADD	$sz, T0
   184  		//	BNE	Rarg1, T0, -2(PC)
   185  		{
   186  			name:      "LoweredZero",
   187  			aux:       "Int64",
   188  			argLength: 3,
   189  			reg: regInfo{
   190  				inputs:   []regMask{regNamed["T0"], gpMask},
   191  				clobbers: regNamed["T0"],
   192  			},
   193  			typ:            "Mem",
   194  			faultOnNilArg0: true,
   195  		},
   196  
   197  		// general unaligned move
   198  		// arg0 = address of dst memory (in T0, changed as side effect)
   199  		// arg1 = address of src memory (in T1, changed as side effect)
   200  		// arg2 = address of the last element of src (can't be T2 as we clobber it before using arg2)
   201  		// arg3 = mem
   202  		// auxint = alignment
   203  		// clobbers T2 as a tmp register.
   204  		// returns mem
   205  		//	mov	(T1), T2
   206  		//	mov	T2, (T0)
   207  		//	ADD	$sz, T0
   208  		//	ADD	$sz, T1
   209  		//	BNE	Rarg2, T0, -4(PC)
   210  		{
   211  			name:      "LoweredMove",
   212  			aux:       "Int64",
   213  			argLength: 4,
   214  			reg: regInfo{
   215  				inputs:   []regMask{regNamed["T0"], regNamed["T1"], gpMask &^ regNamed["T2"]},
   216  				clobbers: regNamed["T0"] | regNamed["T1"] | regNamed["T2"],
   217  			},
   218  			typ:            "Mem",
   219  			faultOnNilArg0: true,
   220  			faultOnNilArg1: true,
   221  		},
   222  
   223  		// Lowering pass-throughs
   224  		{name: "LoweredNilCheck", argLength: 2, faultOnNilArg0: true, nilCheck: true, reg: regInfo{inputs: []regMask{gpspMask}}}, // arg0=ptr,arg1=mem, returns void.  Faults if ptr is nil.
   225  		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{regCtxt}}},                                                // scheduler ensures only at beginning of entry block
   226  
   227  		// F extension.
   228  		{name: "FADDS", argLength: 2, reg: fp21, asm: "FADDS", commutative: true, typ: "Float32"},                        // arg0 + arg1
   229  		{name: "FSUBS", argLength: 2, reg: fp21, asm: "FSUBS", commutative: false, typ: "Float32"},                       // arg0 - arg1
   230  		{name: "FMULS", argLength: 2, reg: fp21, asm: "FMULS", commutative: true, typ: "Float32"},                        // arg0 * arg1
   231  		{name: "FDIVS", argLength: 2, reg: fp21, asm: "FDIVS", commutative: false, typ: "Float32"},                       // arg0 / arg1
   232  		{name: "FSQRTS", argLength: 1, reg: fp11, asm: "FSQRTS", typ: "Float32"},                                         // sqrt(arg0)
   233  		{name: "FNEGS", argLength: 1, reg: fp11, asm: "FNEGS", typ: "Float32"},                                           // -arg0
   234  		{name: "FMVSX", argLength: 1, reg: gpfp, asm: "FMVSX", typ: "Float32"},                                           // reinterpret arg0 as float
   235  		{name: "FCVTSW", argLength: 1, reg: gpfp, asm: "FCVTSW", typ: "Float32"},                                         // float32(arg0)
   236  		{name: "FCVTSL", argLength: 1, reg: gpfp, asm: "FCVTSL", typ: "Float32"},                                         // float32(arg0)
   237  		{name: "FCVTWS", argLength: 1, reg: fpgp, asm: "FCVTWS", typ: "Int32"},                                           // int32(arg0)
   238  		{name: "FCVTLS", argLength: 1, reg: fpgp, asm: "FCVTLS", typ: "Int64"},                                           // int64(arg0)
   239  		{name: "FMOVWload", argLength: 2, reg: fpload, asm: "MOVF", aux: "SymOff", typ: "Float32", faultOnNilArg0: true}, // load float32 from arg0+auxint+aux
   240  		{name: "FMOVWstore", argLength: 3, reg: fpstore, asm: "MOVF", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},   // store float32 to arg0+auxint+aux
   241  		{name: "FEQS", argLength: 2, reg: fp2gp, asm: "FEQS", commutative: true},                                         // arg0 == arg1
   242  		{name: "FNES", argLength: 2, reg: fp2gp, asm: "FNES", commutative: true},                                         // arg0 != arg1
   243  		{name: "FLTS", argLength: 2, reg: fp2gp, asm: "FLTS"},                                                            // arg0 < arg1
   244  		{name: "FLES", argLength: 2, reg: fp2gp, asm: "FLES"},                                                            // arg0 <= arg1
   245  
   246  		// D extension.
   247  		{name: "FADDD", argLength: 2, reg: fp21, asm: "FADDD", commutative: true, typ: "Float64"},                        // arg0 + arg1
   248  		{name: "FSUBD", argLength: 2, reg: fp21, asm: "FSUBD", commutative: false, typ: "Float64"},                       // arg0 - arg1
   249  		{name: "FMULD", argLength: 2, reg: fp21, asm: "FMULD", commutative: true, typ: "Float64"},                        // arg0 * arg1
   250  		{name: "FDIVD", argLength: 2, reg: fp21, asm: "FDIVD", commutative: false, typ: "Float64"},                       // arg0 / arg1
   251  		{name: "FSQRTD", argLength: 1, reg: fp11, asm: "FSQRTD", typ: "Float64"},                                         // sqrt(arg0)
   252  		{name: "FNEGD", argLength: 1, reg: fp11, asm: "FNEGD", typ: "Float64"},                                           // -arg0
   253  		{name: "FMVDX", argLength: 1, reg: gpfp, asm: "FMVDX", typ: "Float64"},                                           // reinterpret arg0 as float
   254  		{name: "FCVTDW", argLength: 1, reg: gpfp, asm: "FCVTDW", typ: "Float64"},                                         // float64(arg0)
   255  		{name: "FCVTDL", argLength: 1, reg: gpfp, asm: "FCVTDL", typ: "Float64"},                                         // float64(arg0)
   256  		{name: "FCVTWD", argLength: 1, reg: fpgp, asm: "FCVTWD", typ: "Int32"},                                           // int32(arg0)
   257  		{name: "FCVTLD", argLength: 1, reg: fpgp, asm: "FCVTLD", typ: "Int64"},                                           // int64(arg0)
   258  		{name: "FCVTDS", argLength: 1, reg: fp11, asm: "FCVTDS", typ: "Float64"},                                         // float64(arg0)
   259  		{name: "FCVTSD", argLength: 1, reg: fp11, asm: "FCVTSD", typ: "Float32"},                                         // float32(arg0)
   260  		{name: "FMOVDload", argLength: 2, reg: fpload, asm: "MOVD", aux: "SymOff", typ: "Float64", faultOnNilArg0: true}, // load float64 from arg0+auxint+aux
   261  		{name: "FMOVDstore", argLength: 3, reg: fpstore, asm: "MOVD", aux: "SymOff", typ: "Mem", faultOnNilArg0: true},   // store float6 to arg0+auxint+aux
   262  		{name: "FEQD", argLength: 2, reg: fp2gp, asm: "FEQD", commutative: true},                                         // arg0 == arg1
   263  		{name: "FNED", argLength: 2, reg: fp2gp, asm: "FNED", commutative: true},                                         // arg0 != arg1
   264  		{name: "FLTD", argLength: 2, reg: fp2gp, asm: "FLTD"},                                                            // arg0 < arg1
   265  		{name: "FLED", argLength: 2, reg: fp2gp, asm: "FLED"},                                                            // arg0 <= arg1
   266  	}
   267  
   268  	RISCVblocks := []blockData{
   269  		{name: "BNE"}, // Control != 0 (take a register)
   270  	}
   271  
   272  	archs = append(archs, arch{
   273  		name:            "RISCV",
   274  		pkg:             "cmd/internal/obj/riscv",
   275  		genfile:         "../../riscv/ssa.go",
   276  		ops:             RISCVops,
   277  		blocks:          RISCVblocks,
   278  		regnames:        regNamesRISCV,
   279  		gpregmask:       gpMask,
   280  		fpregmask:       fpMask,
   281  		framepointerreg: -1, // not used
   282  	})
   283  }