Code Generation �and Optimization
Translation
fun(y) {
x = y - 2;
};
function {
local_vars = [y, x],
constants = [None, 2],
instructions = [
load_local 0
load_const 1
sub
store_local 1
load_const 0
return
]
}
function __function_1 (y) : {
%x = alloca int64_t
%0 = %y
%1 = 2
%2 = sub %0 %1
store %x %2
%3 = 0
return %3
}
__function_1 :
sub 8 %rsp
mov %rsp %rbp
mov (%rbp, 2, 8) %rax
mov 2 %rcx
sub %rcx %rax
mov %rax (%rbp)
mov 0 %rax
ret
High-Level
VM
Low-Level
VM
Machine Code
Language
Why?
Why? Performance.
The Translation Task
Registers
Control
CPU
ALU
fun(y) {
x = y - 2;
};
Language
Machine
Sources of Performance Issues
Your Interpreter (Likely)
void interpret(Function *func) {
Frame f = ...; OperandStack s;
Instruction* inst = func->instructions().begin();
while(inst != func->instructions().end()) {
switch(inst->operation()) {
case Operation::LoadLocal:
s.push(f.locals[inst->operand0]); break;
case Operation::LoadConst:
s.push(f.constants[inst->operand0]); break;
case Operation::Sub:
Integer* op2 = s.pop_int();
Integer* op1 = s.pop_int();
auto* result = new Integer(op1->value - op2->value);
s.push(result); break;
...
}
++inst;
}}
Performance Issues
Pre-Evaluate Control Flow
void execute_0() {
Frame f = ...;
push(f.locals[0]);
push(f.constants[1]);
Integer* op2 = pop_int(stack);
Integer* op1 = pop_int(stack);
auto* result = new Integer(op1->value - op2->value);
push(result);
Value* op3 = pop(stack);
f.locals[1] = op3;
push(f.constants[0]);
Value* op4 = pop(stack);
return op4;
}
function {
local_vars = [y, x],
constants = [None, 2],
instructions = [
load_local 0
load_const 1
sub
store_local 1
load_const 0
return
]
}
But that’s not all…
__execute_0 :
push %rdi
call assert_integer
pop %rdi
mov %rdi %rax
shr $3 %rax
mov $2 %rcx
sub %rcx %rax
shl $3 %rax
or $1 %rax
ret
void execute_0() {
Frame f = ...;
push(f.locals[0]);
push(f.constants[1]);
Integer* op2 = pop_int(stack);
Integer* op1 = pop_int(stack);
auto* result = new Integer(op1->value - op2->value);
push(result);
Value* op3 = pop(stack);
f.locals[1] = op3;
push(f.constants[0]);
Value* op4 = pop(stack);
return op4;
}
Instructions: > 1000s -> 9
Function Calls: 10s -> 1
Memory Accesses: 100s -> 2
Allocations: > 1 -> 0
How? Many optimizations
When?
Language Implementations
Interpreted
Interpreted
Swift
LLVM
EVM
Java
Python VM
Ethereum
Java VM
x86
ARM
SPARC
PowerPC
Language
High-Level
VM
Machine Code
C/C++ (Clang)
Low-Level
VM
JavaScript
C/C++ (gcc)
GIMPLE (HL)
GIMPLE (LL, SSA)
MIPS
LLVM (MC)
Direct
Translation
Python
SIL
LLVM (MC)
LLVM
When?
Courtesy of Mario Wolczko
Next Lectures
Resources
”Threaded Code.” by James Bell. In, Communications of the ACM, 1973.
“Stack Caching for Interpreters.” by M. Anton Erlt. In EuroForth, 1994.
“The Structure and Performance of Efficient Interpreters.” by M. Anton Erlt and David Gregg in Journal of Instruction-Level Paralleism, 2003
“Friday Q&A 2012-07-27: Let's Build Tagged Pointers”
https://www.mikeash.com/pyblog/friday-qa-2012-07-27-lets-build-tagged-pointers.html
“An Efficient Implementation of SELF, a Dynamically-Typed Object-Oriented Language Based on Prototypes.” by Craig Chambers, David Ungar, and Elgin Lee. In LISP and Symbolic Computation, 1991