Compiler II: Code Genaration: These Slides Support Chapter 11 of The Book by Noam Nisan and Shimon Schocken MIT Press
Compiler II: Code Genaration: These Slides Support Chapter 11 of The Book by Noam Nisan and Shimon Schocken MIT Press
Nand to Tetris
Building a Modern Computer from First Principles
Chapter 11
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 1
Compiler development roadmap
Methodolgy:
• Extending the basic syntax analyzer
• Adding code generation capabilities.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 2
Program compilation
source code
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 4
Program compilation
class Point {
field int x, y; Compilation
static int pointCount;
• class-level code
constructor Point new(int ax, int ay) {}
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 5
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 6
Take home lessons
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 7
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 8
Variables
High-level (Jack) code
class Foo {
VM code (pseudo)
...
field int a, b, c; ...
static int x, y; // let c = a2 + (x – v3)
... push a2
method int bar(int a1; int a2)
{ compiler push x
var int v1, v2, v3; push v3
... sub VM code
let c = a2 + (x – v3); add
... ...
pop c push argument 1
}
... push static 0
...
} push local 2
sub
add
In order to generate VM code,
pop this 2
the compiler must know (among other things):
...
• Weather each variable is a
field, static, local, or argument
• Weather each variable is the
first, second, third... variable of its kind
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 9
Variables
High-level (Jack) code
class Point {
field int x, y;
Variable properties:
static int pointCount;
q name (identifier)
...
q type (int, char, boolean, class name)
method int distance(Point other) {
var int dx, dy; q kind (field, static, local, argument)
let dx = x - other.getx();
let dy = y - other.gety(); q index (0, 1, 2, ...)
return Math.sqrt((dx*dx)+ (dy*dy));
} q scope (class level, subroutine level)
...
}
Variable properties:
• Needed for code generation
• Can be recorded and managed
using a symbol table
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 10
Symbol tables: construction
High-level (Jack) code
class Point { class-level
field int x, y; symbol table
static int pointCount; (field, static)
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 11
Symbol tables: construction
High-level (Jack) code
class Point {
field int x, y;
static int pointCount;
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 12
Symbol tables: usage
High-level (Jack) code
class Point {
field int x, y;
static int pointCount;
...
(4,6)
Handling variables that appear in statements and expressions:
5
• Each time the compiler detects a variable in some statement or
expression, it looks up the variable in the subroutine-level symbol table
• If not found, it looks it up in the class-level symbol table.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 13
Symbol tables: usage
High-level (Jack) code
class Point {
field int x, y;
static int pointCount;
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 14
Handling variables’s life cycle
Static variables:
High-level (Jack) code
Seen by all the class subroutines;
must exist throughout the program’s class Point {
execution field int x, y;
static int pointCount;
Local variables:
...
During run-time, each time a
subroutine is invoked, it must get a method int distance(Point other) {
fresh set of local variables; each time var int dx, dy;
a subroutine returns, its local variables let dx = x - other.getx();
let dy = y - other.gety();
must be recycled
return Math.sqrt((dx*dx)+ (dy*dy));
Argument variables: }
Implementation note
The variable life cycle is managed by the VM implementation during run-time;
the compiler need not worry about it!
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 15
End note: handling nested scoping
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 17
Expressions
Definition:
Examples:
5
x
x + 5
Math.abs(x + 5)
arr[Math.abs(x + 5)]
foo(arr[Math.abs(x + 5)])
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 18
Parse tree
* a + b c a * (b + c) a b c + *
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 19
Generating code for expressions: a two-stage approach
stack-machine code
source code
push x
parse code
x + g(2,y,-z) - 5 generation push 2
tree
push y
push z
(infix)
-
call g
push 5
*
+
(postfix)
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 20
Generating code for expressions: a one-stage approach
codeWrite(exp):
stack-machine code
source code if exp is a number n:
output “push n”
push x
x + g(2,y,-z) * 5 resulting
if exp is a variable var: code push 2
output “push var”
push y
if exp is “exp1 op exp2”: push z
codeWrite(exp1),
codeWrite(exp2),
-
output “op” call g
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 21
End note 1: parsing and code generation
XML code
parsing
only <letStatement>
<keyword> let </keyword>
(project 10) <identifier> x </identifier>
<symbol> = </symbol>
<expression>
<term> <identifier> a </identifier> </term>
<symbol> + </symbol>
source code example: <term> <identifier> b </identifier> </term>
<symbol> - </symbol>
... <term> <identifier> c </identifier> </term>
let x = a + b - c; <symbol> ; </symbol>
... </expression>
</letStatement>
VM (pseudo) code
push a
push b
parsing +
and push c
code generation -
(project 11) pop x
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 22
End note 2: operator priority
• The Jack language definition specifies no operator priority;
• To simplify the compiler writing, we assume left-to-right priority:
push a
push b
let x = a + b * c; compiler +
push c
*
pop x
The Jack language definition specifies that expressions in parentheses are evaluated first:
push a
General comment:
The Jack language designers decided to leave the question of how to handle operator
order priority up to the compiler’s implementation.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 23
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 24
The challenge
high-level code
let low = 0;
let high = x;
while ((high – low) > epsilon) { VM code, using:
let mid = (high + low) / 2;
if ((mid * mid) > x) { • goto
high = mid; translate
} to: • if-goto
else {
• label
let low = mid;
}
}
return low;
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 25
Compiling if statements
source code VM code
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 26
Compiling while statements
source code VM code
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 27
Recap
Conclusion:
We can write a compiler for a
simple procedural language!
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 28
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 29
Handling variables: the big picture
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 30
Handling local and argument variables (review)
Compilation challenge:
• How to represent local and argument variables
using the host RAM
• How to initialize and recycle local and argument
variables when methods start / return
Solution
local
• The compiler maps the high-level local and
and argument variables on local 0, 1, ... argument
and on argument 0, 1, ... segments
• The compiler translates high-level operations on
local and argument variables into VM operations on
on local 0,1,... and on argument 0,1,...
• During run-time, the VM implementation initializes
and recycles the local and argument segments, as
needed, using the global stack.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 31
Handling field variables (object data)
class Point {
field int x, y;
static int pointCount;
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 32
Handling field variables (object data)
Abstraction:
• Each object is represented by a bundle of field
variable values (each object has a private copy
of these values)
• There may be many objects of the same type
• When I call a method, say bar.foo(), I want foo
to operate on a specific object, in this case bar
... ...
Example:
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 34
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
q Construction
• Compiling “new”
• Compiling constructors
q Manipulation
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 35
Object construction: the big picture
some class
...
...
class Point {
caller ...
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 36
Object construction: the caller’s side
source code
...
var Point p1, p2;
var int d;
...
let p1 = Point.new(2,3);
...
p1
0 p2
0 d
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 37
Object construction: the caller’s side
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 38
Resulting impact
source code
...
var Point p1, p2;
var int d;
...
let p1 = Point.new(2,3);
let p2 = Point.new(5,7);
...
• During compile-time,
the compiler maps p1 on local 0,
p2 on local 1, and d on local 2.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 39
Object construction: the big picture
some class
...
let p1 = Point.new(2,3);
callee
Point class
caller class Point {
...
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 40
Object construction: the big picture
callee
class Point {
...
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 41
Object construction: the big picture
Requirements:
• The constructor must create a new object
• Just like any method, the constructor must be able to manipulate the fields
of the current object
• in the case of construcors, the current object is the newly constructed object
Implementation: callee
The compiled constructor’s code ...
• starts by creating a memory block for
class Point {
representing the new object (details later)
• Next, it sets THIS to the base address ...
of this block (using pointer) constructor Point new(...)
• From now on, the constructor’s code ...
can access the object’s fields
}
using the this segment.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 42
Compiling constructors
caller’s code Caller’s code: compiled
separately, shown for context
compiled constructor's code
var Point p1;
...
let p1 = Point.new(2,3);
...
/** Represents a Point. */
class Point {
field int x, y;
static int pointCount;
...
/** Constructs a new point */
constructor Point new(int ax,
int ay) {
let x = ax;
let y = ay;
let pointCount = pointCount + 1;
return this;
}
...
class-
level
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 43
Compiling constructors
caller’s code
compiled constructor's code
var Point p1; ...
... // constructor Point new(int ax, int ay)
let p1 = Point.new(2,3);
// The compiler creates the subroutine’s symbol table.
...
// The compiler figures out the size of an object of this
/** Represents a Point. */
// class (n), and writes code that calls Memory.alloc(n).
class Point { // This OS method finds a memory block of the required
field int x, y; // size, and returns its base address.
static int pointCount; push 2 // two 16-bit words are required (x and y)
... call Memory.alloc 1 // one argument
/** Constructs a new point */ pop pointer 0 // anchors this at the base address
constructor Point new(int ax,
int ay) { // let x = ax; let y = ay;
let x = ax; push argument 0
let y = ay; pop this 0
let pointCount = pointCount + 1; push argument 1
return this; pop this 1
} // let pointCount = pointCount + 1;
... push static 0
push 1
add
class- pop static 0
level
// return this
push pointer 0
constructor- return // returns the base address of the new object
level
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 44
Compiling constructors
caller’s code
compiled constructor's code
var Point p1; ...
... // constructor Point new(int ax, int ay)
let p1 = Point.new(2,3);
// The compiler creates the subroutine’s symbol table.
...
// The compiler figures out the size of an object of this
// class (n), and writes code that calls Memory.alloc(n).
// This OS method finds a memory block of the required
// size, and returns its base address.
push 2 // two 16-bit words are required (x and y)
call Memory.alloc 1 // one argument
pop pointer 0 // anchors this at the base address
// let x = ax; let y = ay;
push argument 0
pop this 0
push argument 1
pop this 1
// let pointCount = pointCount + 1;
push static 0
push 1
add
pop static 0
// return this
push pointer 0
return // returns the base address of the new object
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 45
Compiling constructors
caller’s code
compiled constructor's code
var Point p1; ...
...
compiled // constructor Point new(int ax, int ay)
let p1 = Point.new(2,3);
...
caller’s code // The compiler creates the subroutine’s symbol table.
// The compiler figures out the size of an object of this
// let p1 = Point.new(2,3)
// class (n), and writes code that calls Memory.alloc(n).
push 2
// This OS method finds a memory block of the required
push 3 // size, and returns its base address.
call Point.new push 2 // two 16-bit words are required (x and y)
pop p1 call Memory.alloc 1 // one argument
pop pointer 0 // anchors this at the base address
// let x = ax; let y = ay;
push argument 0
pop this 0
push argument 1
pop this 1
// let pointCount = pointCount + 1;
push static 0
push 1
add
pop static 0
// return this
push pointer 0
return // returns the base address of the new object
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 46
Object construction: the big picture
...
let p1 = Point.new(2,3);
callee
...
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 47
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
q Construction
q Manipulation
• Compiling method calls (caller side)
• Compiling methods (callee side)
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 48
Object manipulation: high-level
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 49
Compiling method calls
some class
...
var Point p1, p2, p3;
var int x, d;
...
let p1 = Point.new(2,3);
let p2 = Point.new(5,7);
...
let x = p1.getx();
let p3 = p1.plus(p2);
let d = p1.distance(p2);
...
caller
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 50
Compiling method calls
• Solution: when calling a method, the object on which the method is called to
operate is always passed as a first, implicit argument.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 51
Compiling method calls: the general technique
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 52
Object manipulation: the big picture
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 53
Compiling methods
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 54
Compiling methods
... compiled VM code
Caller’s code:
let d = p1.distance(p2);
compiled separately,
... shown for context
class-
level
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 55
Compiling methods
... compiled VM code
Caller’s code: ...
let d = p1.distance(p2);
compiled separately,
... // method int distance(Point other)
shown for context
// var int dx,dy
// The compiler constructs the method’s
/** Represents a Point. */ // symbol table. No code is generated.
class Point { // Next, it generates code that associates the
field int x, y; // this memory segment with the object on
static int pointCount; // which the method was called to operate.
... push argument 0
/** Distance to the other point */ pop pointer 0 // THIS = argument 0
method int distance(Point other) { // let dx = x – other.getx()
var int dx, dy push this 0
let dx = x – other.getx(); push argument 1
let dy = y – other.gety(); call Point.getx 1
return Math.sqrt((dx*dx) + sub
(dy*dy)); pop local 0
} // let dy = y – other.gety()
...
// Similar, code omitted.
}
// return Math.sqrt((dx*dx)+(dy*dy))
push local 0
push local 0
class- call Math.multiply 2
level push local 1
push local 1
call Math.multiply 2
method- add
level call Math.sqrt 1
return
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 56
Compiling methods
... compiled VM code
Caller’s code: ...
let d = p1.distance(p2);
compiled separately,
... // method int distance(Point other)
shown for context
// var int dx,dy
// The compiler constructs the method’s
// symbol table. No code is generated.
// Next, it generates code that associates the
// this memory segment with the object on
// which the method was called to operate.
push argument 0
pop pointer 0 // THIS = argument 0
// let dx = x – other.getx()
push this 0
push argument 1
call Point.getx 1
sub
pop local 0
// let dy = y – other.gety()
// Similar, code omitted.
// return Math.sqrt((dx*dx)+(dy*dy))
push local 0
push local 0
call Math.multiply 2
push local 1
push local 1
call Math.multiply 2
add
call Math.sqrt 1
return
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 57
Compiling methods
... compiled VM code
Caller’s code: ...
let d = p1.distance(p2);
compiled separately,
... // method int distance(Point other)
shown for context
// var int dx,dy
// The compiler constructs the method’s
compiled caller (pseudo) code // symbol table. No code is generated.
// Next, it generates code that associates the
... // this memory segment with the object on
// which the method was called to operate.
push p1
push argument 0
push p2 pop pointer 0 // THIS = argument 0
call Point.distance // let dx = x – other.getx()
push this 0
pop d
push argument 1
... call Point.getx 1
sub
pop local 0
// let dy = y – other.gety()
// Similar, code omitted.
// return Math.sqrt((dx*dx)+(dy*dy))
push local 0
push local 0
call Math.multiply 2
push local 1
push local 1
call Math.multiply 2
add
call Math.sqrt 1
return
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 58
Compiling void methods
... compiled VM code
Caller’s code:
do p1.print(); compiled separately,
... shown for context
class-
level
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 59
Compiling void methods
... compiled VM code
Caller’s code:
do p1.print(); compiled separately, ...
... shown for context // method void print()
// The compiler constructs the method’s
// symbol table. No code is generated.
/** Represents a Point. */ // Next, it generates code that associates the
class Point { // this memory segment with the object on
field int x, y; // which the method is called to operate.
static int pointCount; push argument 0
... pop pointer 0 // THIS = argument 0
/** Prints the point as (x,y) */ ...
method void print() { // Returns a value (all methods must return a value)
... push constant 0
} return
...
}
class-
level
method-
level
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 60
Compiling void methods
... compiled VM code
Caller’s code:
do p1.print(); compiled separately, ...
... shown for context // method void print()
// The compiler constructs the method’s
// symbol table. No code is generated.
// Next, it generates code that associates the
// this memory segment with the object on
// which the method is called to operate.
push argument 0
pop pointer 0 // THIS = argument 0
...
// Methods must return a value.
push constant 0
return
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 61
Compiling void methods
... compiled VM code
Caller’s code:
do p1.print(); compiled separately, ...
... shown for context // method void print()
// The compiler constructs the method’s
// symbol table. No code is generated.
compiled caller (pseudo) code // Next, it generates code that associates the
// this memory segment with the object on
... // which the method is called to operate.
push argument 0
push p1
pop pointer 0 // THIS = argument 0
call Point.print ...
// the caller of a void method // Methods must return a value.
// must dump the returned value push constant 0
return
pop temp 0
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 63
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 64
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
q Array construction
q Array manipulation
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 65
Array construction
Code generation
• var Array arr:
• generates no code;
only effects the symbol table
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 66
Compilation challenges
• Handling variables
• Handling expressions
• Handling objects
• Handling arrays
q Array construction
q Array manipulation
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 67
this and that (reminder)
this that
pointer
(base THIS THAT
address)
pop pointer 0 pop pointer 1
how to set:
(sets THIS) (sets THAT)
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 68
Example: RAM access using that
// RAM[8056] = 17
push 8056
pop pointer 1
push 17
pop that 0
17
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 69
Array access
// let arr[2] = 17
push arr // base address
push 2 // offset
add
pop pointer 1
push 17
pop that 0
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 70
Array access
unfortunately,
there’s a problem
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 71
Array access
the problem, illustrated
// let a[i] = b[j]
push a • Our compilation strategy is generating code
push i on the fly, left to right
add • The term within the square brackets is treated
pop pointer 1 as an expression, and compiled as such.
There are many other scenarios that will cause similar problems, e.g.
let a[a[i]+a[j+a[a[3]]] = a[j+2]
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 72
Array access
solution
// let a[i] = b[j]
push a
push i
add
push b
push j
add
At this point:
pop pointer 1
push that 0 • The stack topmost value is
the RAM address of a[i]
pop temp 0
• temp 0 contains
pop pointer 1
the value of b[j]
push temp 0
pop that 0
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 73
Array access
General solution for generating array access code
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 74
Compilation challenges
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 75
The big picture
p10
p11
p7
p8
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 76
The big picture
classes, functions,
... constructors, methods,
arrays, objects, ...
map
on:
push
pop
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 77
The big picture
classes, functions,
constructors, methods,
arrays, objects, ...
map
on:
push
pop
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 78
Files and subroutines mapping
Foo.jack Foo.vm
class Foo {
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 80
Arrays mapping
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 81
Compiling subroutines
When compiling a Jack method:
• the compiled VM code must set the base of the this segment to argument 0
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 82
Compiling subroutine calls
Compiling a subroutine call subName(arg1, arg2, ...):
The caller (a VM function) must push the arguments arg1, arg2, ...
onto the stack, and then call the subroutine
If the called subroutine is a method,
• The caller (a VM function) must first push a reference to the object on
which the method is supposed to operate;
next, the caller must push arg1, arg2, ... and then call the method
If the called subroutine is void:
• A void Jack subroutine is not required to return a value;
However, VM functions are required to always return a value.
Therefore, when compiling the Jack statement do subName,
following the call the caller must pop (and ignore) the returned value.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 83
Compiling constants
• null is mapped on the constant 0
can be obtained
using push 1
followed by neg
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 84
OS classes and subroutines
• The OS is written in Jack
• Implemented as a set of 8 compiled Jack classes:
q Math.vm
q Memory.vm
q Screen.vm
q Output.vm Available in
q Keyboard.vm nand2tetris/tools/os
q String.vm
q Array.vm
q Sys.vm
Usage
• The eight OS *.vm files must reside in the same directory as the VM files generated
by the compiler
Or:
• If the generated VM code is executed / tested on the supplied VM emulator, there is
no need to include any OS files, since the supplied emulator features a built-in
implementation of the OS (written in Java).
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 85
Special OS services
Multiplication is handled using the OS function Math.multiply()
Object construction requires allocating space for the new object using the
OS function Memory.alloc(size)
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 86
Compilation challenges
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 87
Compiler development roadmap
Modules:
• JackCompiler: top-most (“main”) module
• JackTokenizer
main focus of • SymbolTable
this project
• VMWriter: generates VM code
• CompilationEngine: recursive top-down compilation engine
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 88
JackCompiler
Usage:
prompt> JackCompiler input
Implementation notes:
• For each source .jack file, the compiler creates a JackTokenizer and an output .vm file
• Next, the compiler uses the SymbolTable, CompilationEngine, and VMWriter modules
to write the VM code into the output .vm file.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 89
JackTokenizer
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 90
SymbolTable
class Point {
field int x, y;
static int pointCount;
...
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 92
VMWriter
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 93
CompilationEngine
• Gets its input from a JackTokenizer and writes its output using the VMWriter
• Organized as a series of compilexxx routines, xxx being a syntactic element in the
Jack language
• Contract:
Ø Each compilexxx routine should read xxx from the input, advance() the input exactly
beyond xxx, and emit to the output VM code effecting the semantics of xxx
Ø Thus compilexxx should be called only if xxx is the current syntactic element
Ø If xxx is part of an expression and thus has a value, the emitted VM code should
compute this value and leave it at the top of the VM’s stack
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 94
CompilationEngine (same as in project 10)
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 95
CompilationEngine (same as in project 10)
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 96
Compilation challenges
• Handling arrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 97
Compiler development roadmap
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 98
Symbol table
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 99
Symbol table
Extend the handling of identifiers
• output the identifier’s category: var, argument, static, field, class, subroutine
• if the identifier’s category is var, argument, static, field, output also the
running index assigned to this variable in the symbol table
• output whether the identifier is being defined, or being used
Implementation
1. implement the SymbolTable API
2. extend the syntax analyzer (developed in project 01) with the identifier
handling described above
3. (use your own output/tags format)
3. Test the extended syntax analyzer by running it on the test programs given
in project 10.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 100
Compiler development roadmap
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 101
Code generation
Test programs
q Seven
q ConvertToBin
Development plan: unit testing
q Square Test your evolving compiler on the
q Average supplied test programs, in the shown order
q Pong
q ComplexArrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 102
Test program: Seven
projects/11/Seven/Main.jack projects/11/Seven/Main.vm
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 103
Test program: Seven
projects/11/Seven/Main.jack projects/11/Seven/Main.vm
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 104
Quiz
projects/11/Seven/Main.jack projects/11/Seven/Main.vm
171 = (10101011)2
Compiled program:
Converts RAM[8000] to binary, and stores input
the 16 bits in RAM[8001]–RAM[8016]
output
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 107
Test program: decimal-to-binary conversion
class Main {
// Converts RAM[8000] to binary, putting the resulting bits in RAM[8001]..RAM[8016]
function void main() {
var int value;
do Main.fillMemory(8001, 16, -1); // sets RAM[8001]..RAM[8016] to -1
let value = Memory.peek(8000); // gets the input from RAM[8000]
do Main.convert(value); // performs the conversion
return;
}
binocular
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 109
Test app: Square
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 110
Test app: Square
projects/11/Square/Square.jack
/** Implements a graphical square */
class Square {
/** Constructs a new square with a given location and size */ SquareGame.jack
constructor Square new(int Ax, int Ay, int Asize)
/** square
/** Disposes this Implements
*/ a square game */
class
method void dispose() SquareGame {
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 111
Test program: average
/** Computes the average of a sequence of integers */
class Main {
Tests how your
function void main() { compiler handles:
var Array a;
var int length; • Arrays
var int i, sum;
• Strings
let length = Keyboard.readInt("How many numbers? ");
let a = Array.new(length);
let i = 0;
pong
game
ball
bat
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 113
Test app: Pong
projects/11/Pong/Ball.jack
/**
* A graphic ball, with methods for drawing, erasing Bat.jack
* and moving on the screen.
*/ /** A graphic paddle (Bat) with methods for drawing,
class Ball { * erasing, moving left and right changing width. */
PongGame.jack
class Bat {
// Ball's screen location (in pixels) /** Pong game */
class PongGame {
field int x, y;// Screen location
field int x, y; static PongGame instance; // the game
// Distance from last destination field Bat bat; // the bat
field int lengthx, lengthy;
// Bat’s width and height field Ball ball; // the ball
field int width, height; ...
// Used for straight line movement computation /** Creates an instance of a PongGame */
field int d, straightD, diagonalD;of movement function void newInstance() {
// Bat’s direction
field boolean invert, positivex,
field int // 1 = left,let
positivey;
direction; 2 =instance
right = PongGame.new();
return; Main.jack
}
// Locations of/**
walls
Constructs a new bat */
... /** Main class of the Pong game */
field int leftWall, rightWall,
constructor topWall,Ax,
Bat new(int bottomWall;
int /**
Ay,Runs the game. */
class Main {
int Awidth, method
int Aheight) { /**
void run() { Initializes a Pong game and
// last wall that let
the xball
= Ax;was bounced from var char key; starts running it. */
field int wall; let y = Ay; while (~exit) function
{ void main() {
let width = Awidth; // waits for avar keyPongGame
to be pressed.
game;
/** Constructs a new
let Ball with
height an initial ball
= Aheight; while ((key = do 0) PongGame.newInstance();
& (~exit)) {
* location and wall locations.= */
let direction 2; let key = Keyboard.keyPressed();
let game = PongGame.getInstance();
constructor Ball new(int Ax, int Ay, int AleftWall, do bat.move();
do show(); do game.run();
do moveBall();
do game.dispose();
int this;
return ArightWall, int AtopWall,
} return;
} int AbottomWall) {}
}
... if (key }= 130) {
... do bat.setDirection(1);
// More Ball methods }
// More Bat methods ...
} }
} }
...
}
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 114
Test program: ComplexArrays
projects/11/ComlpexArrays/Main.jack
class Main {
function void main() { Tests how your compiler handles array
var Array a, b, c;
let a = Array.new(10);
manipulations using index expressions
let b = Array.new(5); that include complex array references
...
// Fills the arrays with some data (omitted)
...
// Manipulates the arrays using some complex index expressions
let a[b[a[3]]] = a[a[5]] * b[7 - a[3] - Main.double(2) + 1];
...
// Prints the expected and the actual values of a[b[a[3]]]
...
}
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 115
Recap: project 11
Test programs
Project 11: q Seven
q ConvertToBin
Extend / morph the syntax analyzer q Square
into a full-scale compiler q Average
q Pong
q ComplexArrays
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 116