Building constants in RISC-V ============================ [Published 2019-11-24, Updated 2020-01-01] No instruction in RISC-V is currently longer than 32 bits. Therefore you need to use multiple instructions if you want to put a big number in a register (unless you read it from e.g. memory). The instructions you will need ------------------------------ Some common instructions for putting values in registers are: addi rd, rs1, imm // rd = rs1 + imm (imm is 12 bits) lui rd, imm // rd = imm << 12 (imm is 20 bits) slli rd, rs1, imm // rd = rs1 << imm That seems easy. Just fill the upper 20 bits using lui and the lower 12 bits using addi and then you can shift the whole thing up a bit using slli if you want to fill in more bits. No, it's not always that easy. Sign extension -------------- The lui instruction doesn't just write the specified value shifted 12 bits on 64-bit RISC-V (but on 32-bit it does). It also copies the most significant bit and repeats it in the more significant bits. This is called sign extension. The addi instruction does sign extension too (on the immediate value but not on the result). In this case it's very useful because that allows you to add negative numbers. In the case of the lui instruction it doesn't make much sense. This instruction is mostly used to set the upper 20 bits of a number, as far as I know. Sign extension in this case doesn't help at all. It causes problems. So this means that you have to do some tricks if the most significant bits in the immediate values are 1. The tricks ---------- In the case of addi when you want to add a number that has the bit with value 1<<11 set (imm[11]), the number will be seen as a negative number since this is the most significant bit (the sign bit). Bits imm[10:0] will still be added as if nothing happened; the difference is that the value represented by imm[11] (1<<11) will be subtracted instead of added. You have to compensate for this by adding an extra 1<<12 if imm[11] is 1, both to remove the subtraction of 1<<11, and to add the 1<<11 that comes from the value of bit imm[11]. In the case of lui there is no simple little trick you can do because the sign extension happens on the whole result at the end. This destroys all higher bits (rd[32] and above) that you may have put there earlier. One way to deal with this is to never put a 1 in the most significant bit (imm[19]) and just fill in the lower bits and repeatedly shift the bits up. This creates more instructions than what would be needed if lui did not sign extend. Another way that makes fewer instructions but can be even slower to execute is to simply load the value from memory instead of building it with instructions (this is what gcc seems to do).