rbytecode

Modern R now executes most code in a stack-based virtual machine (VM).

The R code you write is first parsed to an abstract syntax tree, which is then compiled to bytecode, and this bytecode is then executed in R’s bytecode VM.

{rbytecode} provides an assembler and disassembler for R bytecode - allowing you to peek under the hood at what R is doing to execute your code.

The disassembler is a way of inspecting R code internals by dissecting existing code.

The assembler is a way of compiling R bytecode assembly directly into a bytecode object i.e. writing directly in the virtual machine language understood internally by R’s bytecode VM.

This work is heavily based around the {compiler} package (built-in to R) and Luke Tierney’s document A Byte Code Compiler for R

This package has a book!

I have written a book to accompany this package. The book gives some background on R’s execution of code in its stack-based virtual machine and a reference for the different bytecode instructions understood by this VM.

Available from:

Ideas for the Future

What’s in the box

Installation

You can install from GitHub with:

# install.package('remotes')
# remotes::install_github('coolbutuseless/rbytecode')
install.packages("rbytecode", repos = c('https://coolbutuseless.r-universe.dev', 'https://cloud.r-project.org'))

Disassembler

library(rbytecode)

disq(x + 1) |> 
  as.character()
GETVAR x
LDCONST 1
ADD 
RETURN 
disq(function(x, y = 1) {x + y}) |> 
  as.character()
MAKECLOSURE x; y = 1
  GETVAR x
  GETVAR y
  ADD 
  RETURN 
ENDMAKECLOSURE 
RETURN 

Assembler - simple example

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Create bytecode assembly
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
code <- r"(
LDCONST 1
LDCONST 2
ADD
RETURN
)"

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Assemble the code into a bytecode object
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bc <- asm(code)
bc
<bytecode: 0x1307dd978>
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Evaluate the bytecode
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eval(bc)
[1] 3

Bytecode Assembly - Fibonacci

The following bytecode assembly is a reimplementation of R code for calculating the 11th Fibonacci number.

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# R implementation
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
i   <- 0
fn2 <- 0
fn1 <- 1
while (i < 10) {
  fnnew <- fn1 + fn2
  fn2   <- fn1
  fn1   <- fnnew
  i     <- i + 1
}
fn1
[1] 89
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Bytecode implementation
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
code <- r"(
LDCONST 0L
SETVAR i
SETVAR fn2
LDCONST 1L
SETVAR fn1
@start 
GETVAR i
LDCONST 10L
LT 
BRIFNOT @end
GETVAR fn1
GETVAR fn2
ADD 
SETVAR fnnew
POP 
GETVAR fn1
SETVAR fn2
POP 
GETVAR fnnew
SETVAR fn1
POP 
GETVAR i
LDCONST 1L
ADD 
SETVAR i
POP 
GOTO @start
@end 
GETVAR fn1
RETURN 
)"

asm(code) |> eval()
[1] 89

Related Software

Acknowledgements