0% found this document useful (0 votes)
184 views

Compiler II: Code Genaration: These Slides Support Chapter 11 of The Book by Noam Nisan and Shimon Schocken MIT Press

Uploaded by

jangala gouthami
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
184 views

Compiler II: Code Genaration: These Slides Support Chapter 11 of The Book by Noam Nisan and Shimon Schocken MIT Press

Uploaded by

jangala gouthami
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 116

From

Nand to Tetris
Building a Modern Computer from First Principles

Chapter 11

Compiler II: Code Genaration

These slides support chapter 11 of the book


The Elements of Computing Systems
By Noam Nisan and Shimon Schocken
MIT Press

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 1
Compiler development roadmap

Objective: developing a full-scale compiler

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

class Main { (4,6)


function void main() {
var Point p1, p2, p3; 5
let p1 = Point.new(1,2);
let p2 = Point.new(3,4);
let p3 = p1.plus(p2);
do p3.print();
// should print (4,6)
do Output.println();
do Output.printInt(p1.distance(p3));
// should print 5
return;
}
} /** Represents a Point. */
Compilation
class Point {
field int x, y; Each class is compiled
static int pointCount;
/** Constructs a new point */
separately
constructor Point new(int ax,
int ay) {
let x = ax;
let y = ay;
let pointCount =
pointCount + 1;
return this;
}
// ... more Point methods
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 3
Program compilation

class Point { class


field int x, y; declaration
static int pointCount;

constructor Point new(int ax, int ay) {}

method int getx() {}

method int gety() {}

function int getPointCount() {} subroutines:


method Point plus(Point other) {} q constructors
method int distance(Point other) { q methods
var int dx, dy; q functions
let dx = x - other.getx();
let dy = y - other.gety();
return Math.sqrt((dx*dx)+ (dy*dy));
}
method void print() {}
}

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) {}

method int getx() {} • subroutine-level code


method int gety() {} q constructors
function int getPointCount() {}
q methods
method Point plus(Point other) {}
q functions
method int distance(Point other) {
var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
return Math.sqrt((dx*dx)+ (dy*dy));
}
method void print() {}
}

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 5
Compilation challenges

• Handling variables

• Handling expressions

• Handling flow of control

• Handling objects

• Handling arrays

The challenge: expressing the above semantics in the VM language.

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 6
Take home lessons

How to implement: Techniques:

• Procedural programming • Parsing (mostly done)


q variables
• Recursive compilation
q expressions
q flow of control • Code generation
• Arrays • Symbol tables
• Objects • Memory management.

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 7
Compilation challenges

• Handling variables

• Handling expressions

• Handling flow of control

• 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)

...

method int distance(Point other) {


var int dx, dy; subroutine-level
let dx = x - other.getx(); symbol table
let dy = y - other.gety(); (argument, local)
return Math.sqrt((dx*dx)+ (dy*dy));
}
...
}

In methods only (not in constructors and functions):


(4,6)
• in addition to the explicit arguments, there is always
an
5 implicit argument:
• argument 0 is always named this, and its type is
always set to the class name
• Generated by the compiler

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;

...

method int distance(Point other) {


var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
return Math.sqrt((dx*dx)+ (dy*dy));
}
...
}

Handling variable declarations: (4,6)


• 5 , static, argument, local),
Each time the compiler detects a variable declaration (field
it adds an entry to the symbol table
• The entry records the variable properties.

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;

...

method int distance(Point other) {


var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
return Math.sqrt((dx*dx)+ (dy*dy));
}
...
}

(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;

...

method int distance(Point other) {


var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
return Math.sqrt((dx*dx)+ (dy*dy));
}
...
look-up
}

source code example: VM code

... push this 1 // y

let y = y + dy; compiler push local 1 // dy


add
...
pop this 1 // y

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: }

Same as local variables ...


}
Field variables:
Unique to object-oriented languages;
will be discussed later.

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

class foo { // class level


variable declarations
method bar () { // method level
variable declarations
• Some high-level languages feature
... { // scope 1 level
unlimited nested variable scoping
variable declarations
... { // scope 2 levek • Can be handled using a linked list of
variable declarations symbol tables
...
}
}
}
}

scope 2 level scope 1 level method level class level


symbol
tables
... symbol
table
symbol
table
symbol
table
symbol
table

Variable lookup: start in the first table in the list:


if not found, look up the next table, and so on.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 16
Compilation challenges

• Handling variables

• Handling expressions

• Handling flow of control

• 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

prefix infix postfix

* a + b c a * (b + c) a b c + *

functional human oriented stack oriented


*(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)

When executed, the generated code ends up leaving


the value of the expression at the top of the stack.

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

if exp is “op exp”: push 5


codeWrite(exp), *
output “op” +
if exp is “f (exp1, exp2, ...)”:
codeWrite(exp1),
codeWrite(exp2), ...,
output “call f ”

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

let x = a + (b * c); compiler push b


push c
*
+
pop x

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 flow of control

• 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

if (expression) compiled (expression)


statements1 not
else compiler if-goto L1
statements2 compiled (statements1)
... goto L2
label L1
compiled (statements2)
label L2
...

the labels are generated


by the compiler

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 26
Compiling while statements
source code VM code

while (expression) label L1


statements compiler compiled (expression)
... not
if-goto L2
compiled (statements)
goto L1
label L2
...

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 27
Recap

high-level code Our compiler (front-end) knows


how to handle
let low = 0;
let high = x; • Variables
while ((high – low) > epsilon) {
let mid = (high + low) / 2;
• Expressions
if ((mid * mid) > x) { • Flow of control
high = mid;
}
else { Our compiler (back-end) knows
let low = mid; how to handle
}
} • Functions
return low;
• Function call-and-return
• Memory allocation

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 flow of control

• 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

/** Represents a Point. */


class Point {
field int x, y;
static int pointCount;
/** Constructs a new point */
constructor Point new(int ax, compile VM
int ay) r translator
{
let x = ax;
let y = ay;
let pointCount =
pointCount + 1;
return this;
}
// ... more Point methods

high-level view intermediate-level view low-level view

• High-level OO programs operate on high-level, symbolic variables


• Mid-level VM programs operate on virtual memory segments
• Low-level machine programs operate directly on the RAM
High-level
view
The compilation challenge: bridging the gaps.

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;

...

method int distance(Point other) {


var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
return Math.sqrt((dx*dx)+ (dy*dy)); local and
} argument
... variables
}

• There may be many objects (class instances)


of type Point
• Each represented by its own (also called
private) set of x,y values
• As long as the program is running,
all these objects must be managed.

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

Implementation local and


argument
• Each object is managed as a separate memory variables
block, stored in a RAM area named heap
• The current object is represented by the
segment this
• Basic compilation strategy
for accessing object data: The memory blocks that
q when we want to operate on a particular object, represent objects are
we set THIS to its base address managed in the heap
q From this point onward, high-level code that uses
field i of the current object can be translated into
VM code that operates on this i.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 33
Accessing the RAM using this
Suppose we wish to access
RAM words 8000, 8001, 8002...
VM code Resulting
(commands) effect
8000
push 8000
pop pointer 0 sets THIS to 8000
push/pop this 0 accesses RAM[8000]
push/pop this 1 accesses RAM[8001]

... ...

push/pop this i accesses RAM[8000+i]

Example:

// Sets the second element 8000


basic technique // of the memory block
for accessing // beginning at 8000 to 17: 17 17
object data push 8000
pop pointer 0
push 17
pop this 1

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 34
Compilation challenges

• Handling variables

• Handling expressions

• Handling flow of control

• 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
...

var Point p1;

...

let p1 = Point.new(2,3); callee


... Point class

class Point {
caller ...

constructor Point new(...)

...

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

source code compiled VM (pseudo) code


... // var Point p1, p2
var Point p1, p2; // var int d;
var int d; // The compiler updates the subroutine’s symbol table.
... // No code is generated.
let p1 = Point.new(2,3); ...
Contract: the caller assumes that
let p2 = Point.new(5,7); // let p1 = Point.new(2,3) the constructor’s code (i)
... // Subroutine call: arranges a memory block to store
push 2 the new object, and (ii) returns its
push 3 base address to the caller.
call Point.new
pop p1 // p1 = base address of the new object
// let p2 = Point.new(5,7)
// Similar, code omitted.
...

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.

• During run-time, the execution of the


constructor’s code effects the creation
of the objects themselves, on the heap.

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 39
Object construction: the big picture

some class

var Point p1;

...

let p1 = Point.new(2,3);
callee

Point class
caller class Point {

...

constructor Point new(...)

...

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 40
Object construction: the big picture

callee

class Point {

...

constructor Point new(...)

...

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

var Point p1;

...

let p1 = Point.new(2,3);
callee

caller class Point {

...

constructor Point new(...)

...

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 47
Compilation challenges

• Handling variables

• Handling expressions

• Handling flow of control

• 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

some class Point class


class Point {
...
field int x, y;
var Point p1, p2, p3;
static int pointCount;
var int x, d;
... constructor Point new(int ax, int ay) {}
let p1 = Point.new(2,3);
let p2 = Point.new(5,7); method int getx() {}
... method int gety() {}
let x = p1.getx();
let p3 = p1.plus(p2); method int getPointCount() {}
let d = p1.distance(p2); method Point plus(Point other) {}
...
method int distance(Point other) {
var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
caller
return Math.sqrt((dx*dx)+ (dy*dy));
callee }
method void print() {}
}

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

Source code (OO) Target code (procedural)

... p1.distance(p2) ... ... distance(p1,p2) ...

... p3.getx() ... ... getx(p3) ...

... obj.foo(x1,x2,...) ... ... foo(obj,x1,x2,...) ...

• The target language is procedural

• Therefore, the compiler must generate procedural code

• 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

source code generated VM (pseudo) code


// obj.foo(x1,x2,...)
obj.foo(x1,x2,...) compiler // Pushes the object on which the method
// is called to operate (implicit argument),
// then pushes the method arguments,
// then calls the method for its effect.
push obj
push x1
push x2
push ...
call foo

example: generated VM (pseudo) code


...
...
push p1
let d = p1.distance(p2); compiler push p2
... call distance
pop d
...

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 52
Object manipulation: the big picture

some class Point class


class Point {
...
field int x, y;
var Point p1, p2, p3;
static int pointCount;
var int x, d;
... constructor Point new(int ax, int ay) {}
let p1 = Point.new(2,3);
let p2 = Point.new(5,7); method int getx() {}
... method int gety() {}
let x = p1.getx();
let p3 = p1.plus(p2); method int getPointCount() {}
let d = p1.distance(p2); method Point plus(Point other) {}
...
method int distance(Point other) {
var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
caller
return Math.sqrt((dx*dx) + (dy*dy));
callee }
method void print() {}
}

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 53
Compiling methods

Methods are designed to operate Point class


on the current object (this): class Point {
field int x, y;
Therefore, each method’s code static int pointCount;
needs access to the object’s fields
constructor Point new(int ax, int ay) {}

method int getx() {}


How to access the object’s fields:
method int gety() {}
• The method’s code can access
method int getPointCount() {}
the object’s i-th field by
accessing this i method Point plus(Point other) {}

method int distance(Point other) {


• To enable this, the method’s var int dx, dy;
code must anchor the this let dx = x - other.getx();
segment on the object’s data, let dy = y - other.gety();
return Math.sqrt((dx*dx) + (dy*dy));
using pointer }
method void print() {}
}

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

/** Represents a Point. */


class Point {
field int x, y;
static int pointCount;
...
/** Distance to the other point */
method int distance(Point other) {
var int dx, dy
let dx = x – other.getx();
let dy = y – other.gety();
return Math.sqrt((dx*dx) +
(dy*dy));
}
...
}

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

/** Represents a Point. */


class Point {
field int x, y;
static int pointCount;
...
/** Prints the point as (x,y) */
method void print() {
...
}
...
}

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

Compiler writers, heads up:


Calle’s contract (compiled code): before terminating,
I must must return a value
(by convention, void methods return 0)
Caller’s contract (compiled code): after calling a voide method,
I must dump the topmost stack value
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 62
Object manipulation: the big picture

some class Point class


class Point {
...
field int x, y;
var Point p1, p2, p3;
static int pointCount;
var int x, d;
... constructor Point new(int ax, int ay) {}
let p1 = Point.new(2,3);
let p2 = Point.new(5,7); method int getx() {}
... method int gety() {}
let x = p1.getx();
let p3 = p1.plus(p2); method int getPointCount() {}
let d = p1.distance(p2); method Point plus(Point other) {}
...
method int distance(Point other) {
var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
caller
return Math.sqrt((dx*dx)+ (dy*dy));
callee }
method void print() {}
}

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 63
Compilation challenges

• Handling variables

• Handling expressions

• Handling flow of control

• 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 flow of control

• 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

var Array arr;


...
let arr = Array.new(5);
...

Code generation
• var Array arr:
• generates no code;
only effects the symbol table

• let arr = Array.new(n):


• from the caller’s perspective,
handled exactly like object construction

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 66
Compilation challenges

• Handling variables

• Handling expressions

• Handling flow of control

• 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)

Two “portable” virtual memory segments that


can be aligned to different RAM addresses

this that

used to represent used to represent


VM use: the values of the the values of the
current object current array

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

// let arr[expression1] = expression2


push arr
push expression1
add
pop pointer 1
push expression2
pop that 0

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.

// now handle the right hand side


push b
push j
add
pop pointer 1

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

// let arr[expression1] = expression2


push arr

VM code for computing and pushing the value of expression1


add // top stack value = RAM address of arr[expression1] If needed, the evaluation of
expression2 can set and use
VM code for computing and pushing the value of expression2 pointer 1 and that 0 safely
pop temp 0 // temp 0 = the value of expression2
pop pointer 1
push temp 0
pop that 0

What about handling, say, let a[a[i]] = a[b[a[b[j]]]] ?


No problem...

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 74
Compilation challenges

• Handling variables • Standard mapping

• Handling expressions • Proposed implementation

• Handling flow of control • Project 11

• Handling objects • Perspectives

• 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

Standard Mapping on the VM platform:


Specifies how to map the constructs of a given high-level language
on the constructs of the virtual machine

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

Standard Mapping of Jack on the VM platform:


Specifies how to map the constructs of the Jack language on the
constructs of the virtual machine.

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 {

constructor Foo new(int x) {} function Foo.new


...
method void bar(int x) {}
JackCompiler function Foo.bar
...
function int baz(int x) {}
function Foo.baz
}

• Each file filename.jack is compiled separately into the file fileName.vm


• Each subroutine subName in fileName.jack is compiled
into a VM function fileName.subName
• A Jack constructor / function with k arguments is compiled into a VM
function that operates on k arguments
• A Jack method with k arguments is compiled into
a VM function that operates on k + 1 arguments.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 79
Variables mapping

The local variables


of a Jack subroutine are mapped on the virtual segment local

The argument variables


of a Jack subroutine are mapped on the virtual segment argument

The static variables


of a .jack class file are mapped on the virtual memory segment static
of the compiled .vm class file

The field variables


of the current object are accessed as follows:
q pointer 0 is set to argument 0 (which represents the current object, or this)
q the i-th field of this object is mapped on this i

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 80
Arrays mapping

Accessing array entries:


Access to any array entry arr[i] is realized as follows:
q first, set pointer 1 to the entry’s address (arr + i)
q access the entry by accessing that 0

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

When compiling a Jack constructor:


• the compiled VM code must allocate a memory block for the new object,
and then set the base of the this segment to the new object’s base address
• the compiled VM code must return the object’s base address to the caller

When compiling a void function or a void method:


• The compiled VM code must return the value constant 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

• false is mapped on the constant 0

• true is mapped on the constant -1

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

• Any VM function can call any OS function for its effect.

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()

Division is handled using the OS function Math.divide()

String constants are created using the OS constructor String.new(length)

String assignments like x="cc...c" are handled using a series of calls


to String.appendChar(c)

Object construction requires allocating space for the new object using the
OS function Memory.alloc(size)

Object recycling is handled using the OS function Memory.deAlloc(object).

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 86
Compilation challenges

• Handling variables • Standard mapping

• Handling expressions • Proposed implementation

• Handling flow of control • Project 11

• Handling objects • Perspectives

• 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

Input: q fileName.jack: name of a single source file, or


q directoryName: name of a directory containing
one or more .jack source files

Output: q if the input is a single file: fileName.vm


q if the input is a directory: one .vm file for every .jack file,
stored in the same directory

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

Developed in project 10.

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;

...

method int distance(Point other) {


var int dx, dy;
let dx = x - other.getx();
let dy = y - other.gety();
return Math.sqrt((dx*dx)+ (dy*dy));
}
...
} Can be implemented
using two hash tables
Implementation notes:
• Each variable is assigned a running index within its scope (table) and kind
• The index starts at 0, increments by 1 each time a new symbol is added to
the table, and is reset to 0 when starting a new scope
• (the operations described above are performed by the SymbolTable routines)
• When compiling an error-free Jack code, each symbol not found in the symbol tables
can be assumed to be either a subroutine name or a class name.
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 91
SymbolTable

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 variables • Standard mapping

• Handling expressions • Proposed implementation

• Handling flow of control • Project 11

• Handling objects • Perspectives

• Handling arrays

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 97
Compiler development roadmap

Project 11: extend the syntax analyzer into a full-scale compiler


Stage 0: syntax analyzer (done)
Stage 1: symbol table handling
Stage 2: code generation.

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 98
Symbol table

Output of the syntax analyzer (project 10)


...
<expression>
<term> In the syntax analyzer built
<identifier> count </identifier>
in project 10, identifiers
</term>
<symbol> < </symbol> were handled by outputting
<term> <identifier> tags
<intConstant> 100 </intConstant>
</term>
</expression>
...

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

Project 11: extend the syntax analyzer into a full-scale compiler

Stage 0: syntax analyzer


Stage 1: symbol table handling
Stage 2: code generation

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

For each test program:


1. Use your compiler to compile the program directory
2. Inspect the generated code;
If there’s a problem, fix your compiler and go to stage 1
3. Load the directory into the VM emulator
4. Run the compiled program, inspect the results
5. If there’s a problem, fix your compiler and go to stage 1.

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

/** function Main.main 0


* Computes the value of 1 + (2 * 3) push constant 1
* and prints the result at the top-left push constant 2
* corner of the screen. push constant 3
*/ call Math.multiply 2
class Main { JackCompiler add
function void main() { call Output.printInt 1
do Output.printInt(1 + (2 * 3)); pop temp 0
return; push constant 0
} return
}

Tests how your compiler handles:


• a simple program
• an arithmetic expression involving constants only
• a do statement
• a return statement

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

/** function Main.main 0


* Computes the value of 1 + (2 * 3) push constant 1
* and prints the result at the top-left push constant 2
* corner of the screen. push constant 3
*/ call Math.multiply 2
class Main { JackCompiler add
function void main() { call Output.printInt 1
do Output.printInt(1 + (2 * 3)); pop temp 0
return; push constant 0
} return
}

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

/** function Main.main 0


* Computes the value of 1 + (2 * 3) push constant 1
* and prints the result at the top-left push constant 2
* corner of the screen. push constant 3
*/ call Math.multiply 2
class Main { JackCompiler add
function void main() { call Output.printInt 1
do Output.printInt(1 + (2 * 3)); pop temp 0
return; push constant 0
} return
}

Inspect the VM code, focusing on the two instructions highlighted in red.


Select the correct observation:
The two highlighted instructions ...
a) are designed to clean up the memory segments before the function returns
b) are related to the function call-and-return contract
c) are example of unnecessary code that is sometimes generated by the compiler
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 105
Quiz answer
projects/11/Seven/Main.jack projects/11/Seven/Main.vm

/** function Main.main 0


* Computes the value of 1 + (2 * 3) push constant 1
* and prints the result at the top-left push constant 2
* corner of the screen. push constant 3
*/ call Math.multiply 2
class Main { JackCompiler add
function void main() { call Output.printInt 1
do Output.printInt(1 + (2 * 3)); pop temp 0
return; push constant 0
} return
}

Inspect the VM code, focusing on the two instructions highlighted in blue.


Select the correct observation:
The two highlighted instructions ...
a) are designed to clean up the memory segments before the function returns
b) are related to the function call-and-return contract
c) are example of unnecessary code that is sometimes generated by the compiler
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 106
Test program: decimal-to-binary conversion

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;
}

// Fills 'length' consecutive memory locations with 'value’,


// starting at 'startAddress’.
function void fillMemory(int startAddress, int length, int value) {}

// Converts the value to binary, and puts the result in RAM[8001]..RAM[8016] */


function void convert(int value) {}

// Some more private functions (omitted)


}

Tests how your compiler handles:


• expressions (without arrays or method calls)
• procedural constructs: if, while, do, let, return
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 108
Decimal-to-binary conversion: testing tips
• Use the “binocular” control
• Note that the “rewind” control erases the RAM
• Note that you cannot enter input into the RAM in “no animation” mode
• To see the program’s results (RAM state), click the “stop” control

stop rewind animate


mode

binocular

Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 109
Test app: Square

When the game starts, a black square is


displayed at When theleft
the top gameof starts,
the screen:
a black square appears at the
top left of the screen.

Tests how your compiler handles object-oriented features


of the Jack language:
• constructors
• methods
• expressions that include method calls.

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 {

/** Draws the square on the


field screensquare;
Square */ // the square
method void draw()
field int direction; // the square’s direction:
/** Erases the square from the screen */ // 0=none, 1=up, 2=down, Main.jack
method void erase() // 3=left, 4=right
/**
/** Increments the/**
square’s size by 2 pixels
Constructs a new Square */ * Main
Game class
*/ of the square game.
method void incSize() * initializes a new game and starts it
constructor SquareGame new() {
let square
/** Decrements the square’s pixels */ */
size by= 2Square.new(0, 0, 30);
method void decSize() let direction = 0; class Main {
return this;
/** Moves up by 2 }pixels */ /** Initializes and starts a new game */
method void moveUp() function void main() {
/** Moves down by /** Disposes
2 pixels */ this game */ var SquareGame game;
method
method void moveDown() void dispose() {
do square.dispose(); let game = SquareGame.new();
/** Moves left by 2 pixels */
do Memory.deAlloc(this); do game.run();
method void moveLeft() do game.dispose();
return;
/** Moves right by} 2 pixels */ return;
method void moveRight() }
} ... }

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;

while (i < length) {


let a[i] = Keyboard.readInt("Enter the next number: ");
let i = i + 1;
}

let i = 0; let sum = 0;

while (i < length) {


let sum = sum + a[i];
let i = i + 1;
}

do Output.printString("The average is: ");


do Output.printInt(sum / length);
do Output.println();
return;
}
}
Nand to Tetris / www.nand2tetris.org / Chapter 11 / Copyright © Noam Nisan and Shimon Schocken Slide 112
Test app: Pong

pong
game

ball

bat

Tests how your compiler handles a complete object-oriented


application, including the handling of objects and static variables.

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]]]
...
}

// A trivial function that tests how the compiler handles a subroutine


// call within an expression that evaluates to an array index
function int double(int a) {
return a * 2;
}

// Creates a two dimensional array


function void fill(Array a, int size) {
while (size > 0) {
let size = size - 1;
let a[size] = Array.new(3);
}
return;
}
}

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

You might also like