Skip to content

Commit

Permalink
Implement closure conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
antoyo committed Nov 9, 2024
1 parent 78ba844 commit 2ea70c2
Show file tree
Hide file tree
Showing 20 changed files with 730 additions and 198 deletions.
10 changes: 9 additions & 1 deletion tiger/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use std::rc::Rc;

use frame::Frame;
use symbol::{Strings, Symbol};
use temp::{Label, Temp};

#[derive(Debug)]
Expand All @@ -30,6 +33,10 @@ pub enum Instruction {
source: Vec<Temp>,
return_label: Label,
},
Debug {
filename: Symbol,
line: i32,
},
Operation {
assembly: String,
destination: Vec<Temp>,
Expand All @@ -52,8 +59,9 @@ pub enum Instruction {
}

impl Instruction {
pub fn to_string<F: Frame>(&self) -> String {
pub fn to_string<F: Frame>(&self, strings: Rc<Strings>) -> String {
match *self {
Instruction::Debug { filename, line } => format!("%line {} {}", line, strings.get(filename).expect("filename")),
Instruction::Label { ref assembly, .. } => assembly.clone(),
Instruction::Call { ref assembly, ref destination, ref source, .. } |
Instruction::Move { ref assembly, ref destination, ref source, .. } |
Expand Down
5 changes: 5 additions & 0 deletions tiger/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ pub enum Expr {
args: Vec<ExprWithPos>,
function: Box<ExprWithPos>,
},
CallWithStaticLink {
args: Vec<ExprWithPos>,
function: Box<ExprWithPos>,
},
Closure {
body: Box<ExprWithPos>,
params: Vec<FieldWithPos>,
Expand All @@ -72,6 +76,7 @@ pub enum Expr {
ClosurePointer {
label: Symbol,
},
DirectVariable(SymbolWithPos),
Field {
ident: SymbolWithPos,
this: Box<ExprWithPos>,
Expand Down
2 changes: 2 additions & 0 deletions tiger/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ impl Collector {
self.allocated += size;
unsafe {
let ptr = self.heap.as_ptr().add(offset) as *mut usize;
//println!("Allocation of size {} at {:?}", size, ptr);
data_layout.write_repr(ptr);
ptr as i64
}
Expand Down Expand Up @@ -354,6 +355,7 @@ impl Collector {
}

fn grow_heap(&mut self) {
println!("****************************************************************************************************Grow");
let old_heap = self.heap.as_ptr() as usize;

let addresses = stack_return_addresses();
Expand Down
28 changes: 25 additions & 3 deletions tiger/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use std::collections::BTreeMap;
use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet};
use std::rc::Rc;

use escape::{DepthEscape, EscapeEnv};
Expand All @@ -31,16 +32,37 @@ use symbol::{Strings, Symbol, Symbols};
use temp::Label;
use types::{Type, Unique};

#[derive(Clone, Debug, PartialEq)]
pub struct ClosureField {
pub ident: Symbol,
pub typ: Type,
}

impl Eq for ClosureField {
}

impl PartialOrd for ClosureField {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for ClosureField {
fn cmp(&self, other: &Self) -> Ordering {
self.ident.cmp(&other.ident)
}
}

#[derive(Clone, Debug)]
pub enum Entry<F: Clone + Frame> {
ClassField {
class: Type,
},
Fun {
access_outside_vars: bool,
external: bool,
label: Label,
level: Level<F>,
escaping_vars: BTreeSet<ClosureField>,
parameters: Vec<Type>,
pure: bool,
result: Type,
Expand Down Expand Up @@ -112,10 +134,10 @@ impl<F: Clone + Frame> Env<F> {
fn add_function(&mut self, name: &str, parameters: Vec<Type>, result: Type, pure: bool) {
let symbol = self.var_env.symbol(name);
let entry = Entry::Fun {
access_outside_vars: false,
external: true,
label: Label::with_name(name),
level: gen::outermost(), // FIXME: Might want to create a new level.
escaping_vars: BTreeSet::new(),
parameters,
pure,
result,
Expand Down
8 changes: 0 additions & 8 deletions tiger/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ pub enum Error {
BreakOutsideLoop {
pos: Pos,
},
CannotAccessOutsideVars {
pos: Pos,
},
CannotAssignInPureFun {
pos: Pos,
},
Expand Down Expand Up @@ -164,11 +161,6 @@ impl Error {
pos.show(symbols, terminal);
highlight_line(pos, symbols, terminal)?;
},
CannotAccessOutsideVars { pos } => {
eprintln!("Cannot convert into a closure a function that accesses variables declared outside of it{}", terminal.end_bold());
pos.show(symbols, terminal);
highlight_line(pos, symbols, terminal)?;
},
CannotAssignInPureFun { pos } => {
eprintln!("Cannot assign in pure functions{}", terminal.end_bold());
pos.show(symbols, terminal);
Expand Down
12 changes: 8 additions & 4 deletions tiger/src/escape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ impl EscapeFinder {
Declaration::Function(ref declarations) => {
for &WithPos { node: FuncDeclaration { ref params, ref body, .. }, .. } in declarations {
for param in params {
// TODO: param dig are considered escaping while they should not.
self.env.enter(param.node.name, DepthEscape {
depth,
depth: depth + 1,
escape: false,
});
}
Expand All @@ -75,7 +76,7 @@ impl EscapeFinder {
},
Declaration::Type(_) => (),
Declaration::VariableDeclaration { ref init, name, .. } => {
self.visit_exp(init, depth + 1); // TODO: do we really need to increment depth here?
self.visit_exp(init, depth);
self.env.enter(name, DepthEscape {
depth,
escape: false,
Expand All @@ -99,17 +100,19 @@ impl EscapeFinder {
Expr::Closure { ref body, ref params, .. } => {
for param in params {
self.env.enter(param.node.name, DepthEscape {
depth,
depth: depth + 1,
escape: false,
});
}
self.visit_exp(body, depth + 1);
},
Expr::Call { ref args, .. } => {
Expr::Call { ref args, ref function, .. } => {
self.visit_exp(function, depth);
for arg in args {
self.visit_exp(arg, depth);
}
},
Expr::CallWithStaticLink { .. } => unreachable!(),
Expr::ClosureParamField { ref ident, .. } | Expr::Field { ref ident, .. } | // TODO: does that make sense to look for the field here?
Expr::Variable(ref ident) => {
if let Some(ref mut var) = self.env.look_mut(ident.node) {
Expand All @@ -119,6 +122,7 @@ impl EscapeFinder {
}
},
Expr::ClosurePointer { .. } | Expr::FunctionPointer { .. } => (),
Expr::DirectVariable(_) => unreachable!(),
Expr::FunctionPointerCall { ref args, .. } => {
for arg in args {
self.visit_exp(arg, depth);
Expand Down
2 changes: 1 addition & 1 deletion tiger/src/frame/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ impl Frame for X86_64 {

for instruction in instructions.iter_mut().rev() {
match *instruction {
Instruction::Label { .. } => (),
Instruction::Debug { .. } | Instruction::Label { .. } => (),
Instruction::Call { ref mut source, .. } |
Instruction::Move { ref mut source, .. } |
Instruction::Operation { ref mut source, .. } =>
Expand Down
78 changes: 20 additions & 58 deletions tiger/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ impl<F: Frame> IR<F> {
#[derive(Debug)]
pub struct Level<F> {
pub current: Rc<RefCell<F>>,
parent: Option<Box<Level<F>>>,
pub is_closure: bool,
pub parent: Option<Box<Level<F>>>,
}

impl<F> Clone for Level<F> {
fn clone(&self) -> Self {
Self {
current: self.current.clone(),
is_closure: self.is_closure,
parent: self.parent.clone(),
}
}
Expand All @@ -109,6 +111,7 @@ impl<F: PartialEq> PartialEq for Level<F> {
pub fn outermost<F: Frame>() -> Level<F> {
Level {
current: Rc::new(RefCell::new(F::new(Label::new(), vec![]))),
is_closure: false,
parent: None,
}
}
Expand All @@ -118,6 +121,7 @@ impl<F: Frame> Level<F> {
formals.push(true); // for the static link.
Level {
current: Rc::new(RefCell::new(F::new(name, formals))),
is_closure: false,
parent: Some(Box::new(parent.clone())),
}
}
Expand Down Expand Up @@ -173,48 +177,9 @@ pub fn field_access<F: Frame>(var: Exp, field_index: usize, field_type: FieldTyp
}))
}

fn call<F: Clone + Frame + PartialEq>(function: Exp, mut arguments: Vec<Exp>, parent_level: &Level<F>,
current_level: &Level<F>, collectable_return_type: bool, in_closure: bool) -> Exp
fn call(function: Exp, arguments: Vec<Exp>, collectable_return_type: bool) -> Exp
{
if *current_level == *parent_level {
if in_closure {
panic!("Trying to access static link from inside a closure for a recursive call");
}

// For a recursive call, we simply pass the current static link, which represents the stack
// frame of the parent function.
let frame = current_level.current.borrow();
arguments.push(frame.exp(frame.formals().last().expect("static link").clone(), Exp::Temp(F::fp())));
}
else if current_level.parent.as_deref() == Some(parent_level) {
// When calling a function defined in the current frame, simply pass the current frame
// pointer for the static link.
arguments.push(Exp::Temp(F::fp()));
}
else {
// TODO: print an error as well.
if in_closure {
panic!("Trying to access static link from inside a closure");
}

// When calling a function defined in a parent frame, go up throught the static links.
let mut function_level = parent_level;
let mut var = Exp::Temp(F::fp());
loop {
if let Some(ref current_level) = current_level.parent {
if &**current_level == function_level {
break;
}
}
let frame = function_level.current.borrow();
var = frame.exp(frame.formals().last().expect("static link").clone(), var);
match function_level.parent {
Some(ref parent) => function_level = parent,
None => break,
}
}
arguments.push(var);
}
// TODO: remove this function.
Call {
arguments,
collectable_return_type,
Expand All @@ -223,10 +188,9 @@ fn call<F: Clone + Frame + PartialEq>(function: Exp, mut arguments: Vec<Exp>, pa
}
}

pub fn function_call<F: Clone + Frame + PartialEq>(label: &Label, arguments: Vec<Exp>, parent_level: &Level<F>,
current_level: &Level<F>, collectable_return_type: bool, in_closure: bool) -> Exp
pub fn function_call(label: &Label, arguments: Vec<Exp>, collectable_return_type: bool) -> Exp
{
call(Name(label.clone()), arguments, parent_level, current_level, collectable_return_type, in_closure)
call(Name(label.clone()), arguments, collectable_return_type)
}

pub fn function_pointer_call(function_pointer: Exp, arguments: Vec<Exp>, collectable_return_type: bool) -> Exp {
Expand All @@ -238,8 +202,7 @@ pub fn function_pointer_call(function_pointer: Exp, arguments: Vec<Exp>, collect
}
}

pub fn method_call<F: Clone + Frame + PartialEq>(index: usize, arguments: Vec<Exp>, parent_level: &Level<F>,
current_level: &Level<F>, collectable_return_type: bool, in_closure: bool) -> Exp
pub fn method_call<F: Clone + Frame + PartialEq>(index: usize, arguments: Vec<Exp>, collectable_return_type: bool) -> Exp
{
let vtable = Mem(Box::new(BinOp {
op: Plus,
Expand All @@ -251,7 +214,7 @@ pub fn method_call<F: Clone + Frame + PartialEq>(index: usize, arguments: Vec<Ex
left: Box::new(vtable),
right: Box::new(Const(F::WORD_SIZE * index as i64)),
}));
call(function_ptr, arguments, parent_level, current_level, collectable_return_type, in_closure)
call(function_ptr, arguments, collectable_return_type)
}

pub fn goto(label: Label) -> Exp {
Expand Down Expand Up @@ -298,13 +261,13 @@ pub fn if_expression<F: Clone + Frame>(test_expr: Exp, if_expr: Exp, else_expr:
)
}

pub fn init_array<F: Clone + Frame + PartialEq>(var: Option<Access<F>>, size_expr: Exp, is_pointer: Exp, init_expr: Exp, level: &Level<F>, in_closure: bool) -> Exp {
pub fn init_array<F: Clone + Frame + PartialEq>(var: Option<Access<F>>, size_expr: Exp, is_pointer: Exp, init_expr: Exp, level: &Level<F>) -> Exp {
// FIXME: it does many allocations for a 2D array.
let temp = Temp::new();
let result =
if let Some(var) = var.clone() {
let level = var.0.clone();
simple_var(var, &level, in_closure)
simple_var(var, &level)
}
else {
Exp::Temp(temp)
Expand Down Expand Up @@ -350,9 +313,9 @@ pub fn num(number: i64) -> Exp {
Const(number)
}

pub fn class_create<F: Frame + PartialEq>(var: Access<F>, data_layout: Exp, fields: Vec<Exp>, vtable_name: Label, in_closure: bool) -> Exp {
pub fn class_create<F: Frame + PartialEq>(var: Access<F>, data_layout: Exp, fields: Vec<Exp>, vtable_name: Label) -> Exp {
let level = var.0.clone();
let result = simple_var(var, &level, in_closure);
let result = simple_var(var, &level);

let mut sequence = Move(result.clone(), F::external_call("allocClass", vec![data_layout], true)).into();
sequence = Sequence(
Expand Down Expand Up @@ -457,20 +420,19 @@ pub fn relational_oper<F: Clone + Frame>(op: Operator, left: Exp, right: Exp, le
)
}

pub fn simple_var<F: Clone + Frame + PartialEq>(access: Access<F>, level: &Level<F>, in_closure: bool) -> Exp {
let mut function_level = level;
let var_level = access.0;
pub fn simple_var<F: Clone + Frame + PartialEq>(access: Access<F>, level: &Level<F>) -> Exp {
//let function_level = level;
let frame = level.current.borrow();
let mut var = Exp::Temp(F::fp());
// Add the offset of each parent frames (static link).
while function_level.current != var_level.current {
/*while function_level.current != var_level.current {
// TODO: remove this check.
if in_closure {
panic!("Trying to access static link from inside a closure");
}
var = frame.exp(function_level.current.borrow().formals().last().expect("static link").clone(), var);
function_level = function_level.parent.as_ref().unwrap_or_else(|| panic!("function level should have a parent"));
}
}*/
var = frame.exp(access.1, var);
var
}
Expand Down
Loading

0 comments on commit 2ea70c2

Please sign in to comment.