Skip to content

Codegen for ports and channels #279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ TASK_XFAILS := $(addprefix $(S)src/test/run-pass/, \
task-comm-12.rs \
task-comm-2.rs \
task-comm-9.rs \
task-comm-chan-nil.rs \
task-life-0.rs \
alt-type-simple.rs \
many.rs)
Expand Down
4 changes: 1 addition & 3 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ TASK_XFAILS := test/run-pass/task-comm-8.rs \
test/run-pass/task-comm-12.rs \
test/run-pass/task-comm-2.rs \
test/run-pass/task-comm-9.rs \
test/run-pass/task-comm-chan-nil.rs \
test/run-pass/task-life-0.rs \
test/run-pass/alt-type-simple.rs \
test/run-pass/many.rs
Expand Down Expand Up @@ -530,9 +531,6 @@ TEST_XFAILS_RUSTC := $(addprefix test/run-pass/, \
task-comm-15.rs \
task-comm-2.rs \
task-comm-3.rs \
task-comm-4.rs \
task-comm-5.rs \
task-comm-6.rs \
task-comm-7.rs \
task-comm-8.rs \
task-comm-9.rs \
Expand Down
162 changes: 162 additions & 0 deletions src/comp/middle/trans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,14 @@ fn T_box(TypeRef t) -> TypeRef {
ret T_struct(vec(T_int(), t));
}

fn T_port(TypeRef t) -> TypeRef {
ret T_struct(vec(T_int())); // Refcount
}

fn T_chan(TypeRef t) -> TypeRef {
ret T_struct(vec(T_int())); // Refcount
}

fn T_crate(type_names tn) -> TypeRef {
auto s = "crate";
if (tn.name_has_type(s)) {
Expand Down Expand Up @@ -623,6 +631,12 @@ fn type_of_inner(@crate_ctxt cx, @ty.t t, bool boxed) -> TypeRef {
case (ty.ty_vec(?mt)) {
llty = T_ptr(T_vec(type_of_inner(cx, mt.ty, true)));
}
case (ty.ty_port(?t)) {
llty = T_ptr(T_port(type_of_inner(cx, t, true)));
}
case (ty.ty_chan(?t)) {
llty = T_ptr(T_chan(type_of_inner(cx, t, true)));
}
case (ty.ty_tup(?elts)) {
let vec[TypeRef] tys = vec();
for (ty.mt elt in elts) {
Expand Down Expand Up @@ -1609,6 +1623,28 @@ fn make_drop_glue(@block_ctxt cx, ValueRef v, @ty.t t) -> result {
T_int(), C_int(0));
}

case (ty.ty_port(_)) {
fn hit_zero(@block_ctxt cx, ValueRef v) -> result {
ret trans_upcall(cx, "upcall_del_port",
vec(vp2i(cx, v)));
}
ret decr_refcnt_and_if_zero(cx, v,
bind hit_zero(_, v),
"free port",
T_int(), C_int(0));
}

case (ty.ty_chan(_)) {
fn hit_zero(@block_ctxt cx, ValueRef v) -> result {
ret trans_upcall(cx, "upcall_del_chan",
vec(vp2i(cx, v)));
}
ret decr_refcnt_and_if_zero(cx, v,
bind hit_zero(_, v),
"free chan",
T_int(), C_int(0));
}

case (ty.ty_obj(_)) {
fn hit_zero(@block_ctxt cx, ValueRef v) -> result {

Expand Down Expand Up @@ -4496,6 +4532,22 @@ fn trans_expr(@block_ctxt cx, @ast.expr e) -> result {
ret trans_be(cx, e);
}

case (ast.expr_port(?ann)) {
ret trans_port(cx, ann);
}

case (ast.expr_chan(?e, ?ann)) {
ret trans_chan(cx, e, ann);
}

case (ast.expr_send(?lhs, ?rhs, ?ann)) {
ret trans_send(cx, lhs, rhs, ann);
}

case (ast.expr_recv(?lhs, ?rhs, ?ann)) {
ret trans_recv(cx, lhs, rhs, ann);
}

// lval cases fall through to trans_lval and then
// possibly load the result (if it's non-structural).

Expand Down Expand Up @@ -4667,6 +4719,116 @@ fn trans_be(@block_ctxt cx, @ast.expr e) -> result {
ret trans_ret(cx, some(e));
}

fn trans_port(@block_ctxt cx, ast.ann ann) -> result {

auto t = node_ann_type(cx.fcx.ccx, ann);
auto unit_ty;
alt (t.struct) {
case (ty.ty_port(?t)) {
unit_ty = t;
}
case (_) {
cx.fcx.ccx.sess.bug("non-port type in trans_port");
fail;
}
}

auto llunit_ty = type_of(cx.fcx.ccx, unit_ty);

auto bcx = cx;
auto unit_sz = size_of(bcx, unit_ty);
bcx = unit_sz.bcx;
auto sub = trans_upcall(bcx, "upcall_new_port", vec(unit_sz.val));
bcx = sub.bcx;
auto llty = type_of(cx.fcx.ccx, t);
auto port_val = vi2p(bcx, sub.val, llty);
auto dropref = clean(bind drop_ty(_, port_val, t));
find_scope_cx(bcx).cleanups += vec(dropref);

ret res(bcx, port_val);
}

fn trans_chan(@block_ctxt cx, @ast.expr e, ast.ann ann) -> result {

auto bcx = cx;
auto prt = trans_expr(bcx, e);
bcx = prt.bcx;

auto prt_val = vp2i(bcx, prt.val);
auto sub = trans_upcall(bcx, "upcall_new_chan", vec(prt_val));
bcx = sub.bcx;

auto chan_ty = node_ann_type(bcx.fcx.ccx, ann);
auto chan_llty = type_of(bcx.fcx.ccx, chan_ty);
auto chan_val = vi2p(bcx, sub.val, chan_llty);
auto dropref = clean(bind drop_ty(_, chan_val, chan_ty));
find_scope_cx(bcx).cleanups += vec(dropref);

ret res(bcx, chan_val);
}

fn trans_send(@block_ctxt cx, @ast.expr lhs, @ast.expr rhs,
ast.ann ann) -> result {

auto bcx = cx;
auto chn = trans_expr(bcx, lhs);
bcx = chn.bcx;
auto data = trans_expr(bcx, rhs);
bcx = data.bcx;

auto chan_ty = node_ann_type(cx.fcx.ccx, ann);
auto unit_ty;
alt (chan_ty.struct) {
case (ty.ty_chan(?t)) {
unit_ty = t;
}
case (_) {
bcx.fcx.ccx.sess.bug("non-chan type in trans_send");
fail;
}
}

auto data_alloc = alloc_ty(bcx, unit_ty);
bcx = data_alloc.bcx;
auto data_tmp = copy_ty(bcx, INIT, data_alloc.val, data.val, unit_ty);
bcx = data_tmp.bcx;

find_scope_cx(bcx).cleanups +=
vec(clean(bind drop_ty(_, data_alloc.val, unit_ty)));

auto sub = trans_upcall(bcx, "upcall_send",
vec(vp2i(bcx, chn.val),
vp2i(bcx, data_alloc.val)));
bcx = sub.bcx;

ret res(bcx, chn.val);
}

fn trans_recv(@block_ctxt cx, @ast.expr lhs, @ast.expr rhs,
ast.ann ann) -> result {

auto bcx = cx;
auto data = trans_lval(bcx, lhs);
check (data.is_mem);
bcx = data.res.bcx;
auto prt = trans_expr(bcx, rhs);
bcx = prt.bcx;

auto sub = trans_upcall(bcx, "upcall_recv",
vec(vp2i(bcx, data.res.val),
vp2i(bcx, prt.val)));
bcx = sub.bcx;

auto unit_ty = node_ann_type(cx.fcx.ccx, ann);
auto data_load = load_scalar_or_boxed(bcx, data.res.val, unit_ty);
auto cp = copy_ty(bcx, DROP_EXISTING, data.res.val, data_load, unit_ty);
bcx = cp.bcx;

// TODO: Any cleanup need to be done here?

ret res(bcx, data.res.val);
}

fn init_local(@block_ctxt cx, @ast.local local) -> result {

// Make a note to drop this slot on the way out.
Expand Down
6 changes: 6 additions & 0 deletions src/comp/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ fn type_is_boxed(@t ty) -> bool {
case (ty_str) { ret true; }
case (ty_vec(_)) { ret true; }
case (ty_box(_)) { ret true; }
case (ty_port(_)) { ret true; }
case (ty_chan(_)) { ret true; }
case (_) { ret false; }
}
fail;
Expand Down Expand Up @@ -759,6 +761,10 @@ fn expr_ty(@ast.expr expr) -> @t {
case (ast.expr_index(_, _, ?ann)) { ret ann_to_type(ann); }
case (ast.expr_path(_, _, ?ann)) { ret ann_to_type(ann); }
case (ast.expr_ext(_, _, _, _, ?ann)) { ret ann_to_type(ann); }
case (ast.expr_port(?ann)) { ret ann_to_type(ann); }
case (ast.expr_chan(_, ?ann)) { ret ann_to_type(ann); }
case (ast.expr_send(_, _, ?ann)) { ret ann_to_type(ann); }
case (ast.expr_recv(_, _, ?ann)) { ret ann_to_type(ann); }

case (ast.expr_fail) { ret plain_ty(ty_nil); }
case (ast.expr_log(_)) { ret plain_ty(ty_nil); }
Expand Down
122 changes: 122 additions & 0 deletions src/test/run-pass/task-comm-16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// -*- rust -*-

// Tests of ports and channels on various types

impure fn test_rec() {
type r = rec(int val0, u8 val1, char val2);

let port[r] po = port();
let chan[r] ch = chan(po);
let r r0 = rec(val0 = 0, val1 = 1u8, val2 = '2');

ch <| r0;

let r r1;
r1 <- po;

check (r1.val0 == 0);
check (r1.val1 == 1u8);
check (r1.val2 == '2');
}

impure fn test_vec() {
let port[vec[int]] po = port();
let chan[vec[int]] ch = chan(po);
let vec[int] v0 = vec(0, 1, 2);

ch <| v0;

let vec[int] v1;
v1 <- po;

check (v1.(0) == 0);
check (v1.(1) == 1);
check (v1.(2) == 2);
}

impure fn test_str() {
let port[str] po = port();
let chan[str] ch = chan(po);
let str s0 = "test";

ch <| s0;

let str s1;
s1 <- po;

check (s1.(0) as u8 == 't' as u8);
check (s1.(1) as u8 == 'e' as u8);
check (s1.(2) as u8 == 's' as u8);
check (s1.(3) as u8 == 't' as u8);
}

impure fn test_tup() {
type t = tup(int, u8, char);

let port[t] po = port();
let chan[t] ch = chan(po);
let t t0 = tup(0, 1u8, '2');

ch <| t0;

let t t1;
t1 <- po;

check (t0._0 == 0);
check (t0._1 == 1u8);
check (t0._2 == '2');
}

impure fn test_tag() {
tag t {
tag1;
tag2(int);
tag3(int, u8, char);
}

let port[t] po = port();
let chan[t] ch = chan(po);

ch <| tag1;
ch <| tag2(10);
ch <| tag3(10, 11u8, 'A');

let t t1;

t1 <- po;
check (t1 == tag1);
t1 <- po;
check (t1 == tag2(10));
t1 <- po;
check (t1 == tag3(10, 11u8, 'A'));
}

impure fn test_chan() {
let port[chan[int]] po = port();
let chan[chan[int]] ch = chan(po);

let port[int] po0 = port();
let chan[int] ch0 = chan(po0);

ch <| ch0;

let chan[int] ch1;
ch1 <- po;

// Does the transmitted channel still work?
ch1 <| 10;

let int i;
i <- po0;

check (i == 10);
}

impure fn main() {
test_rec();
test_vec();
test_str();
test_tup();
test_tag();
test_chan();
}
17 changes: 17 additions & 0 deletions src/test/run-pass/task-comm-chan-nil.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// -*- rust -*-

// rustboot can't transmit nils across channels because they don't have
// any size, but rustc currently can because they do have size. Whether
// or not this is desirable I don't know, but here's a regression test.

impure fn main() {
let port[()] po = port();
let chan[()] ch = chan(po);

ch <| ();

let () n;
n <- po;

check (n == ());
}