About Compilers
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.