> >
> > Consider this:
> >
> > o.m1(..)
> > o.m2(..)
> >
> > Since type of o hasn't changed between the 2 calls, you can skip the
> method
> > table load for the second call. Anyway, I need to understand the call
> > protocol in greater detail to comment more.
>
> This is an interesting use-case for closures. If 'o' is captured in a
> block and made into a proc and executed on another thread, then it
> could potentially change o at any time. So, I think your example
> works most of the time, but could fail in a case like this:
>
> o.m1 { o = something_else }
> o.m2
>
> Since we do not know when the block can occur we would need to lookup
> o for both calls. Ability to pass around blocks is easy so this
> example probably means any place where o is reachable from another
> scope cannot be shared across calls.
Thanks for expanding on that example .. I guess even a seemingly simple
example can be complex :-)
Thinking about it some, within the IR, we don't need to worry about this
case because when captured variables are identified, the two 'o's above will
become different objects automatically.
So, what will happen in this case is:
--- IR for the block being passed into o.m1 ---
v = frame_load(o)
mtbl = method_table(v)
maddr = lookup(mtbl, "m1")
call(maddr, ..)
v = frame_load(o)
mtbl = method_table(v)
maddr = lookup(mtbl, "m2")
call(maddr, ..)
So, in this case, the two v's are different objects altogether, and there
the two loads of mtbl are also different, and the optimization to eliminate
the second load gets disabled automatically.
However in the case where there is no block being passed (o.m1; o.m2), the
IR will be:
mtbl = method_table(o)
maddr = lookup(mtbl, "m1")
call(maddr, ...)
mtbl = method_table(o)
maddr = lookup(mtbl, "m2")
call(maddr, ...)
In this case, both method table loads come off the same object (o), and
hence the second load is a candidate for optimization. The only case where
this gets disabled is if the intervening call can completely switch the
method table of the object o. But, depending on how method tables are
implemented in the runtime, evals presumably switch around entries in the
method table, and not the method table itself in which case the optimization
is safe.
So, the real question here boils down to the previous one: can we safely
determine what local variables are captured by closures? In that sense,
this is not a new special case that we need to worry about, and the method
table opt. falls out of the IR automatically without having to do anything
extra special. All the semantics are captured by the method table load, and
the frame load/store IR instructions.
I originally thought of a scenario like:
>
> Thread.run { o = something_else}
>
> o.m1
> o.m2
>
> In this case I would be less worried because execution is dependent on
> a race and would by it's very nature be uncertain. In this case you
> could probably share o and no one would be the wiser.
That makes sense. This is a clear race condition. That being said, since o
is a captured variable (is this the terminology you use for variables used
in closures that are defined outside the closure?), this scenario is the
same as the block passing example above. So, the two o's will get treated
as different objects and the optimization gets blocked.
But, thinking a little bit more, the reason the optimization is getting
blocked is because I am assuming conservative semantics for a frame_load,
i.e. frame_loads cannot be optimized away and they always need to be
performed.
However, we can also use more aggressive semantics where we could consider
eliminating a second frame load of the same variable is considered redundant
if there are no intervening frame-stores or method calls. If we did that,
for this thread example, the second frame load will get removed, which means
the two o's will be considered the same object. But as you noted, this is a
race condition and the compiler is justified in optimizing that code
snippet.
We need to also deal with eval + binding, which
> the JIT currently treats those like keywords, but perhaps we can be a
> little more dynamic with these in the IR?
I haven't delved into how you currently handle all this so once I understand
that better, let us discuss how to handle them.
Subbu.