diff --git a/proposals/0174-sbpf-arithmetics-improvements.md b/proposals/0174-sbpf-arithmetics-improvements.md new file mode 100644 index 00000000..3e9ce325 --- /dev/null +++ b/proposals/0174-sbpf-arithmetics-improvements.md @@ -0,0 +1,154 @@ +--- +simd: '0174' +title: SBPF arithmetics improvements +authors: + - Alexander Meißner +category: Standard +type: Core +status: Draft +created: 2024-09-06 +feature: TBD +extends: SIMD-0161 +--- + +## Summary + +This proposal introduces wide multiplication, signed division and explicit sign +extension to SBPF. + +## Motivation + +All major hardware ISAs support 64 x 64 = 128 bit multiplication; BPF does not. +This hurts performance of big integer arithmetics. Similarly, signed division +and signed remainder / modulo instructions must be emulated in software as +well. + +Another issue related to arithmetics is that some 32 bit instructions perform +sign extension of their results (output, **not** input, there is a difference) +implicitly. This is first of all useless because source languages do not read +the 32 MSBs of a 32 bit result stored in a 64 bit register. And second, it +requires interpreters and compilers to perform extra work to add sign extension +at the end of these instructions. Instead we should go the same route as the +underlying hardware ISAs and require sign extension to be made explicit in a +dedicated instruction. + +Furthermore, the instruction `sub dst, imm` is redundant as it can also be +encoded as `add dst, -imm`. If we were to swap the operands meaning of minuend +and subtrahend, then the instruction would become useful and would even render +`neg dst` redundant. Negation would then be encoded as `reg = 0 - reg`. This +would also make it clear how sign extension rules work for negation. + +## Alternatives Considered + +None. + +## New Terminology + +None. + +## Detailed Design + +The following must go into effect if and only if a program indicates the +SBPF-version (TBD) or higher in its program header (see SIMD-0161). + +### Changes to the Bytecode Verifier + +A program containing one of the following instructions must throw +`VerifierError::UnknownOpCode` during verification: + +- the `MUL` instruction (opcodes `0x24`, `0x2C`, `0x27` and `0x2F`) +- the `DIV` instruction (opcodes `0x34`, `0x3C`, `0x37` and `0x3F`) +- the `MOD` instruction (opcodes `0x94`, `0x9C`, `0x97` and `0x9F`) +- the `NEG` instruction (opcodes `0x84` and `0x87`) + +A program containing one of the following instructions must **not** throw +`VerifierError::UnknownOpCode` during verification anymore (opcodes are for +immediate and register variant each, in that order): + +- the `UHMUL64` instruction (opcode `0x36` and `0x3E`) +- the `UDIV32` instruction (opcode `0x46` and `0x4E`) +- the `UDIV64` instruction (opcode `0x56` and `0x5E`) +- the `UREM32` instruction (opcode `0x66` and `0x6E`) +- the `UREM64` instruction (opcode `0x76` and `0x7E`) +- the `LMUL32` instruction (opcode `0x86` and `0x8E`) +- the `LMUL64` instruction (opcode `0x96` and `0x9E`) +- the `SHMUL64` instruction (opcode `0xB6` and `0xBE`) +- the `SDIV32` instruction (opcode `0xC6` and `0xCE`) +- the `SDIV64` instruction (opcode `0xD6` and `0xDE`) +- the `SREM32` instruction (opcode `0xE6` and `0xEE`) +- the `SREM64` instruction (opcode `0xF6` and `0xFE`) + +The verification rule, that an immediate divisor must not be zero or else +`VerifierError::DivisionByZero` is thrown, is moved to the new division like +instructions: `UDIV32_IMM`, `UDIV64_IMM`, `UREM32_IMM`, `UREM64_IMM`, +`SDIV32_IMM`, `SDIV64_IMM`, `SREM32_IMM`, `SREM64_IMM`. + +### Changes to Execution + +#### PQR Instruction Class + +A new instruction class product, quotient and remainder (PQR) is introduced: + +- the `UHMUL64` instruction (opcode `0x36` and `0x3E`) produces the 64 MSBs of +the product of an unsigned 64 x 64 bit multiplication (`dst * imm` and +`dst * src`). +- the `UDIV32` instruction (opcode `0x46` and `0x4E`) produces the quotient of +an unsigned 32 bit division (`dst / imm` and `dst / src`). +- the `UDIV64` instruction (opcode `0x56` and `0x5E`) produces the quotient of +an unsigned 64 bit division (`dst / imm` and `dst / src`). +- the `UREM32` instruction (opcode `0x66` and `0x6E`) produces the remainder of +an unsigned 64 bit division (`dst % imm` and `dst % src`). +- the `UREM64` instruction (opcode `0x76` and `0x7E`) produces the remainder of +an unsigned 64 bit division (`dst % imm` and `dst % src`). +- the `LMUL32` instruction (opcode `0x86` and `0x8E`) produces the 32 LSBs of +the product of any 32 x 32 bit multiplication (`dst * imm` and `dst * src`). +- the `LMUL64` instruction (opcode `0x96` and `0x9E`) produces the 64 LSBs of +the product of any 64 x 64 bit multiplication (`dst * imm` and `dst * src`). +- the `SHMUL64` instruction (opcode `0xB6` and `0xBE`) produces the 64 MSBs of +the product of a signed 64 x 64 bit multiplication (`dst * imm` and +`dst * src`). +- the `SDIV32` instruction (opcode `0xC6` and `0xCE`) produces the quotient of +a signed 32 bit division (`dst / imm` and `dst / src`). +- the `SDIV64` instruction (opcode `0xD6` and `0xDE`) produces the quotient of +a signed 64 bit division (`dst / imm` and `dst / src`). +- the `SREM32` instruction (opcode `0xE6` and `0xEE`) produces the remainder of +a signed 32 bit division (`dst / imm` and `dst / src`). +- the `SREM64` instruction (opcode `0xF6` and `0xFE`) produces the remainder of +a signed 64 bit division (`dst / imm` and `dst / src`). + +Runtime exceptions are: + +- If the divisor (`imm` or `src`) of any division is zero, +`EbpfError::DivideByZero` must be thrown. +- If the dividend (`dst`) of a signed division has the minimal value for its +bit-width and the divisor (`imm` or `src`) is `-1`, then +`EbpfError::DivideOverflow` must be thrown. + +#### Explicit Sign Extension + +The following instructions must stop performing implicit sign extension of +their results, instead filling the 32 MSBs of `dst` with zeros: + +- the `ADD32` instruction (opcode `0x04` and `0x0C`) +- the `SUB32` instruction (opcode `0x14` and `0x1C`) +- all of the new 32 bit PQR instructions: `UDIV32`, `UREM32`, `SDIV32`, +`SREM32` and `LMUL32` + +Instead the `MOV32_REG` instruction (opcode `0xBC`) which until now did zero +out the 32 MSBs, must now perform sign extension in the 32 MSBs. Meaning this +instruction becomes the explicit sign extension operation. + +#### Register Immediate Subtraction + +The operands roles of `SUB32_IMM` (opcode `0x14`) and `SUB64_IMM` (opcode +`0x17`) must be swapped: Until now the resulting difference was `src - imm` and +it must be changed to `imm - src`. + +## Impact + +The toolchain will emit machine-code according to the selected SBPF version. +Big integer arithmetics and signed divisions will become cheaper CU wise. + +## Security Considerations + +None.