SBPF arithmetics improvements
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 (v2) 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
MULinstruction (opcodes0x24,0x2C,0x27and0x2F) - the
DIVinstruction (opcodes0x34,0x3C,0x37and0x3F) - the
MODinstruction (opcodes0x94,0x9C,0x97and0x9F) - the
NEGinstruction (opcodes0x84and0x87)
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
UHMUL64instruction (opcode0x36and0x3E) - the
UDIV32instruction (opcode0x46and0x4E) - the
UDIV64instruction (opcode0x56and0x5E) - the
UREM32instruction (opcode0x66and0x6E) - the
UREM64instruction (opcode0x76and0x7E) - the
LMUL32instruction (opcode0x86and0x8E) - the
LMUL64instruction (opcode0x96and0x9E) - the
SHMUL64instruction (opcode0xB6and0xBE) - the
SDIV32instruction (opcode0xC6and0xCE) - the
SDIV64instruction (opcode0xD6and0xDE) - the
SREM32instruction (opcode0xE6and0xEE) - the
SREM64instruction (opcode0xF6and0xFE)
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
UHMUL64instruction (opcode0x36and0x3E) produces the 64 MSBs of the product of an unsigned 64 x 64 bit multiplication (dst * immanddst * src). - the
UDIV32instruction (opcode0x46and0x4E) produces the quotient of an unsigned 32 bit division (dst / immanddst / src). - the
UDIV64instruction (opcode0x56and0x5E) produces the quotient of an unsigned 64 bit division (dst / immanddst / src). - the
UREM32instruction (opcode0x66and0x6E) produces the remainder of an unsigned 64 bit division (dst % immanddst % src). - the
UREM64instruction (opcode0x76and0x7E) produces the remainder of an unsigned 64 bit division (dst % immanddst % src). - the
LMUL32instruction (opcode0x86and0x8E) produces the 32 LSBs of the product of any 32 x 32 bit multiplication (dst * immanddst * src). - the
LMUL64instruction (opcode0x96and0x9E) produces the 64 LSBs of the product of any 64 x 64 bit multiplication (dst * immanddst * src). - the
SHMUL64instruction (opcode0xB6and0xBE) produces the 64 MSBs of the product of a signed 64 x 64 bit multiplication (dst * immanddst * src). - the
SDIV32instruction (opcode0xC6and0xCE) produces the quotient of a signed 32 bit division (dst / immanddst / src). - the
SDIV64instruction (opcode0xD6and0xDE) produces the quotient of a signed 64 bit division (dst / immanddst / src). - the
SREM32instruction (opcode0xE6and0xEE) produces the remainder of a signed 32 bit division (dst / immanddst / src). - the
SREM64instruction (opcode0xF6and0xFE) produces the remainder of a signed 64 bit division (dst / immanddst / src).
Runtime exceptions are:
- If the divisor (
immorsrc) of any division is zero,EbpfError::DivideByZeromust be thrown. - If the dividend (
dst) of a signed division has the minimal value for its bit-width and the divisor (immorsrc) is-1, thenEbpfError::DivideOverflowmust 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
ADD32instruction (opcode0x04and0x0C) - the
SUB32instruction (opcode0x14and0x1C) - all of the new 32 bit PQR instructions:
UDIV32,UREM32,SDIV32,SREM32andLMUL32
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.