# 64-bit changes Main points of 64-bit: - Registers are now 64-bit. - Some of the instructions now operate on 64-bits only. Some of the instructions stay as 32-bit (ignoring the upper 32-bits of the register) but unconditionally sign-extend the result to 64-bit, and have a separate 64-bit variant added. - If an instruction writes to a register then it always overwrites all 64-bits (there are no instructions that do partial register updates like there are on 64-bit x86). - When accessing memory the upper 32-bits of addresses are always ignored. - Operand encoding stays the same; physical immediate encoding is still at most 32-bits; when a 64-bit value is needed the immediate is sign extended to 64-bit. - Since some instructions now come in 32-bit and 64-bit variants it'd be a good idea to have `32` and `64` in their names to clearly disambiguate between them (as opposed to keeping one without any number and only e.g. the 64-bit one with 64). ## A.5.3. Instructions with Arguments of Two Immediates. Changes to existing instructions: - `store_imm_u32` - ignores upper 32-bits of the register New instructions: - `store_imm_u64` - sign-extends the immediate to 64-bit and stores it in memory ## A.5.5. Instructions with Arguments of One Register & One Immediate. Changes to existing instructions: - `load_imm` - the immediate value (which can be at most 32-bit due to our instruction encoding) is sign extended to 64-bit and loaded into the register - `load_i8` - is sign extended to 64-bit now - `load_i16` - is sign extended to 64-bit now - `store_u32` - ignores upper 32-bits of the register (add $mod\ 2^{32}$ to the equation) New instructions: - `load_i32` - reads a 32-bit value, sign extends it to 64-bit and loads it into a register - `load_u64` - reads a 64-bit value and loads it into a register - `store_u64` - stores the full 64-bit register in memory Opcode changes: - swap `load_i32` and `load_u32`'s opcodes (why: the raw instruction stays semantically the same wrt arithmetic instructions, due to RISC-V's convention of always sign extending 32-bit values) ## A.5.6. Instructions with Arguments of One Register & Two Immediates. New instructions: - `store_imm_ind_u64` - sign-extends the immediate to 64-bit and stores it in memory ## A.5.7. Instructions with Arguments of One Register, One Immediate and One Offset. Changes to existing instructions: - `load_imm_jump` - similar to `load_imm`, should sign extend the value to 64-bit before loading it into register - `branch_*_imm` - sign extend the immediate to full 64-bit before comparison, signed comparisons are now 64-bit ## A.5.9. Instructions with Arguments of Two Registers & One Immediate. Changes to existing instructions: - `load_ind_i8` - is sign extended to 64-bit now - `load_ind_i16` - is sign extended to 64-bit now - `add_imm`, `shlo_l_imm`, `neg_add_imm`, `shlo_l_imm_alt`, `shlo_r_imm_alt`, `shar_r_imm_alt` - the result is sign extended to 64-bit, add `32` to the name - `and_imm`, `xor_imm`, `or_imm` - the immediate is sign-extended to 64-bit, and the bitwise operation is applied to the 64-bit value from the register - `mul_imm` - the result is sign extended to 64-bit (after being truncated to 32-bits), rename to `mul32_imm` - `set_*_*_imm` - the immediate is sign extended to 64-bit, signed comparisons are now 64-bit - `shlo_r_imm`, `shar_r_imm` - the register operand is truncated to 32-bits (so that no upper 32-bits get right-shifted into the lower 32-bits), the result is sign extended to 64-bit, add `32` to the names - `cmov_iz_imm`, `cmov_nz_imm` - the immediate is sign extended to 64-bit New instructions: - `add_imm_64` - the immediate is sign extended into 64-bits, added to the value of the register, and the result is $\text{mod}\ 2^{64}$ - `mul_imm_64` - the immediate is sign extended into 64-bits, multiplied with the value of the register, and the result is $\text{mod}\ 2^{64}$ - `shlo_l_imm_64`, `shlo_r_imm_64`, `shar_r_imm_64` - 64-bit shifts, the shift amount in $v_{x}$ is now $\text{mod}\ 64$ - `neg_add_imm_64` - the immediate is sign extended into 64-bits, the operation is 64-bit - `shlo_l_imm_alt_64`, `shlo_r_imm_alt_64`, `shar_r_imm_alt_64` - the immediate is sign extended into 64-bits, the shift amount is now $\text{mod}\ 64$, the result is 64-bit - `load_ind_i32` - reads a 32-bit value, sign extends it to 64-bit and loads it into a register - `load_ind_u64` - reads a 64-bit value and loads it into a register - `store_ind_u64` - stores the full 64-bit register in memory Deleted instructions: - `mul_upper_s_s_imm`, `mul_upper_u_u_imm` - for 32-bit these did 64-bit multiplies so they had still seen some use, but for 64-bit the `mul_upper` opcodes now do 128-bit multiply (and grab the upper 64-bits) which is rare enough that it's probably not worth it to have a dedicated instruction to multiply with an immediate Opcode changes: - Swap opcodes of `load_ind_u32` and `load_ind_i32` ## A.5.10. Instructions with Arguments of Two Registers & One Offset. Changes to existing instructions: - `branch_lt_s`, `branch_ge_s` - comparisons are now 64-bit ## A.5.11. Instruction with Arguments of Two Registers and Two Immediates. Changes to existing instructions: - `load_imm_jump_ind` - similar to `load_imm`, should sign extend the value to 64-bit before loading it into register ## A.5.12. Instructions with Arguments of Three Registers. Changes to existing instructions: - `add`, `sub`, `mul`, `div_u`, `div_s`, `rem_u`, `rem_s`, `shlo_l`, `shlo_r`, `shar_r` - sign extend the resulting value to 64-bits, rename the instructions to have `32` in the name - `and` - instruction now operates on 64-bits - `xor` - instruction now operates on 64-bits - `or` - instruction now operates on 64-bits - `set_lt_s` - the comparison is now 64-bit - `mul_upper_s_s` - the instruction now returns the upper 64-bits of a signed x signed 128-bit multiply (where previously it returned the upper 32-bits of a signed x signed 64-bit multiply) - `mul_upper_u_u`, `mul_upper_s_u` - similar change as `mul_upper_s_s` New instructions: - `add_64`, `sub_64`, `mul_64`, `div_u_64`, `div_s_64`, `rem_u_64`, `rem_s_64`, `shlo_l_64`, `shlo_r_64`, `shar_r_64` - 64-bit variants of existing instructions --------------- Remaining changes to the instruction set until its feature complete (I'd propose to add this as the last final update after the 64-bit update; I'll add this as soon as possible once I get the 64-bit actually fully implemented): - `load_imm64` - a load immediate instruction in the instruction set which takes a full 64-bit immediate without sign extension; will need a dedicated "instructions with arguments of one extended width immediate" or something like that - the `Zbb` RISC-V extension (various bitmanipulation instructions; Alex wants this for accelerated Ethereum compatibility) - `memcpy`, `memset` (essentially instructions to accelerate copying memory and clearing memory; for WASM I've measured up to a 20% performance improvement in certain use cases when the Polkadot runtime was compiled with these enabled for WASM) - opcode reordering? Originally I've picked the opcode numbers (except for trap) to be ordered according to how frequently a given instruction is used to make instruction decoders a little bit more icache friendly in certain cases (because I had to pick *some* order, so why not make it the most efficient one); obviously after significant instruction set changes the order gets messed up. In general this is just a "nice to have" and not a huge deal if we don't do this, but if we do then it makes sense to do it as the very last thing once all instructions are in.