TurboVM
TurboVM is a 64-byte fixed-format bytecode VM for parsing structured
log lines. Two ISA-specific dispatchers (SSE 4.2 on x86-64, NEON
on aarch64) share one bytecode contract. It runs inside
liblognorm and powers the parsing stage of our
rsyslog port.
Why
liblognorm’s scalar parser is adequate until the
collector also runs WAKE. TurboVM compiles a rulebase ahead of time
into a frozen instruction stream and executes it with SIMD literal
matching, so parse cost is no longer the bottleneck.
- Fixed 64-byte instruction
- Every opcode is the same size, cache-line sized, with a 60-byte payload carried inline rather than through a separate literal pool: one fetch, no second indirection. Control flow is a relative jump plus a fork stack for alternatives. The bytecode contract is frozen so the userland compiler and the VM stay in sync.
- Dual ISA code paths
- One interpreter, with SIMD leaf helpers built per target: SSE 4.2 on x86-64, NEON on aarch64, and a scalar fallback.
PCMPESTRIand its NEON equivalent do character-class and whitespace scanning; literal comparison ismemcmp. Selection is at compile time, so a binary built without-msse4.2stays on the scalar path whatever the CPU supports. Same bytecode either way. - Where the time goes
- The bottleneck moves off parsing onto I/O, entity scoring, and downstream emission.
Instruction format
/* 64 bytes, cache-line sized. Payload is inline, not a pool offset. */ typedef struct { uint8_t op; /* Opcode */ uint8_t flags; /* Flags */ uint16_t aux; /* Auxiliary (length, etc.) */ union { char str[60]; /* Inline literal or name */ uint8_t bytes[60]; struct { /* Jump / fork target */ int32_t offset; /* Relative instruction offset */ int32_t _pad[14]; } jump; struct { /* Field with delimiter */ char name[56]; uint8_t delim; uint8_t ass; /* name-value-list assignator */ uint8_t ignore_ws; uint8_t _pad[1]; } char_to; } data; } ln_instr_t; _Static_assert(sizeof(ln_instr_t) == LN_INSTR_SIZE, "Instruction must be 64 bytes");
Upstream
TurboVM isn’t a fork. It ships as patches against the canonical
upstream projects, integrated into MarlinOS’s
devel/liblognorm and sysutils/rsyslog8 ports.
The pieces that fit upstream go upstream.
| Where | Status |
|---|---|
liblognorm PR #400 | Merged upstream. |
liblognorm PR #403 | Merged upstream. |
rsyslog follow-on work | Ongoing. |
rsyslog PR #6667 | Merged upstream. |