| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| /* |
| Package riscv implements the riscv64 assembler. |
| |
| # Register naming |
| |
| The integer registers are named X0 through to X31, however X4 must be accessed |
| through its RISC-V ABI name, TP, and X27, which holds a pointer to the Go |
| routine structure, must be referred to as g. Additionally, when building in |
| shared mode, X3 is unavailable and must be accessed via its RISC-V ABI name, |
| GP. |
| |
| The floating-point registers are named F0 through to F31. |
| |
| The vector registers are named V0 through to V31. |
| |
| Both integer and floating-point registers can be referred to by their RISC-V |
| ABI names, e.g., A0 or FT0, with the exception that X27 cannot be referred to |
| by its RISC-V ABI name, S11. It must be referred to as g. |
| |
| Some of the integer registers are used by the Go runtime and assembler - X26 is |
| the closure pointer, X27 points to the Go routine structure and X31 is a |
| temporary register used by the Go assembler. Use of X31 should be avoided in |
| hand written assembly code as its value could be altered by the instruction |
| sequences emitted by the assembler. |
| |
| # Instruction naming |
| |
| Many RISC-V instructions contain one or more suffixes in their names. In the |
| [RISC-V ISA Manual] these suffixes are separated from themselves and the |
| name of the instruction mnemonic with a dot ('.'). In the Go assembler, the |
| separators are omitted and the suffixes are written in upper case. |
| |
| Example: |
| |
| FMVWX <=> fmv.w.x |
| |
| # Rounding modes |
| |
| The Go toolchain does not set the FCSR register and requires the desired |
| rounding mode to be explicitly encoded within floating-point instructions. |
| The syntax the Go assembler uses to specify the rounding modes differs |
| from the syntax in the RISC-V specifications. In the [RISC-V ISA Manual] |
| the rounding mode is given as an extra operand at the end of an |
| assembly language instruction. In the Go assembler, the rounding modes are |
| converted to uppercase and follow the instruction mnemonic from which they |
| are separated with a dot ('.'). |
| |
| Example: |
| |
| FCVTLUS.RNE F0, X5 <=> fcvt.lu.s x5, f0, rne |
| |
| RTZ is assumed if the rounding mode is omitted. |
| |
| # RISC-V extensions |
| |
| By default the Go compiler targets the [rva20u64] profile. This profile mandates |
| all the general RISC-V instructions, allowing Go to use integer, multiplication, |
| division, floating-point and atomic instructions without having to |
| perform compile time or runtime checks to verify that their use is appropriate |
| for the target hardware. All widely available riscv64 devices support at least |
| [rva20u64]. The Go toolchain can be instructed to target later RISC-V profiles, |
| including, [rva22u64] and [rva23u64], via the GORISCV64 environment variable. |
| Instructions that are provided by newer profiles cannot typically be used in |
| handwritten assembly code without compile time guards (or runtime checks) |
| that ensure they are hardware supported. |
| |
| The file asm_riscv64.h defines macros for each RISC-V extension that is enabled |
| by setting the GORISCV64 environment variable to a value other than [rva20u64]. |
| For example, if GORISCV64=rva22u64 the macros hasZba, hasZbb and hasZbs will be |
| defined. If GORISCV64=rva23u64 hasV will be defined in addition to hasZba, |
| hasZbb and hasZbs. These macros can be used to determine whether it's safe |
| to use an instruction in hand-written assembly. |
| |
| It is not always necessary to include asm_riscv64.h and use #ifdefs in your |
| code to safely take advantage of instructions present in the [rva22u64] |
| profile. In some cases the assembler can generate [rva20u64] compatible code |
| even when an [rva22u64] instruction is used in an assembly source file. When |
| GORISCV64=rva20u64 the assembler will synthesize certain [rva22u64] |
| instructions, e.g., ANDN, using multiple [rva20u64] instructions. Instructions |
| such as ANDN can then be freely used in assembly code without checking to see |
| whether the instruction is supported by the target profile. When building a |
| source file containing the ANDN instruction with GORISCV64=rva22u64 the |
| assembler will emit the Zbb ANDN instruction directly. When building the same |
| source file with GORISCV64=rva20u64 the assembler will emit multiple [rva20u64] |
| instructions to synthesize ANDN. |
| |
| The assembler will also use [rva22u64] instructions to implement the zero and |
| sign extension instructions, e.g., MOVB and MOVHU, when GORISCV64=rva22u64 or |
| greater. |
| |
| The instructions not implemented in the default profile ([rva20u64]) that can |
| be safely used in assembly code without compile time checks are: |
| |
| - ANDN |
| - MAX |
| - MAXU |
| - MIN |
| - MINU |
| - MOVB |
| - MOVH |
| - MOVHU |
| - MOVWU |
| - ORN |
| - ROL |
| - ROLW |
| - ROR |
| - RORI |
| - RORIW |
| - RORW |
| - XNOR |
| |
| # Operand ordering |
| |
| The ordering used for instruction operands in the Go assembler differs from the |
| ordering defined in the [RISC-V ISA Manual]. |
| |
| 1. R-Type instructions |
| |
| R-Type instructions are written in the reverse order to that given in the |
| [RISC-V ISA Manual], with the register order being rs2, rs1, rd. |
| |
| Examples: |
| |
| ADD X10, X11, X12 <=> add x12, x11, x10 |
| FADDD F10, F11, F12 <=> fadd.d f12, f11, f10 |
| |
| 2. I-Type arithmetic instructions |
| |
| I-Type arithmetic instructions (not loads, fences, ebreak, ecall) use the same |
| ordering as the R-Type instructions, typically, imm12, rs1, rd. |
| |
| Examples: |
| |
| ADDI $1, X11, X12 <=> add x12, x11, 1 |
| SLTI $1, X11, X12 <=> slti x12, x11, 1 |
| |
| 3. Loads and Stores |
| |
| Load instructions are written with the source operand (whether it be a register |
| or a memory address), first followed by the destination operand. |
| |
| Examples: |
| |
| MOV 16(X2), X10 <=> ld x10, 16(x2) |
| MOV X10, (X2) <=> sd x10, 0(x2) |
| |
| 4. Branch instructions |
| |
| The branch instructions use the same operand ordering as is given in the |
| [RISC-V ISA Manual], e.g., rs1, rs2, label. |
| |
| Example: |
| |
| BLT X12, X23, loop1 <=> blt x12, x23, loop1 |
| |
| BLT X12, X23, label will jump to label if X12 < X23. Note this is not the |
| same ordering as is used for the SLT instructions. |
| |
| 5. FMA instructions |
| |
| The Go assembler uses a different ordering for the RISC-V FMA operands to |
| the ordering given in the [RISC-V ISA Manual]. The operands are rotated one |
| place to the left, so that the destination operand comes last. |
| |
| Example: |
| |
| FMADDS F1, F2, F3, F4 <=> fmadd.s f4, f1, f2, f3 |
| |
| 6. AMO instructions |
| |
| The ordering used for the AMO operations is rs2, rs1, rd, i.e., the operands |
| as specified in the [RISC-V ISA Manual] are rotated one place to the left. |
| |
| Example: |
| |
| AMOSWAPW X5, (X6), X7 <=> amoswap.w x7, x5, (x6) |
| |
| 7. Vector instructions |
| |
| The VSETVLI instruction uses the same symbolic names as the [RISC-V ISA Manual] |
| to represent the components of vtype, with the exception |
| that they are written in upper case. The ordering of the operands in the Go |
| assembler differs from the [RISC-V ISA Manual] in that the operands are |
| rotated one place to the left so that the destination register, the register |
| that holds the new vl, is the last operand. |
| |
| Example: |
| |
| VSETVLI X10, E8, M1, TU, MU, X12 <=> vsetvli x12, x10, e8, m1, tu, mu |
| |
| Vector load and store instructions follow the pattern set by scalar loads and |
| stores, i.e., the source is always the first operand and the destination the |
| last. However, the ordering of the operands of these instructions is |
| complicated by the optional mask register and, in some cases, the use of an |
| additional stride or index register. In the Go assembler the index and stride |
| registers appear as the second operand in indexed or strided loads and stores, |
| while the mask register, if present, is always the penultimate operand. |
| |
| Examples: |
| |
| VLE8V (X10), V3 <=> vle8.v v3, (x10) |
| VSE8V V3, (X10) <=> vse8.v v3, (x10) |
| VLE8V (X10), V0, V3 <=> vle8.v v3, (x10), v0.t |
| VSE8V V3, V0, (X10) <=> vse8.v v3, (x10), v0.t |
| VLSE8V (X10), X11, V3 <=> vlse8.v v3, (x10), x11 |
| VSSE8V V3, X11, (X10) <=> vsse8.v v3, (x10), x11 |
| VLSE8V (X10), X11, V0, V3 <=> vlse8.v v3, (x10), x11, v0.t |
| VSSE8V V3, X11, V0, (X10) <=> vsse8.v v3, (x10), x11, v0.t |
| VLUXEI8V (X10), V2, V3 <=> vluxei8.v v3, (x10), v2 |
| VSUXEI8V V3, V2, (X10) <=> vsuxei8.v v3, (x10), v2 |
| VLUXEI8V (X10), V2, V0, V3 <=> vluxei8.v v3, (x10), v2, v0.t |
| VSUXEI8V V3, V2, V0, (X10) <=> vsuxei8.v v3, (x10), v2, v0.t |
| VL1RE8V (X10), V3 <=> vl1re8.v v3, (x10) |
| VS1RV V3, (X11) <=> vs1r.v v3, (x11) |
| |
| The ordering of operands for two and three argument vector arithmetic instructions is |
| reversed in the Go assembler. |
| |
| Examples: |
| |
| VMVVV V2, V3 <=> vmv.v.v v3, v2 |
| VADDVV V1, V2, V3 <=> vadd.vv v3, v2, v1 |
| VADDVX X10, V2, V3 <=> vadd.vx v3, v2, x10 |
| VMADCVI $15, V2, V3 <=> vmadc.vi v3, v2, 15 |
| |
| The mask register, when specified, is always the penultimate operand in a vector |
| arithmetic instruction, appearing before the destination register. |
| |
| Examples: |
| |
| VANDVV V1, V2, V0, V3 <=> vand.vv v3, v2, v1, v0.t |
| |
| # Ternary instructions |
| |
| The Go assembler allows the second operand to be omitted from most ternary |
| instructions if it matches the third (destination) operand. |
| |
| Examples: |
| |
| ADD X10, X12, X12 <=> ADD X10, X12 |
| ANDI $3, X12, X12 <=> ANDI $3, X12 |
| |
| The use of this abbreviated syntax is encouraged. |
| |
| # Ordering of atomic instructions |
| |
| It is not possible to specify the ordering bits in the FENCE, LR, SC or AMO |
| instructions. The FENCE instruction is always emitted as a full fence, the |
| acquire and release bits are always set for the AMO instructions, the acquire |
| bit is always set for the LR instructions while the release bit is set for |
| the SC instructions. |
| |
| # Immediate operands |
| |
| In many cases, where an R-Type instruction has a corresponding I-Type |
| instruction, the R-Type mnemonic can be used in place of the I-Type mnemonic. |
| The assembler assumes that the immediate form of the instruction was intended |
| when the first operand is given as an immediate value rather than a register. |
| |
| Example: |
| |
| AND $3, X12, X13 <=> ANDI $3, X12, X13 |
| |
| # Integer constant materialization |
| |
| The MOV instruction can be used to set a register to the value of any 64 bit |
| constant literal. The way this is achieved by the assembler varies depending |
| on the value of the constant. Where possible the assembler will synthesize the |
| constant using one or more RISC-V arithmetic instructions. If it is unable |
| to easily materialize the constant it will load the 64 bit literal from memory. |
| |
| A 32 bit constant literal can be specified as an argument to ADDI, ANDI, ORI and |
| XORI. If the specified literal does not fit into 12 bits the assembler will |
| generate extra instructions to synthesize it. |
| |
| Integer constants provided as operands to all other instructions must fit into |
| the number of bits allowed by the instructions' encodings for immediate values. |
| Otherwise, an error will be generated. |
| |
| # Floating point constant materialization |
| |
| The MOVF and MOVD instructions can be used to set a register to the value |
| of any 32 bit or 64 bit floating point constant literal, respectively. Unless |
| the constant literal is 0.0, MOVF and MOVD will be encoded as FLW and FLD |
| instructions that load the constant from a location within the program's |
| binary. |
| |
| # Compressed instructions |
| |
| The Go assembler converts 32 bit RISC-V instructions to compressed |
| instructions when generating machine code. This conversion happens |
| automatically without the need for any direct involvement from the programmer, |
| although judicious choice of registers can improve the compression rate for |
| certain instructions (see the [RISC-V ISA Manual] for more details). This |
| behaviour is enabled by default for all of the supported RISC-V profiles, i.e., |
| it is not affected by the value of the GORISCV64 environment variable. |
| |
| The use of compressed instructions can be disabled via a debug flag, |
| compressinstructions: |
| |
| - Use -gcflags=all=-d=compressinstructions=0 to disable compressed |
| instructions in Go code. |
| - Use -asmflags=all=-d=compressinstructions=0 to disable compressed |
| instructions in assembly code. |
| |
| To completely disable automatic instruction compression in a Go binary both |
| options must be specified. |
| |
| The assembler also permits the use of compressed instructions in hand coded |
| assembly language, but this should generally be avoided. Note that the |
| compressinstructions flag only prevents the automatic compression of 32 |
| bit instructions. It has no effect on compressed instructions that are |
| hand coded directly into an assembly file. |
| |
| [RISC-V ISA Manual]: https://github.com/riscv/riscv-isa-manual |
| [rva20u64]: https://github.com/riscv/riscv-profiles/blob/main/src/profiles.adoc#51-rva20u64-profile |
| [rva22u64]: https://github.com/riscv/riscv-profiles/blob/main/src/profiles.adoc#rva22u64-profile |
| [rva23u64]: https://github.com/riscv/riscv-profiles/blob/main/src/rva23-profile.adoc#rva23u64-profile |
| */ |
| package riscv |