Commit 47b56f7d authored by qinsoon's avatar qinsoon

a test to construct a factorial fucntion

parent af6c5e98
#![allow(dead_code)]
#![allow(unused_variables)]
use ast::ptr::P;
use ast::types::*;
use std::sync::Arc;
pub type WPID = usize;
pub type MuID = usize;
......@@ -8,9 +11,15 @@ pub type Address = usize; // TODO: replace this with Address(usize)
#[derive(Clone)]
pub struct SSAVar {
id: MuID,
tag:Option<MuTag>,
ty: Arc<MuType_>
pub id: MuID,
pub tag: MuTag,
pub ty: P<MuType_>
}
#[derive(Clone)]
pub enum Value {
SSAVar(SSAVar),
Constant(MuConstant)
}
#[derive(Copy, Clone)]
......@@ -24,18 +33,6 @@ pub enum MemoryOrder {
SeqCst
}
#[derive(Clone)]
pub enum Literal {
LitInt(usize, Arc<MuType_>),
LitFP(f64, Arc<MuType_>),
LitFPNaN(Arc<MuType_>),
LitFPInfPos(Arc<MuType_>),
LitFPInfNeg(Arc<MuType_>),
LitNull(Arc<MuType_>)
}
#[derive(Copy, Clone)]
pub enum CallConvention {
Mu,
......@@ -47,54 +44,64 @@ pub enum ForeignFFI {
C
}
#[derive(Clone)]
pub struct CallData {
func: SSAVar,
args: Vec<SSAVar>,
convention: CallConvention
pub func: P<SSAVar>,
pub args: Vec<P<Value>>,
pub convention: CallConvention
}
#[derive(Clone)]
pub struct Block<'func> {
args: Vec<SSAVar>,
body: Vec<Instruction>,
exit: Terminal<'func>,
keepalives: Vec<SSAVar>
pub struct Block {
label: MuTag,
content: Option<BlockContent>
}
#[derive(Clone)]
pub struct TerminationData<'func> {
normal_dest: Destination<'func>,
exn_dest: Destination<'func>
impl Block {
pub fn new(label: MuTag) -> Block {
Block{label: label, content: None}
}
pub fn set_content(&mut self, v: BlockContent) {
self.content = Some(v);
}
}
pub struct BlockContent {
pub args: Vec<P<Value>>,
pub body: Vec<Instruction>,
pub exit: Terminal,
pub keepalives: Option<Vec<P<SSAVar>>>
}
pub struct TerminationData {
normal_dest: Destination,
exn_dest: Destination
}
#[derive(Clone)]
pub enum DestArg {
Normal(SSAVar),
Normal(P<Value>),
Freshbound(usize)
}
#[derive(Clone)]
pub struct Destination<'func> {
block: &'func Block<'func>,
args: Vec<DestArg>
pub struct Destination {
pub target: MuTag,
pub args: Vec<DestArg>
}
#[derive(Clone)]
pub enum Value {
pub enum Constant {
Int(usize, usize),
IRef(Arc<MuType_>, Address),
IRef(P<MuType_>, Address),
FloatV(f32),
DoubleV(f64),
VectorV(Vec<Value>),
VectorV(Vec<Constant>),
FuncRefV(Address),
UFuncRefV(Address)
}
#[derive(Clone)]
pub enum Expression {
BinOp(BinOp, SSAVar, SSAVar),
Value(Value),
BinOp(BinOp, P<Value>, P<Value>),
CmpOp(CmpOp, P<Value>, P<Value>),
Constant(P<Constant>),
// memory operations
......@@ -103,136 +110,136 @@ pub enum Expression {
is_strong: bool,
success_order: MemoryOrder,
fail_order: MemoryOrder,
mem_loc: SSAVar,
expected_value: SSAVar,
desired_value: SSAVar
mem_loc: P<SSAVar>,
expected_value: P<Value>,
desired_value: P<Value>
},
AtomicRMW{
is_iref: bool, // T for iref, F for ptr
order: MemoryOrder,
op: AtomicRMWOp,
mem_loc: SSAVar,
value: SSAVar // operand for op
mem_loc: P<Value>,
value: P<Value> // operand for op
},
Fence(MemoryOrder),
// allocation operations
New(Arc<MuType_>),
AllocA(Arc<MuType_>),
New(P<MuType_>),
AllocA(P<MuType_>),
NewHybrid{ // hybrid type, var part length
ty: Arc<MuType_>,
var_len: SSAVar
ty: P<MuType_>,
var_len: P<Value>
},
AllocAHybrid{
ty: Arc<MuType_>,
var_len: SSAVar
ty: P<MuType_>,
var_len: P<Value>
},
NewStack{
func: SSAVar
func: P<Value>
},
NewThread{
stack: SSAVar,
args: Vec<SSAVar>
stack: P<Value>,
args: Vec<P<Value>>
},
NewThreadExn{ // NewThreadExn SSAVar (* stack id *) SSAVar (* exception value *) ???
stack: SSAVar,
exn: SSAVar
stack: P<Value>,
exn: P<Value>
},
PushFrame{
stack: SSAVar,
func: SSAVar
stack: P<Value>,
func: P<Value>
},
PopFrame{
stack: SSAVar
stack: P<Value>
}
}
#[derive(Clone)]
pub enum Instruction {
Assign{
left: Vec<SSAVar>,
left: Vec<P<Value>>,
right: Expression
},
Load{
dest: SSAVar,
dest: P<SSAVar>,
is_iref: bool,
mem_loc: SSAVar,
mem_loc: P<Value>,
order: MemoryOrder
},
Store{
src: SSAVar,
src: P<SSAVar>,
is_iref: bool,
mem_loc: SSAVar,
mem_loc: P<Value>,
order: MemoryOrder
}
}
#[derive(Clone)]
pub enum Terminal<'func> {
Return(Vec<SSAVar>),
pub enum Terminal {
Return(Vec<P<Value>>),
ThreadExit,
Throw(Vec<SSAVar>),
Throw(Vec<P<Value>>),
TailCall(CallData),
Branch1(Destination<'func>),
Branch1(Destination),
Branch2{
cond: SSAVar,
true_dest: Destination<'func>,
false_dest: Destination<'func>
cond: P<Value>,
true_dest: Destination,
false_dest: Destination
},
Watchpoint, // TODO: Watchpoint ((wpid # destination) option) termination_data
WPBranch{
wp: WPID,
disable_dest: Destination<'func>,
enable_dest: Destination<'func>
disable_dest: Destination,
enable_dest: Destination
},
Call{
data: CallData,
normal_dest: Destination<'func>,
exn_dest: Destination<'func>
normal_dest: Destination,
exn_dest: Option<Destination>
},
SwapStack{
stack: SSAVar,
args: Vec<SSAVar>,
normal_dest: Destination<'func>,
exn_dest: Destination<'func>
stack: P<Value>,
args: Vec<P<Value>>,
normal_dest: Destination,
exn_dest: Destination
},
Switch{
cond: SSAVar,
default: Destination<'func>,
branches: Vec<(Value, Destination<'func>)>
cond: P<Value>,
default: Destination,
branches: Vec<(P<Constant>, Destination)>
},
ExnInstruction{
inner: Expression,
term: TerminationData<'func>
term: TerminationData
}
}
#[derive(Clone)]
pub enum Declaration<'global> {
ConstDecl{
const_name: MuTag,
ty: Arc<MuType_>,
val: Value
},
TypeDef{
type_name: MuTag,
ty: Arc<MuType_>
},
FunctionSignature{
sig_name: MuTag,
ret_tys: Vec<Arc<MuType_>>,
arg_tys: Vec<Arc<MuType_>>
},
FuncDef{
fn_name: MuTag,
sig_name: MuTag,
label: MuTag, // ?
blocks: Vec<(MuTag, Block<'global>)>
}
pub struct MuConstant{
ty: P<MuType_>,
val: Constant
}
pub struct MuFunction {
pub fn_name: MuTag,
pub sig: P<MuFuncSig>,
pub entry: MuTag,
pub blocks: Vec<(MuTag, Block)>
}
pub fn declare_const(const_name: MuTag, ty: P<MuType_>, val: Constant) -> Value {
Value::Constant(MuConstant{ty: ty, val: val})
}
pub fn declare_type(type_name: MuTag, ty: P<MuType_>) -> P<MuType_> {
ty
}
pub fn declare_func_sig(sig_name: MuTag, ret_tys: Vec<P<MuType_>>, arg_tys: Vec<P<MuType_>>) -> MuFuncSig {
MuFuncSig::new(ret_tys, arg_tys)
}
pub fn declare_func (fn_name: MuTag, sig: P<MuFuncSig>, entry: MuTag, blocks: Vec<(MuTag, Block)>) -> MuFunction {
MuFunction{fn_name: fn_name, sig: sig, entry: entry, blocks: blocks}
}
#[derive(Copy, Clone)]
......
pub mod types;
pub mod ir;
\ No newline at end of file
pub mod ir;
pub mod ptr;
\ No newline at end of file
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! The AST pointer
//!
//! Provides `P<T>`, a frozen owned smart pointer, as a replacement for `@T` in
//! the AST.
//!
//! # Motivations and benefits
//!
//! * **Identity**: sharing AST nodes is problematic for the various analysis
//! passes (e.g. one may be able to bypass the borrow checker with a shared
//! `ExprAddrOf` node taking a mutable borrow). The only reason `@T` in the
//! AST hasn't caused issues is because of inefficient folding passes which
//! would always deduplicate any such shared nodes. Even if the AST were to
//! switch to an arena, this would still hold, i.e. it couldn't use `&'a T`,
//! but rather a wrapper like `P<'a, T>`.
//!
//! * **Immutability**: `P<T>` disallows mutating its inner `T`, unlike `Box<T>`
//! (unless it contains an `Unsafe` interior, but that may be denied later).
//! This mainly prevents mistakes, but can also enforces a kind of "purity".
//!
//! * **Efficiency**: folding can reuse allocation space for `P<T>` and `Vec<T>`,
//! the latter even when the input and output types differ (as it would be the
//! case with arenas or a GADT AST using type parameters to toggle features).
//!
//! * **Maintainability**: `P<T>` provides a fixed interface - `Deref`,
//! `and_then` and `map` - which can remain fully functional even if the
//! implementation changes (using a special thread-local heap, for example).
//! Moreover, a switch to, e.g. `P<'a, T>` would be easy and mostly automated.
use std::fmt::{self, Display, Debug};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ptr;
/// An owned smart pointer.
pub struct P<T> {
ptr: Box<T>
}
#[allow(non_snake_case)]
/// Construct a `P<T>` from a `T` value.
pub fn P<T: 'static>(value: T) -> P<T> {
P {
ptr: Box::new(value)
}
}
impl<T: 'static> P<T> {
/// Move out of the pointer.
/// Intended for chaining transformations not covered by `map`.
pub fn and_then<U, F>(self, f: F) -> U where
F: FnOnce(T) -> U,
{
f(*self.ptr)
}
}
impl<T> Deref for P<T> {
type Target = T;
fn deref<'a>(&'a self) -> &'a T {
&*self.ptr
}
}
impl<T: 'static + Clone> Clone for P<T> {
fn clone(&self) -> P<T> {
P((**self).clone())
}
}
impl<T: PartialEq> PartialEq for P<T> {
fn eq(&self, other: &P<T>) -> bool {
**self == **other
}
}
impl<T: Eq> Eq for P<T> {}
impl<T: Debug> Debug for P<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<T: Display> Display for P<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl<T> fmt::Pointer for P<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Pointer::fmt(&self.ptr, f)
}
}
impl<T: Hash> Hash for P<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
This diff is collapsed.
extern crate mu;
#[cfg(test)]
mod test_ir {
use mu::ast::types::*;
use mu::ast::ir::*;
use mu::ast::ptr::*;
#[test]
#[allow(unused_variables)]
fn factorial() {
// .typedef @int_64 = int<64>
// .typedef @int_1 = int<1>
// .typedef @float = float
// .typedef @double = double
// .typedef @void = void
// .typedef @int_8 = int<8>
// .typedef @int_32 = int<32>
let type_def_int64 = declare_type("int_64", P(MuType_::int(64)));
let type_def_int1 = declare_type("int_1", P(MuType_::int(1)));
let type_def_float = declare_type("float", P(MuType_::float()));
let type_def_double = declare_type("double", P(MuType_::double()));
let type_def_void = declare_type("void", P(MuType_::void()));
let type_def_int8 = declare_type("int8", P(MuType_::int(8)));
let type_def_int32 = declare_type("int32", P(MuType_::int(32)));
// .const @int_64_1 <@int_64> = 1
let const_def_int64_1 = P(declare_const("int64_1", type_def_int64.clone(), Constant::Int(64, 1)));
// .funcsig @fac_sig = (@int_64) -> (@int_64)
let fac_sig = P(declare_func_sig("fac_sig", vec![type_def_int64.clone()], vec![type_def_int64.clone()]));
// .funcdef @fac VERSION @fac_v1 <@fac_sig>
let fac_func_ref = P(MuType_::funcref(fac_sig.clone()));
// %blk_0(<@int_64> %n_3):
let mut blk_0 = Block::new("blk_0");
let blk_0_n_3 = P(Value::SSAVar(SSAVar{id: 0, tag: "n_3", ty: type_def_int64.clone()}));
// %v48 = EQ <@int_64> %n_3 @int_64_1
let blk_0_v48 = P(Value::SSAVar(SSAVar{id: 1, tag: "v48", ty: type_def_int64.clone()}));
let blk_0_v48_expr = Expression::CmpOp(
CmpOp::EQ,
blk_0_n_3.clone(),
const_def_int64_1.clone()
);
let blk_0_inst0 = Instruction::Assign{left: vec![blk_0_v48.clone()], right: blk_0_v48_expr};
// BRANCH2 %v48 %blk_2(@int_64_1) %blk_1(%n_3)
let blk_0_term = Terminal::Branch2{
cond: blk_0_v48.clone(),
true_dest: Destination {
target: "blk_2",
args: vec![DestArg::Normal(const_def_int64_1.clone())]
},
false_dest: Destination {
target: "blk_1",
args: vec![DestArg::Normal(blk_0_n_3.clone())]
}
};
let blk_0_content = BlockContent {
args: vec![blk_0_n_3.clone()],
body: vec![blk_0_inst0],
exit: blk_0_term,
keepalives: None
};
blk_0.set_content(blk_0_content);
// %blk_2(<@int_64> %v53):
let mut blk_2 = Block::new("blk_2");
let blk_2_v53 = P(Value::SSAVar(SSAVar{id: 2, tag: "v53", ty: type_def_int64.clone()}));
// RET %v53
let blk_2_term = Terminal::Return(vec![blk_2_v53.clone()]);
let blk_2_content = BlockContent {
args: vec![blk_2_v53.clone()],
body: vec![],
exit: blk_2_term,
keepalives: None
};
blk_2.set_content(blk_2_content);
// %blk_1(<@int_64> %n_3):
let mut blk_1 = Block::new("blk_1");
let blk_1_n_3 = P(Value::SSAVar(SSAVar{id: 3, tag: "n_3", ty: type_def_int64.clone()}));
// %v50 = SUB <@int_64> %n_3 @int_64_1
let blk_1_v50 = P(Value::SSAVar(SSAVar{id: 4, tag: "v50", ty: type_def_int64.clone()}));
let blk_1_v50_expr = Expression::BinOp(
BinOp::Sub,
blk_1_n_3.clone(),
const_def_int64_1.clone()
);
let blk_1_inst0 = Instruction::Assign{left: vec![blk_1_v50.clone()], right: blk_1_v50_expr};
// %v51 = CALL <@fac_sig> @fac (%v50)
let blk_1_v51 = P(Value::SSAVar(SSAVar{id: 5, tag: "v51", ty: type_def_int64.clone()}));
let blk_1_term = Terminal::Call {
data: CallData {
func: P(SSAVar{id: 6, tag: "fac", ty: fac_func_ref.clone()}),
args: vec![blk_1_v50.clone()],
convention: CallConvention::Mu
},
normal_dest: Destination {
target: "blk_1_cont",
args: vec![DestArg::Normal(blk_1_n_3.clone()), DestArg::Freshbound(0)]
},
exn_dest: None
};
let blk_1_content = BlockContent {
args: vec![blk_1_n_3.clone()],
body: vec![blk_1_inst0],
exit: blk_1_term,
keepalives: None
};
blk_1.set_content(blk_1_content);
// %blk_1_cont(<@int_64> %n_3, <@int_64> %v51):
let mut blk_1_cont = Block::new("blk_1_cont");
let blk_1_cont_n_3 = P(Value::SSAVar(SSAVar{id: 7, tag: "n_3", ty: type_def_int64.clone()}));
let blk_1_cont_v51 = P(Value::SSAVar(SSAVar{id: 8, tag: "v51", ty: type_def_int64.clone()}));
// %v52 = MUL <@int_64> %n_3 %v51
let blk_1_cont_v52 = P(Value::SSAVar(SSAVar{id: 9, tag: "v52", ty: type_def_int64.clone()}));
let blk_1_cont_v52_expr = Expression::BinOp(
BinOp::Mul,
blk_1_cont_n_3.clone(),
blk_1_cont_v52.clone()
);
let blk_1_cont_inst0 = Instruction::Assign{left: vec![blk_1_cont_v52.clone()], right: blk_1_cont_v52_expr};
let blk_1_cont_term = Terminal::Branch1 (
Destination {
target: "blk_2",
args: vec![DestArg::Normal(blk_1_cont_v52.clone())]
}
);
let blk_1_cont_content = BlockContent {
args: vec![blk_1_cont_n_3.clone(), blk_1_cont_v52.clone()],
body: vec![blk_1_cont_inst0],
exit: blk_1_cont_term,
keepalives: None
};
blk_1_cont.set_content(blk_1_cont_content);
// wrap into a function
let func_fac = declare_func("fac", fac_sig.clone(), "blk_0", vec![
("blk_0", blk_0),
("blk_1", blk_1),
("blk_1_cont", blk_1_cont),
("blk_2", blk_2)
]
);
}
}
\ No newline at end of file
extern crate mu;
#[cfg(test)]
mod tests {
use mu::ast::ptr::*;
use mu::ast::types::*;
macro_rules! assert_type (
($test:expr, $expect: expr) => (
assert_eq!(format!("{:?}", $test), $expect)
)
);
macro_rules! println_type (
($test:expr) => (
println!("{:?}", $test)
)
);
/// create one of each MuType_
fn create_types() -> Vec<P<MuType_>> {
let mut types = vec![];
let t0 = MuType_::int(8);
types.push(P(t0));
let t1 = MuType_::float();
types.push(P(t1));
let t2 = MuType_::double();
types.push(P(t2));
let t3 = MuType_::muref(types[0].clone());
types.push(P(t3));
let t4 = MuType_::iref(types[0].clone());
types.push(P(t4));
let t5 = MuType_::weakref(types[0].clone());
types.push(P(t5));
let t6 = MuType_::uptr(types[0].clone());
types.push(P(t6));
let t7 = MuType_::mustruct("MyStructTag1", vec![types[0].clone(), types[1].clone()]);
types.push(P(t7));
let t8 = MuType_::array(types[0].clone(), 5);
types.push(P(t8));
let t9 = MuType_::hybrid(vec![types[7].clone(), types[1].clone()], types[0].clone());
types.push(P(t9));
let t10 = MuType_::void();
types.push(P(t10));
let t11 = MuType_::threadref();
types.push(P(t11));
let t12 = MuType_::stackref();
types.push(P(t12));
let t13 = MuType_::tagref64();
types.push(P(t13));
let t14 = MuType_::vector(types[0].clone(), 5);
types.push(P(t14));
let sig = P(MuFuncSig::new(vec![types[10].clone()], vec![types[0].clone(), types[0].clone()]));
let t15 = MuType_::funcref(sig.clone());
types.push(P(t15));
let t16 = MuType_::ufuncptr(sig.clone());
types.push(P(t16));
types
}
#[test]
#[allow(unused_variables)]
fn test_type_constructors() {
let types = create_types();
assert_type!(*types[0], "Int(8)");
assert_type!(*types[1], "Float");
assert_type!(*types[2], "Double");
assert_type!(*types[3], "Ref(Int(8))");
assert_type!(*types[4], "IRef(Int(8))");
assert_type!(*types[5], "WeakRef(Int(8))");
assert_type!(*types[6], "UPtr(Int(8))");
assert_type!(*types[7], "Struct(\"MyStructTag1\")");
{
let map = STRUCT_TAG_MAP.read().unwrap();
let t7_struct_ty = map.get("MyStructTag1").unwrap();
assert_type!(t7_struct_ty, "StructType_ { tys: [Int(8), Float] }");
}
assert_type!(*types[8], "Array(Int(8), 5)");
assert_type!(*types[9], "Hybrid([Struct(\"MyStructTag1\"), Float], Int(8))");
assert_type!(*types[10], "Void");
assert_type!(*types[11], "ThreadRef");
assert_type!(*types[12], "StackRef");
assert_type!(*types[13], "Tagref64");
assert_type!(*types[14], "Vector(Int(8), 5)");
assert_type!(*types[15], "FuncRef(MuFuncSig { ret_tys: [Void], arg_tys: [Int(8), Int(8)] })");
assert_type!(*types[16], "UFuncPtr(MuFuncSig { ret_tys: [Void], arg_tys: [Int(8), Int(8)] })");
}
#[test]
fn test_cyclic_struct() {
// .typedef @cyclic_struct_ty = struct<ref<@cyclic_struct_ty> int<32>>
let ty = P(MuType_::mustruct_empty("MyStructTag2"));
let ref_ty = P(MuType_::muref(ty.clone()));
let i32_ty = P(MuType_::int(32));
{