RISC-V Arithmetic Instructions I: Arithmetic Operations
Learning Outcomes
- Write assembly to perform arithmetic operations.
- Write arithmetic instructions involving immediates.
- Understand how pseudoinstructions and the zero register help balance a reduced instruction set with flexibility of operations.
🎥 Lecture Video: `add`/`sub`
🎥 Lecture Video: Immediates
add and sub Instructions
Generally, assembly instructions have a very rigid format. Consider arithmetic and logical instructions like addition (add), bitwise AND (and), etc., which operate on two registers and store the result in a third register. These instructions always follow the same rigid syntax shown in #fig-r-type-arithmetic:
R-Type instructions (arithmetic and logical involving two source registers).
The fields are separated by spaces1, in order:
opname: The operation namerd: The destination register, i.e., the operand to which we store the result of the operation.rs1: The “source1” register, i.e., the first source operand for the operation.rs2: The “source2” register, i.e., the second operand for the operation.
The full set of instructions that follow the format in #fig-r-type are located on the RISC-V green card. For now, we focus on two instructions in #tab-add-sub:
The addition command in assembly is simply add, which is easy to remember. The RV32I instruction
add x1, x2, x3
is equivalent to the C statement a = b + c; for 32-bit integers3 a, b, and c, where each variable corresponds to a value stored in a register. In #fig-rv32i-add, the variable a is in register x1, variable b is in register x2, and variable c is in x3.
Addition instruction add in RISC-V and C.
Subtraction works almost the same way, using the operation sub. If you want to subtract the values in x4 and x5 and store the result in x3, you would write the below, which is equivalent to the C integer arithmetic statement d = e - f; (@fig-rv32i-sub).
sub x3, x4, x5
Subtraction instruction sub in RISC-V and C.
RISC Arithmetic: Examples
As mentioned earlier, a single line of C may translate into several lines of RISC-V. Consider the C integer arithmetic statement:
a = b + c + d - e;
Suppose that variables a, b, c, d, and e mapped to registers x10, x1, x2, x3, and x4, respectively. We can use x10 as a running sum to compute the correct value of a after three instructions:
add x10 x1 x2 # a_temp = b + c
add x10 x10 x3 # a_temp = a_temp + d
sub x10 x10 x4 # a = a_temp - e
Show Answer
Both solutions are valid. Tradeoffs:
-
Approach A uses temporary registers
x5andx6and more directly follows the C order of operations. However, any existing data in registersx5andx6will now be overwritten. -
Approach B leverages algebra to avoid any temporary registers. For more complicated algebra (e.g., distributing a multiply), we may need more clever compilers.
Immediates
Immediates are numerical constants in RISC-V. Immediates are called as such because their bit patterns are directly encoded into the machine instruction—thus their values are “immediately” available to the computer.
Immediates appear often in code; hence, they have separate instructions (@fig-i-type-arithmetic).
Arithmetic and Logical I-Type instructions involving one source register and one immediate.
Like before, fields are separated by spaces1, in order:
opname: The operation namerd: The destination register, i.e., the operand to which we store the result of the operation.rs1: The “source1” register, i.e., the first source operand for the operation.imm: The immediate (numeric constant).
The full set of instructions that follow the format in #fig-i-type-arithmetic are located on the RISC-V green card.
addi instruction
Let’s discuss the Add Immediate instruction (@tab-addi).
The RV32I instruction
addi x3, x4, 10
is equivalent to the C statement f = g + 10;, where f and g are 32-bit integers (@fig-rv32i-addi).
Add immediate instruction in RISC-V and C.
::::{warning} No subi instruction!
Recall the “R” in RISC is Reduced. If an operation can be decomposed into equivalent or simpler operations, don’t include it in the ISA.
RISC-V Immediates are signed. There is therefore no “subtract immediate” instruction–there is just addi. The instruction
addi x3 x4 -10
is equivalent to the C statement f = g - 10; where f and g are 32-bit integers (@fig-rv32i-subi).
Add immediate instruction in RISC-V and C with negative values.
Using x0 to reduce our instruction set
We have previously discussed the zero register x0. Hardwiring register x0 to the value zero proves extremely useful for reusing add and addi for very common C operations.
Two toy examples are shown in #fig-rv32i-x0-mv and #fig-rv32i-x0-li:
To assign the C integer variable f to the value of another integer variable g, we could use add with x0 as a source operand (though we don’t in practice4).
To assign the variable f to a numeric constant, use addi with x0 as the source register operand.
Pseudoinstructions
We have just seen several cases where common C statements translate into other instructions in our reduced instruction set architecture. While this is fine and dandy for designing architecture, compilers (and you, as a human assembly instruction-writer) will find it useful to use pseudoinstructions.
Pseudoinstructions are convenient instructions in RISC-V. Pseudoinstructions help compilers more directly translate higher-level language code into assembly instructions (really!), but by themselves they are not real instructions.
Consider two examples below (and see the full set in the RISC-V green card).
When assembling to machine instructions, the assembler replaces pseudoinstructions with their real instruction counterpart.
Footnotes
-
Stylistically, assembly instructions can include commas. In RISC-V, commas are not needed. We stick to a space-only convention in this course. ↩ ↩2
-
We won’t expect you to know Verilog in this course, but the syntax is useful.
R[rs1]means the data in registerrs1. ↩ ↩2 -
You will see later that signed and unsigned addition and subtraction are implemented with the same hardware circuit. ↩ ↩2
-
Why
addiwith immediate0and notaddwithx0(as in #fig-rv32i-x0-mv)? See the ASM manual on GitHub. ↩ ↩2 -
This description is incomplete given the range of
imminaddi. See the green-card and a later section for the full translation of load immediates. ↩ -
We will see later how a “no-op” instruction can improve hardware performance (really). ↩