Permalink
Browse files

Improve JIT fixup logic for inlined blocks

In the case where the original method called in the creator had a block
set, we need to propagate this to the call frame when do an uncommon
exit. This would cause a LocalJumpError in the following situation:

class Foo
  def bar ary, obj
    ary.each do
      obj.quz
      a = yield
    end
  end
end

If quz was redefined, we have to do an uncommon exit from the obj.quz call.
If the jitted method is Foo#bar, it was passed a block originally that was
yielded inline in the JIT'ed code. We need to store it in the call frame we
exit from.

What needs to be done here is similar as in when we emit blocks in the
JIT uncommon exit code, but in this case this wouldn't happen since the
original block wasn't created inside the JIT. This means this fixup
logic wouldn't handle this case and so the call frame fixup code needs
to handle this.

Also moves the uncommon exit fixup code into call frame because we had it
in multiple places now and was becoming more code.

Originally found because of spec failures in the Discourse spec suite:

https://github.com/discourse/discourse
  • Loading branch information...
1 parent 0d7e43c commit 64122b9b47e7cc7164eaec28813603ef1269fc5b @dbussink dbussink committed Aug 12, 2013
Showing with 14 additions and 6 deletions.
  1. +6 −0 vm/call_frame.cpp
  2. +2 −0 vm/call_frame.hpp
  3. +2 −6 vm/llvm/jit_util.cpp
  4. +4 −0 vm/stack_variables.hpp
View
@@ -196,6 +196,12 @@ namespace rubinius {
return false;
}
+ void CallFrame::jit_fixup(STATE, CallFrame* creator) {
+ VariableScope* parent = creator->promote_scope(state);
+ scope->set_parent(parent);
+ scope->set_block(creator->scope->block());
+ }
+
void CallFrame::dump() {
VM* vm = VM::current();
State state_obj(vm), *state = &state_obj;
View
@@ -203,6 +203,8 @@ namespace rubinius {
return cf;
}
+ void jit_fixup(STATE, CallFrame* creator);
+
Object* last_match(STATE);
void set_last_match(STATE, Object* obj);
@@ -1224,9 +1224,7 @@ extern "C" {
MachineCode* mcode = call_frame->compiled_code->machine_code();
if(call_frame->is_inline_frame() && creator_call_frame) {
- // Fix up this inlined block.
- VariableScope* parent = creator_call_frame->promote_scope(state);
- call_frame->scope->set_parent(parent);
+ call_frame->jit_fixup(state, creator_call_frame);
}
state->vm()->unwinds().set_current(unwind_count);
@@ -1247,9 +1245,7 @@ extern "C" {
MachineCode* mcode = call_frame->compiled_code->machine_code();
if(call_frame->is_inline_frame() && creator_call_frame) {
- // Fix up this inlined block.
- VariableScope* parent = creator_call_frame->promote_scope(state);
- call_frame->scope->set_parent(parent);
+ call_frame->jit_fixup(state, creator_call_frame);
}
call_frame->ip_ = entry_ip;
@@ -42,6 +42,10 @@ namespace rubinius {
parent_ = scope;
}
+ void set_block(Object* block) {
+ block_ = block;
+ }
+
Object* self() const {
return self_;
}

0 comments on commit 64122b9

Please sign in to comment.