Skip to content

Commit a334ada

Browse files
committed
Thread "self" through the stack. Backwarding! Closes #702.
1 parent b2cac5a commit a334ada

File tree

4 files changed

+87
-71
lines changed

4 files changed

+87
-71
lines changed

src/comp/middle/trans.rs

+78-20
Original file line numberDiff line numberDiff line change
@@ -6464,6 +6464,44 @@ fn trans_fn(cx: @local_ctxt, sp: &span, f: &ast::_fn, llfndecl: ValueRef,
64646464
log_fn_time(cx.ccx, str::connect_ivec(cx.path, "::"), start, end);
64656465
}
64666466

6467+
// Update a self-stack structure ([[wrapper_self_pair], self_pair*]) to
6468+
// [[backwarding_vtbl*, inner_obj_body*], outer_obj*].
6469+
//
6470+
// We do this when we're receiving the outer object in a forwarding function
6471+
// via the llenv argument, and we want the forwarding function to call a
6472+
// method on a "self" that's inner-obj-shaped, but we also want to hold onto
6473+
// the outer obj for potential use later by backwarding functions.
6474+
fn populate_self_stack(bcx: @block_ctxt,
6475+
self_stack: ValueRef, outer_obj: ValueRef,
6476+
backwarding_vtbl: ValueRef, inner_obj_body: ValueRef)
6477+
-> ValueRef {
6478+
6479+
// Drop the outer obj into the second slot.
6480+
let self_pair_ptr = bcx.build.GEP(self_stack,
6481+
~[C_int(0),
6482+
C_int(1)]);
6483+
bcx.build.Store(outer_obj, self_pair_ptr);
6484+
6485+
// Drop in the backwarding vtbl.
6486+
let wrapper_pair = bcx.build.GEP(self_stack,
6487+
~[C_int(0),
6488+
C_int(0)]);
6489+
let wrapper_vtbl_ptr = bcx.build.GEP(wrapper_pair,
6490+
~[C_int(0),
6491+
C_int(0)]);
6492+
let backwarding_vtbl_cast =
6493+
bcx.build.PointerCast(backwarding_vtbl, T_ptr(T_empty_struct()));
6494+
bcx.build.Store(backwarding_vtbl_cast, wrapper_vtbl_ptr);
6495+
6496+
// Drop in the inner obj body.
6497+
let wrapper_body_ptr = bcx.build.GEP(wrapper_pair,
6498+
~[C_int(0),
6499+
C_int(1)]);
6500+
bcx.build.Store(inner_obj_body, wrapper_body_ptr);
6501+
6502+
ret self_stack;
6503+
}
6504+
64676505
// process_bkwding_mthd: Create the backwarding function that appears in a
64686506
// backwarding vtable slot.
64696507
//
@@ -6505,8 +6543,24 @@ fn process_bkwding_mthd(cx: @local_ctxt, sp: &span, m: @ty::method,
65056543
let lltop = bcx.llbb;
65066544

65076545
// The self-object will arrive in the backwarding function via the llenv
6508-
// argument.
6509-
let llself_obj_ptr = fcx.llenv;
6546+
// argument, but we need to jump past the first item in the self-stack to
6547+
// get to the one we really want.
6548+
6549+
// Cast to self-stack's type.
6550+
let llenv = bcx.build.PointerCast(
6551+
fcx.llenv,
6552+
T_ptr(T_struct(~[cx.ccx.rust_object_type,
6553+
T_ptr(cx.ccx.rust_object_type)])));
6554+
6555+
let llself_obj_ptr = bcx.build.GEP(llenv,
6556+
~[C_int(0),
6557+
C_int(1)]);
6558+
llself_obj_ptr = bcx.build.Load(llself_obj_ptr);
6559+
6560+
// Cast it back to the type of fcx.llenv, tho, so LLVM won't complain.
6561+
// TODO: could we just cast this to T_ptr(cx.ccx.rust_object_type)?
6562+
llself_obj_ptr = bcx.build.PointerCast(llself_obj_ptr,
6563+
val_ty(fcx.llenv));
65106564

65116565
// The 'llretptr' that will arrive in the backwarding function we're
65126566
// creating also needs to be the correct type. Cast it to the method's
@@ -6521,7 +6575,7 @@ fn process_bkwding_mthd(cx: @local_ctxt, sp: &span, m: @ty::method,
65216575
// at it.
65226576
let llouter_obj_vtbl =
65236577
bcx.build.GEP(llself_obj_ptr,
6524-
~[C_int(0), C_int(1)]);
6578+
~[C_int(0), C_int(abi::obj_field_vtbl)]);
65256579
llouter_obj_vtbl = bcx.build.Load(llouter_obj_vtbl);
65266580

65276581
// Get the index of the method we want.
@@ -6627,20 +6681,6 @@ fn process_fwding_mthd(cx: @local_ctxt, sp: &span, m: @ty::method,
66276681
// argument.
66286682
let llself_obj_ptr = fcx.llenv;
66296683

6630-
// Grab the vtable out of the self-object and replace it with the
6631-
// backwarding vtable. FIXME (issue #702): Not quite ready to turn this
6632-
// behavior on yet.
6633-
6634-
// let llself_obj_vtbl =
6635-
// bcx.build.GEP(llself_obj_ptr, ~[C_int(0),
6636-
// C_int(abi::obj_field_vtbl)]);
6637-
// let llbv = bcx.build.PointerCast(backwarding_vtbl,
6638-
// T_ptr(T_empty_struct()));
6639-
// bcx.build.Store(llbv, llself_obj_vtbl);
6640-
6641-
// NB: llself_obj is now a freakish combination of outer object body
6642-
// and backwarding (inner-object) vtable.
6643-
66446684
// The 'llretptr' that will arrive in the forwarding function we're
66456685
// creating also needs to be the correct type. Cast it to the method's
66466686
// return type, if necessary.
@@ -6710,6 +6750,11 @@ fn process_fwding_mthd(cx: @local_ctxt, sp: &span, m: @ty::method,
67106750
~[C_int(0), C_int(abi::obj_field_vtbl)]);
67116751
llinner_obj_vtbl = bcx.build.Load(llinner_obj_vtbl);
67126752

6753+
let llinner_obj_body =
6754+
bcx.build.GEP(llinner_obj.val,
6755+
~[C_int(0), C_int(abi::obj_field_box)]);
6756+
llinner_obj_body = bcx.build.Load(llinner_obj_body);
6757+
67136758
// Get the index of the method we want.
67146759
let ix: uint = 0u;
67156760
alt ty::struct(bcx_tcx(bcx), inner_obj_ty) {
@@ -6741,10 +6786,23 @@ fn process_fwding_mthd(cx: @local_ctxt, sp: &span, m: @ty::method,
67416786
bcx.build.PointerCast(llorig_mthd, T_ptr(T_ptr(llorig_mthd_ty)));
67426787
llorig_mthd = bcx.build.Load(llorig_mthd);
67436788

6789+
// Set up the self-stack.
6790+
let self_stack = alloca(bcx, T_struct(~[cx.ccx.rust_object_type,
6791+
T_ptr(cx.ccx.rust_object_type)]));
6792+
self_stack = populate_self_stack(bcx,
6793+
self_stack,
6794+
llself_obj_ptr,
6795+
backwarding_vtbl,
6796+
llinner_obj_body);
6797+
6798+
// Cast self_stack back to the type of fcx.llenv to make LLVM happy.
6799+
// TODO: could we just cast this to T_ptr(cx.ccx.rust_object_type)?
6800+
self_stack = bcx.build.PointerCast(self_stack,
6801+
val_ty(fcx.llenv));
6802+
67446803
// Set up the three implicit arguments to the original method we'll need
67456804
// to call.
6746-
let self_arg = llself_obj_ptr;
6747-
let llorig_mthd_args: ValueRef[] = ~[llretptr, fcx.lltaskptr, self_arg];
6805+
let llorig_mthd_args: ValueRef[] = ~[llretptr, fcx.lltaskptr, self_stack];
67486806

67496807
// Copy the explicit arguments that are being passed into the forwarding
67506808
// function (they're in fcx.llargs) to llorig_mthd_args.
@@ -6759,7 +6817,7 @@ fn process_fwding_mthd(cx: @local_ctxt, sp: &span, m: @ty::method,
67596817
a += 1u;
67606818
}
67616819

6762-
// And, finally, call the original method.
6820+
// And, finally, call the original (inner) method.
67636821
bcx.build.FastCall(llorig_mthd, llorig_mthd_args);
67646822

67656823
bcx.build.RetVoid();

src/comp/middle/trans_common.rs

+4
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ fn set_struct_body(t: TypeRef, elts: &TypeRef[]) {
536536

537537
fn T_empty_struct() -> TypeRef { ret T_struct(~[]); }
538538

539+
// NB: This will return something different every time it's called. If
540+
// you need a generic object type that matches the type of your
541+
// existing objects, use ccx.rust_object_type. Calling
542+
// T_rust_object() again will return a different one.
539543
fn T_rust_object() -> TypeRef {
540544
let t = T_named_struct("rust_object");
541545
let e = T_ptr(T_empty_struct());
+5-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//xfail-stage1
2-
//xfail-stage2
3-
//xfail-stage3
41
use std;
52

63
fn main() {
@@ -17,18 +14,10 @@ fn main() {
1714
with my_a
1815
};
1916

20-
// These should all be 2.
21-
log_err my_a.foo();
22-
log_err my_a.bar();
23-
log_err my_b.foo();
24-
25-
// This works fine. It sends us to foo on my_b, which forwards to
26-
// foo on my_a.
27-
log_err my_b.baz();
28-
29-
// Currently segfaults. It forwards us to bar on my_a, which
30-
// backwards us to foo on my_b, which forwards us to foo on my_a
31-
// -- or, at least, that's how it should work.
32-
log_err my_b.bar();
17+
assert my_a.foo() == 2;
18+
assert my_a.bar() == 2;
19+
assert my_b.foo() == 2;
20+
assert my_b.baz() == 2;
21+
assert my_b.bar() == 2;
3322

3423
}
-35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
//xfail-stage1
2-
//xfail-stage2
3-
//xfail-stage3
41
use std;
52

63
fn main() {
@@ -21,38 +18,6 @@ fn main() {
2118
my_inner
2219
};
2320

24-
log_err my_inner.z();
2521
assert (my_inner.z() == 3u);
26-
log_err my_outer.z();
2722
assert (my_outer.z() == 3u);
2823
}
29-
30-
/*
31-
Here, when we make the self-call to self.m() in inner, we're going
32-
back through the outer "self". That outer "self" has 5 methods in
33-
its vtable: a, b, m, n, z. But the method z has already been
34-
compiled, and at the time it was compiled, it expected "self" to
35-
only have three methods in its vtable: a, m, and z. So, the method
36-
z thinks that "self.m()" means "look up method #1 (indexing from 0)
37-
in my vtable and call it". That means that it'll call method #1 on
38-
the larger vtable that it thinks is "self", and method #1 at that
39-
point is b.
40-
41-
So, when we call my_inner.z(), we get 3, which is what we'd
42-
expect. When we call my_outer.z(), we should also get 3, because
43-
at no point is z being overridden.
44-
45-
To fix this bug, we need to make the vtable slots on the inner
46-
object match whatever the object being passed in at runtime has.
47-
My first instinct was to change the vtable to match the runtime
48-
object, but vtables are already baked into RO memory. So, instead,
49-
we're going to tweak the object being passed in at runtime to match
50-
the vtable that inner already has. That is, it needs to only have
51-
a, m, and z slots in its vtable, and each one of those slots will
52-
forward to the *outer* object's a, m, and z slots, respectively.
53-
From there they will either head right back to inner, or they'll be
54-
overridden.
55-
56-
Adding support for this is issue #702.
57-
58-
*/

0 commit comments

Comments
 (0)