About Compilers

Author

Ken Pu

1 Review: Interpreter runtime environment

Previously, we developed the runtime backend as a collection of classes corresponding to different programming constructs.

Consider the if-else-then construct. It is implemented as:

class Ifelse (
  val cond: Expr,
  val trueExpr: Expr,
  val falseExpr: Expr? = null
): Expr() {
  override fun eval(scope: Runtime): Data {
    ...
  }
}

An instance of if-then-else code would be equivalent in constructing a Ifelse object, and invoke its eval.

Ifelse(
  condExpr,
  trueExpr,
  falseExpr,
)
.eval(scope)

We can create an interpreted programming language using SDD and data attributes.

expr returns [Expr expr]
  : ifElse      { $expr = ifElse.expr; }
  | while       { $expr = while.expr; }
  | deref       { $expr = deref.expr; }
  | funcDecl    { ... }
  | funcInvoke  { ... }
  | ...
  ;
  
ifElse returns [Ifelse expr]
  : 'if' '(' cond=expr ')'
    '{' trueExpr=expr '}'
    ('else' '{' falseExpr=expr '}')?
    {
      $expr = new Ifelse(
        cond.expr,
        trueExpr.expr,
        falseExpr.expr
      );
    }
  ;

2 High-performance Runtime

x86 assembly
  • direct access to CPU, registers and memory
  • assembly code
  • best possible high performance
  • not portable across different hardware architectures
LLVM
  • abstraction of low-level hardware to register based architecture
  • advanced optimization toolchain
  • high overhead to get started
  • not portable across different hardware, but easy to transcompile, with no dependencies.
JVM
  • abstraction of low-level hardware to stack machine
  • advanced (automatic) code optimization
  • low overhead to get started
  • portable across different hardware, but requires JVM installed.

3 Compiled Languages