Revision: 2950 http://trac.macosforge.org/projects/ruby/changeset/2950 Author: lsansonetti@apple.com Date: 2009-11-03 22:05:31 -0800 (Tue, 03 Nov 2009) Log Message: ----------- optimized returns from non-ensure-scoped and non-Proc blocks Modified Paths: -------------- MacRuby/trunk/compiler.cpp MacRuby/trunk/compiler.h MacRuby/trunk/vm.cpp MacRuby/trunk/vm.h Modified: MacRuby/trunk/compiler.cpp =================================================================== --- MacRuby/trunk/compiler.cpp 2009-11-03 07:22:20 UTC (rev 2949) +++ MacRuby/trunk/compiler.cpp 2009-11-04 06:05:31 UTC (rev 2950) @@ -124,7 +124,9 @@ getSpecialFunc = NULL; breakFunc = NULL; returnFromBlockFunc = NULL; + returnedFromBlockFunc = NULL; checkReturnFromBlockFunc = NULL; + setHasEnsureFunc = NULL; longjmpFunc = NULL; setjmpFunc = NULL; setScopeFunc = NULL; @@ -357,7 +359,8 @@ } void -RoxorCompiler::compile_dispatch_arguments(NODE *args, std::vector<Value *> &arguments, int *pargc) +RoxorCompiler::compile_dispatch_arguments(NODE *args, + std::vector<Value *> &arguments, int *pargc) { int argc = 0; @@ -1791,6 +1794,21 @@ } Value * +RoxorCompiler::compile_set_has_ensure(Value *val) +{ + if (setHasEnsureFunc == NULL) { + setHasEnsureFunc = cast<Function>( + module->getOrInsertFunction( + "rb_vm_set_has_ensure", Int8Ty, Int8Ty, NULL)); + } + + std::vector<Value *> params; + params.push_back(val); + return CallInst::Create(setHasEnsureFunc, params.begin(), params.end(), + "", bb); +} + +Value * RoxorCompiler::compile_class_path(NODE *node, bool *outer) { if (nd_type(node) == NODE_COLON3) { @@ -4817,7 +4835,6 @@ PHINode *new_ensure_pn = PHINode::Create(RubyObjTy, "ensure.phi", ensure_return_bb); ensure_pn = new_ensure_pn; - Value *val; ensure_bb = ensure_return_bb; @@ -4828,10 +4845,13 @@ BasicBlock *old_rescue_invoke_bb = rescue_invoke_bb; BasicBlock *old_rescue_rethrow_bb = rescue_rethrow_bb; + Value *old_has_ensure = + compile_set_has_ensure(ConstantInt::get(Int8Ty, 1)); + rescue_invoke_bb = new_rescue_invoke_bb; rescue_rethrow_bb = new_rescue_rethrow_bb; DEBUG_LEVEL_INC(); - val = compile_node(node->nd_head); + Value *val = compile_node(node->nd_head); DEBUG_LEVEL_DEC(); rescue_rethrow_bb = old_rescue_rethrow_bb; rescue_invoke_bb = old_rescue_invoke_bb; @@ -4852,6 +4872,7 @@ BranchInst::Create(new_rescue_rethrow_bb, bb); } bb = new_rescue_rethrow_bb; + compile_set_has_ensure(old_has_ensure); compile_node(node->nd_ensr); compile_rethrow_exception(); } @@ -4869,6 +4890,7 @@ // some value was returned in the block so we have to // make a version of the ensure that returns this value bb = ensure_return_bb; + compile_set_has_ensure(old_has_ensure); compile_node(node->nd_ensr); // the return value is the PHINode from all the return compile_simple_return(new_ensure_pn); @@ -4877,6 +4899,7 @@ // we also have to compile the ensure // for when the block was left without return bb = ensure_normal_bb; + compile_set_has_ensure(old_has_ensure); compile_node(node->nd_ensr); return val; @@ -4973,9 +4996,9 @@ BasicBlock *return_from_block_bb = NULL; if (!old_current_block_chain && return_from_block != -1) { // The block we just compiled contains one or more - // return expressions! We need to enclose the dispatcher - // call inside an exception handler, since return-from - // -block is implemented using a C++ exception. + // return expressions! We need to enclose further + // dispatcher calls inside an exception handler, since + // return-from-block may use a C++ exception. Function *f = bb->getParent(); rescue_invoke_bb = return_from_block_bb = BasicBlock::Create(context, "return-from-block", f); @@ -5019,6 +5042,37 @@ caller = compile_dispatch_call(params); } + if (returnedFromBlockFunc == NULL) { + // VALUE rb_vm_returned_from_block(int id); + returnedFromBlockFunc = cast<Function>( + module->getOrInsertFunction( + "rb_vm_returned_from_block", + RubyObjTy, Int32Ty, NULL)); + } + + std::vector<Value *> params2; + params2.push_back(ConstantInt::get(Int32Ty, + return_from_block_bb != NULL + ? return_from_block : -1)); + + Value *retval_block = CallInst::Create(returnedFromBlockFunc, + params2.begin(), params2.end(), "", bb); + + Value *is_returned = new ICmpInst(*bb, ICmpInst::ICMP_NE, + retval_block, undefVal); + + Function *f = bb->getParent(); + BasicBlock *return_bb = BasicBlock::Create(context, + "return-from-block-fast", f); + BasicBlock *next_bb = BasicBlock::Create(context, "next", f); + + BranchInst::Create(return_bb, next_bb, is_returned, bb); + + bb = return_bb; + ReturnInst::Create(context, retval_block, bb); + + bb = next_bb; + if (return_from_block_bb != NULL) { BasicBlock *old_bb = bb; bb = return_from_block_bb; Modified: MacRuby/trunk/compiler.h =================================================================== --- MacRuby/trunk/compiler.h 2009-11-03 07:22:20 UTC (rev 2949) +++ MacRuby/trunk/compiler.h 2009-11-04 06:05:31 UTC (rev 2950) @@ -209,7 +209,9 @@ Function *getSpecialFunc; Function *breakFunc; Function *returnFromBlockFunc; + Function *returnedFromBlockFunc; Function *checkReturnFromBlockFunc; + Function *setHasEnsureFunc; Function *longjmpFunc; Function *setjmpFunc; Function *setScopeFunc; @@ -308,6 +310,7 @@ Value *compile_dvar_slot(ID name); void compile_break_val(Value *val); void compile_simple_return(Value *val); + Value *compile_set_has_ensure(Value *val); void compile_return_from_block(Value *val, int id); void compile_return_from_block_handler(int id); Value *compile_jump(NODE *node); Modified: MacRuby/trunk/vm.cpp =================================================================== --- MacRuby/trunk/vm.cpp 2009-11-03 07:22:20 UTC (rev 2949) +++ MacRuby/trunk/vm.cpp 2009-11-04 06:05:31 UTC (rev 2950) @@ -271,6 +271,8 @@ last_status = Qnil; errinfo = Qnil; parse_in_eval = false; + has_ensure = false; + return_from_block = -1; } static inline void * @@ -327,6 +329,8 @@ last_status = Qnil; errinfo = Qnil; parse_in_eval = false; + has_ensure = false; + return_from_block = -1; } RoxorVM::~RoxorVM(void) @@ -3099,7 +3103,12 @@ rb_raise(rb_eLocalJumpError, "break from proc-closure"); } #endif - GET_VM()->set_broken_with(val); + RoxorVM *vm = GET_VM(); + if (vm->get_broken_with() != val) { + GC_RELEASE(vm->get_broken_with()); + vm->set_broken_with(val); + GC_RETAIN(val); + } } extern "C" @@ -3117,20 +3126,57 @@ } extern "C" +unsigned char +rb_vm_set_has_ensure(unsigned char state) +{ + RoxorVM *vm = GET_VM(); + const bool old_state = vm->get_has_ensure(); + vm->set_has_ensure(state); + return old_state ? 1 : 0; +} + +extern "C" void rb_vm_return_from_block(VALUE val, int id, rb_vm_block_t *running_block) { + RoxorVM *vm = GET_VM(); + // Do not trigger a return from the calling scope if the running block // is a lambda, to conform to the ruby 1.9 specifications. - if (!(running_block->flags & VM_BLOCK_LAMBDA)) { + if (running_block->flags & VM_BLOCK_LAMBDA) { + return; + } + + // If we are inside an ensure block or if the running block is a Proc, + // let's implement return-from-block using a C++ exception (slow). + if (vm->get_has_ensure() || (running_block->flags & VM_BLOCK_PROC)) { RoxorReturnFromBlockException *exc = new RoxorReturnFromBlockException(); exc->val = val; exc->id = id; throw exc; } + + // Otherwise, let's mark the VM (fast). + vm->set_return_from_block(id); + if (vm->get_broken_with() != val) { + GC_RELEASE(vm->get_broken_with()); + vm->set_broken_with(val); + GC_RETAIN(val); + } } +extern "C" +VALUE +rb_vm_returned_from_block(int id) +{ + RoxorVM *vm = GET_VM(); + if (id != -1 && vm->get_return_from_block() == id) { + vm->set_return_from_block(-1); + } + return vm->pop_broken_with(); +} + extern "C" std::type_info *__cxa_current_exception_type(void); static inline bool @@ -3558,7 +3604,7 @@ { VALUE last_status = GET_VM()->get_last_status(); if (last_status != Qnil) { - rb_objc_release((void *)last_status); + GC_RELEASE(last_status); } if (pid == -1) { @@ -3568,7 +3614,7 @@ last_status = rb_obj_alloc(rb_cProcessStatus); rb_iv_set(last_status, "status", INT2FIX(status)); rb_iv_set(last_status, "pid", PIDT2NUM(pid)); - rb_objc_retain((void *)last_status); + GC_RETAIN(last_status); } GET_VM()->set_last_status(last_status); } @@ -3588,10 +3634,10 @@ } VALUE errinfo = GET_VM()->get_errinfo(); if (errinfo != Qnil) { - rb_objc_release((void *)errinfo); + GC_RELEASE(errinfo); } GET_VM()->set_errinfo(err); - rb_objc_retain((void *)err); + GC_RETAIN(err); } extern "C" @@ -3645,9 +3691,9 @@ { VALUE old = GET_VM()->get_backref(); if (old != val) { - rb_objc_release((void *)old); + GC_RELEASE(old); GET_VM()->set_backref(val); - rb_objc_retain((void *)val); + GC_RETAIN(val); } } Modified: MacRuby/trunk/vm.h =================================================================== --- MacRuby/trunk/vm.h 2009-11-03 07:22:20 UTC (rev 2949) +++ MacRuby/trunk/vm.h 2009-11-04 06:05:31 UTC (rev 2950) @@ -845,6 +845,8 @@ int safe_level; rb_vm_method_missing_reason_t method_missing_reason; bool parse_in_eval; + bool has_ensure; + int return_from_block; public: RoxorVM(void); @@ -861,6 +863,8 @@ ACCESSOR(safe_level, int); ACCESSOR(method_missing_reason, rb_vm_method_missing_reason_t); ACCESSOR(parse_in_eval, bool); + ACCESSOR(has_ensure, bool); + ACCESSOR(return_from_block, int); std::string debug_blocks(void); @@ -948,15 +952,18 @@ VALUE *get_binding_lvar(ID name, bool create); + VALUE ruby_catch(VALUE tag); + VALUE ruby_throw(VALUE tag, VALUE value); + VALUE pop_broken_with(void) { - VALUE v = broken_with; - broken_with = Qundef; - return v; + VALUE val = broken_with; + if (return_from_block == -1) { + GC_RELEASE(val); + broken_with = Qundef; + } + return val; } - VALUE ruby_catch(VALUE tag); - VALUE ruby_throw(VALUE tag, VALUE value); - void setup_from_current_thread(void); VALUE exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj,
participants (1)
-
source_changes@macosforge.org