Skip to content

Commit cad8e69

Browse files
authored
Narrow integer instructions (#628)
Closes #626. VM PRs: FuelLabs/fuel-vm#922 and FuelLabs/fuel-vm#923. Implementing correct overflow handling for narrow integer types (`u8`, `u16`, `u32`) is tedious, error prone, and needs lots of instructions. Likewise, memory load/store operations with these involve lots of expensive bitmasking. This change provides the functionality on the vm level. Only operations for which the overflow handling is different with narrow integers are implemented. For instance, multiplication is definitely different as `256 * 2` overflows `u8` but doesn't overflow `u64`. In comparison, division is not, it never creates results larger than the original value. The operations silently truncate the input operands to the correct bit width, meaning that `niop $ra $rb $rc NIOP_U8 | NIOP_ADD` with `$rb = 0xff01`, `$rc = 0x02` sets `$ra = 3` without panicking. Same goes for store operations, and that was already the case with `SB`. It's up to the compiler or user to take this into account. As always I'm trying to sneak in cool new features. Here it's the `XNOR` operating mode, which is extremely useful for generating bitmasks. For instance `niop $ra $zero $zero NIOP_U32 | NIOP_XNOR` sets `$ra` to `0xffffffff`. ### Before requesting review - [x] I have reviewed the code myself ### After merging, notify other teams - [ ] [Sway compiler](https://github.com/FuelLabs/sway/) - [ ] [Platform documentation](https://github.com/FuelLabs/devrel-requests/issues/new?assignees=&labels=new+request&projects=&template=NEW-REQUEST.yml&title=%5BRequest%5D%3A+) (for out-of-organization contributors, the person merging the PR will do this)
1 parent 6be5f5b commit cad8e69

File tree

2 files changed

+112
-2
lines changed

2 files changed

+112
-2
lines changed

spell-check-custom-words.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,4 +275,6 @@ incentivize
275275
EIPS
276276
eip
277277
eips
278-
ethereum
278+
ethereum
279+
xnor
280+
XNOR

src/fuel-vm/instruction-set.md

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- [`MUL`: Multiply](#mul-multiply)
2323
- [`MULI`: Multiply immediate](#muli-multiply-immediate)
2424
- [`MLDV`: Fused multiply-divide](#mldv-fused-multiply-divide)
25+
- [`NIOP`: Narrow integer operation](#niop-narrow-integer-operation)
2526
- [`NOOP`: No operation](#noop-no-operation)
2627
- [`NOT`: Invert](#not-invert)
2728
- [`OR`: OR](#or-or)
@@ -32,7 +33,6 @@
3233
- [`SRLI`: Shift right logical immediate](#srli-shift-right-logical-immediate)
3334
- [`SUB`: Subtract](#sub-subtract)
3435
- [`SUBI`: Subtract immediate](#subi-subtract-immediate)
35-
- [`SUBI`: Subtract immediate](#subi-subtract-immediate)
3636
- [`WDCM`: 128-bit integer comparison](#wdcm-128-bit-integer-comparison)
3737
- [`WQCM`: 256-bit integer comparison](#wqcm-256-bit-integer-comparison)
3838
- [`WDOP`: Misc 128-bit integer operations](#wdop-misc-128-bit-integer-operations)
@@ -69,6 +69,8 @@
6969
- [`CFS`: Shrink call frame](#cfs-shrink-call-frame)
7070
- [`CFSI`: Shrink call frame immediate](#cfsi-shrink-call-frame-immediate)
7171
- [`LB`: Load byte](#lb-load-byte)
72+
- [`LQW`: Load quarter word](#lqw-load-quarter-word)
73+
- [`LHW`: Load half word](#lhw-load-half-word)
7274
- [`LW`: Load word](#lw-load-word)
7375
- [`MCL`: Memory clear](#mcl-memory-clear)
7476
- [`MCLI`: Memory clear immediate](#mcli-memory-clear-immediate)
@@ -80,6 +82,8 @@
8082
- [`PSHH`: Push a set of high registers to stack](#pshh-push-a-set-of-high-registers-to-stack)
8183
- [`PSHL`: Push a set of low registers to stack](#pshl-push-a-set-of-low-registers-to-stack)
8284
- [`SB`: Store byte](#sb-store-byte)
85+
- [`SQW`: Store quarter word](#sqw-store-quarter-word)
86+
- [`SHW`: Store half word](#shw-store-half-word)
8387
- [`SW`: Store word](#sw-store-word)
8488
- [Contract Instructions](#contract-instructions)
8589
- [`BAL`: Balance of contract ID](#bal-balance-of-contract-id)
@@ -537,6 +541,55 @@ If the result of after the division doesn't fit into a register, `$of` is assign
537541

538542
`$err` is cleared.
539543

544+
### `NIOP`: Narrow integer operation
545+
546+
| | |
547+
|-------------|-------------------------------------------------------------------------------------|
548+
| Description | Perform an ALU operation with overflow handing for 8, 16 or 32 bit integers |
549+
| Operation | `$rA = op($rB,$rC);` |
550+
| Syntax | `niop $rA, $rB, $rC, imm` |
551+
| Encoding | `0x00 rA rB rC i` |
552+
| Notes | Operations that would be identical with their 64-bit counterparts are not available |
553+
554+
The six-bit immediate value is used to select operating mode, as follows:
555+
556+
Bits | Short name | Description
557+
---------|------------|-------------------------------------
558+
`..XXXX` | `op` | Operation selection, see below
559+
`XX....` | `width` | Operation width, see below
560+
561+
Then the actual operation that's performed:
562+
563+
`op` | Name | Description
564+
-----|-------|---------------------------
565+
0 | `add` | Add (`a = b + c`)
566+
1 | `mul` | Multiply (`a = b * c`)
567+
2 | `exp` | Exponentiate (`a = b ** c`)
568+
3 | `sll` | Bit shift left (logical) (`a = b << c`)
569+
4 | `xnor`| Bitwise xnor (`a = b ^ (!c)`).
570+
other| - | Reserved and must not be used
571+
572+
And operation width:
573+
574+
`width`| Bits
575+
-------|------
576+
0 | 8
577+
1 | 16
578+
2 | 32
579+
3 | Reserved and must not be used
580+
581+
All operations first truncate their operands to the given bit width,
582+
then perform the operation with overflow checking on that size. The
583+
result always fits within the bit width of the operation.
584+
585+
Operations set `$of` and `$err` similarly to their 64-bit counterparts.
586+
`XNOR` has no counterpart, and it always clears both `$of` and `$err`.
587+
588+
Panic if:
589+
590+
- Reserved bits of the immediate are set
591+
- `$rA` is a [reserved register](./index.md#semantics)
592+
540593
### `NOOP`: No operation
541594

542595
| | |
@@ -1383,6 +1436,36 @@ Panic if:
13831436
- `$rA` is a [reserved register](./index.md#semantics)
13841437
- `$rB + imm + 1` overflows or `> VM_MAX_RAM`
13851438

1439+
### `LQW`: Load quarter word
1440+
1441+
| | |
1442+
|-------------|----------------------------------------------------------------------|
1443+
| Description | A quarter word is loaded from the specified address offset by `imm`. |
1444+
| Operation | ```$rA = MEM[$rB + (imm * 2), 2];``` |
1445+
| Syntax | `lqw $rA, $rB, imm` |
1446+
| Encoding | `0x00 rA rB i i` |
1447+
| Notes | |
1448+
1449+
Panic if:
1450+
1451+
- `$rA` is a [reserved register](./index.md#semantics)
1452+
- `$rB + (imm * 2) + 2` overflows or `> VM_MAX_RAM`
1453+
1454+
### `LHW`: Load half word
1455+
1456+
| | |
1457+
|-------------|----------------------------------------------------------------------|
1458+
| Description | A half word is loaded from the specified address offset by `imm`. |
1459+
| Operation | ```$rA = MEM[$rB + (imm * 4), 4];``` |
1460+
| Syntax | `lhw $rA, $rB, imm` |
1461+
| Encoding | `0x00 rA rB i i` |
1462+
| Notes | |
1463+
1464+
Panic if:
1465+
1466+
- `$rA` is a [reserved register](./index.md#semantics)
1467+
- `$rB + (imm * 4) + 4` overflows or `> VM_MAX_RAM`
1468+
13861469
### `LW`: Load word
13871470

13881471
| | |
@@ -1565,6 +1648,31 @@ Panic if:
15651648
- `$rA + imm + 1` overflows or `> VM_MAX_RAM`
15661649
- The memory range `MEM[$rA + imm, 1]` does not pass [ownership check](./index.md#ownership)
15671650

1651+
### `SQW`: Store quarter word
1652+
1653+
| | |
1654+
|-------------|-----------------------------------------------------------------------------------------------|
1655+
| Description | The two least significant bytes of of `$rB` is stored at the address `$rA` offset by `imm`. |
1656+
| Operation | ```MEM[$rA + (imm * 2), 2] = $rB;``` |
1657+
| Syntax | `sqw $rA, $rB, imm` |
1658+
| Encoding | `0x00 rA rB i i` |
1659+
| Notes | |
1660+
1661+
### `SHW`: Store half word
1662+
1663+
| | |
1664+
|-------------|-----------------------------------------------------------------------------------------------|
1665+
| Description | The four least significant bytes of of `$rB` is stored at the address `$rA` offset by `imm`. |
1666+
| Operation | ```MEM[$rA + (imm * 4), 4] = $rB;``` |
1667+
| Syntax | `shw $rA, $rB, imm` |
1668+
| Encoding | `0x00 rA rB i i` |
1669+
| Notes | |
1670+
1671+
Panic if:
1672+
1673+
- `$rA + (imm * 4) + 4` overflows or `> VM_MAX_RAM`
1674+
- The memory range `MEM[$rA + (imm * 4), 4]` does not pass [ownership check](./index.md#ownership)
1675+
15681676
### `SW`: Store word
15691677

15701678
| | |

0 commit comments

Comments
 (0)