[macruby-changes] [2012] MacRuby/branches/experimental

source_changes at macosforge.org source_changes at macosforge.org
Fri Jul 10 16:42:34 PDT 2009


Revision: 2012
          http://trac.macosforge.org/projects/ruby/changeset/2012
Author:   lsansonetti at apple.com
Date:     2009-07-10 16:42:33 -0700 (Fri, 10 Jul 2009)
Log Message:
-----------
a better exception-based implementation for return-from-block

Modified Paths:
--------------
    MacRuby/branches/experimental/compiler.cpp
    MacRuby/branches/experimental/compiler.h
    MacRuby/branches/experimental/test_vm/block.rb
    MacRuby/branches/experimental/vm.cpp
    MacRuby/branches/experimental/vm.h

Modified: MacRuby/branches/experimental/compiler.cpp
===================================================================
--- MacRuby/branches/experimental/compiler.cpp	2009-07-10 01:56:08 UTC (rev 2011)
+++ MacRuby/branches/experimental/compiler.cpp	2009-07-10 23:42:33 UTC (rev 2012)
@@ -32,9 +32,9 @@
 llvm::Module *RoxorCompiler::module = NULL;
 RoxorCompiler *RoxorCompiler::shared = NULL;
 
-RoxorCompiler::RoxorCompiler(const char *_fname)
+RoxorCompiler::RoxorCompiler(void)
 {
-    fname = _fname;
+    fname = NULL;
     inside_eval = false;
 
     bb = NULL;
@@ -58,7 +58,8 @@
     current_loop_end_bb = NULL;
     current_loop_exit_val = NULL;
     current_rescue = false;
-    return_from_block = false;
+    return_from_block = -1;
+    return_from_block_ids = 0;
 
     dispatcherFunc = NULL;
     fastEqqFunc = NULL;
@@ -114,7 +115,7 @@
     getSpecialFunc = NULL;
     breakFunc = NULL;
     returnFromBlockFunc = NULL;
-    returnFromBlockValueFunc = NULL;
+    checkReturnFromBlockFunc = NULL;
     longjmpFunc = NULL;
     setjmpFunc = NULL;
     popBrokenValue = NULL;
@@ -147,8 +148,8 @@
 #endif
 }
 
-RoxorAOTCompiler::RoxorAOTCompiler(const char *_fname)
-    : RoxorCompiler(_fname)
+RoxorAOTCompiler::RoxorAOTCompiler(void)
+: RoxorCompiler()
 {
     cObject_gvar = NULL;
     name2symFunc = NULL;
@@ -1445,32 +1446,40 @@
 }
 
 void
-RoxorCompiler::compile_return_from_block(Value *val)
+RoxorCompiler::compile_return_from_block(Value *val, int id)
 {
     if (returnFromBlockFunc == NULL) {
-	// void rb_vm_return_from_block(VALUE val);
+	// void rb_vm_return_from_block(VALUE val, int id);
 	returnFromBlockFunc = cast<Function>(
 		module->getOrInsertFunction("rb_vm_return_from_block", 
-		    Type::VoidTy, RubyObjTy, NULL));
+		    Type::VoidTy, RubyObjTy, Type::Int32Ty, NULL));
     }
     std::vector<Value *> params;
     params.push_back(val);
+    params.push_back(ConstantInt::get(Type::Int32Ty, id));
     CallInst::Create(returnFromBlockFunc, params.begin(), params.end(), "", bb);
 }
 
 void
-RoxorCompiler::compile_return_from_block_handler(void)
+RoxorCompiler::compile_return_from_block_handler(int id)
 {
-    compile_landing_pad_header();
+    //const std::type_info &eh_type = typeid(RoxorReturnFromBlockException *);
+    //Value *exception = compile_landing_pad_header(eh_type);
+    Value *exception = compile_landing_pad_header();
 
-    if (returnFromBlockValueFunc == NULL) {
-	returnFromBlockValueFunc = cast<Function>(
+    if (checkReturnFromBlockFunc == NULL) {
+	// VALUE rb_vm_check_return_from_block_exc(void *exc, int id);
+	checkReturnFromBlockFunc = cast<Function>(
 		module->getOrInsertFunction(
-		    "rb_vm_pop_return_from_block_value", 
-		    RubyObjTy, NULL));
+		    "rb_vm_check_return_from_block_exc", 
+		    RubyObjTy, PtrTy, Type::Int32Ty, NULL));
     }
 
-    Value *val = CallInst::Create(returnFromBlockValueFunc, "", bb);
+    std::vector<Value *> params;
+    params.push_back(exception);
+    params.push_back(ConstantInt::get(Type::Int32Ty, id));
+    Value *val = CallInst::Create(checkReturnFromBlockFunc, params.begin(),
+	    params.end(), "", bb);
 
     Function *f = bb->getParent();
     BasicBlock *ret_bb = BasicBlock::Create("ret", f);
@@ -1558,8 +1567,10 @@
 		compile_pop_exception();
 	    }
 	    if (within_block) {
-		return_from_block = true;
-		compile_return_from_block(val);
+		if (return_from_block == -1) {
+		    return_from_block = return_from_block_ids++;
+		}
+		compile_return_from_block(val, return_from_block);
 		ReturnInst::Create(val, bb);
 	    }
 	    else {
@@ -1596,9 +1607,15 @@
     return compile_current_class();
 }
 
-void
+Value *
 RoxorCompiler::compile_landing_pad_header(void)
 {
+    return compile_landing_pad_header(typeid(void));
+}
+
+Value *
+RoxorCompiler::compile_landing_pad_header(const std::type_info &eh_type)
+{
     Function *eh_exception_f = Intrinsic::getDeclaration(module,
 	    Intrinsic::eh_exception);
     Value *eh_ptr = CallInst::Create(eh_exception_f, "", bb);
@@ -1621,21 +1638,58 @@
     }
     params.push_back(new BitCastInst(__gxx_personality_v0_func,
 		PtrTy, "", bb));
-    params.push_back(compile_const_pointer(NULL));
 
-    CallInst::Create(eh_selector_f, params.begin(), params.end(),
-	    "", bb);
+    if (eh_type == typeid(void)) {
+	// catch (...)
+	params.push_back(compile_const_pointer(NULL));
+    }
+    else {
+	// catch (eh_type &exc)
+	params.push_back(compile_const_pointer((void *)&eh_type));
+	params.push_back(compile_const_pointer(NULL));
+    }
 
+    Value *eh_sel = CallInst::Create(eh_selector_f, params.begin(),
+	    params.end(), "", bb);
+
+    if (eh_type != typeid(void)) {
+	// TODO: this doesn't work yet, the type id must be a GlobalVariable...
+#if __LP64__
+	Function *eh_typeid_for_f = Intrinsic::getDeclaration(module,
+		Intrinsic::eh_typeid_for_i64);
+#else
+	Function *eh_typeid_for_f = Intrinsic::getDeclaration(module,
+		Intrinsic::eh_typeid_for_i32);
+#endif
+	std::vector<Value *> params;
+	params.push_back(compile_const_pointer((void *)&eh_type));
+
+	Value *eh_typeid = CallInst::Create(eh_typeid_for_f, params.begin(),
+		params.end(), "", bb);
+
+	Function *f = bb->getParent();
+	BasicBlock *typeok_bb = BasicBlock::Create("typeok", f);
+	BasicBlock *nocatch_bb  = BasicBlock::Create("nocatch", f);
+	Value *need_ret = new ICmpInst(ICmpInst::ICMP_EQ, eh_sel,
+		eh_typeid, "", bb);
+	BranchInst::Create(typeok_bb, nocatch_bb, need_ret, bb);
+
+	bb = nocatch_bb;
+	compile_rethrow_exception();
+
+	bb = typeok_bb;
+    }
+
     Function *beginCatchFunc = NULL;
     if (beginCatchFunc == NULL) {
 	// void *__cxa_begin_catch(void *);
 	beginCatchFunc = cast<Function>(
 		module->getOrInsertFunction("__cxa_begin_catch",
-		    Type::VoidTy, PtrTy, NULL));
+		    PtrTy, PtrTy, NULL));
     }
     params.clear();
     params.push_back(eh_ptr);
-    CallInst::Create(beginCatchFunc, params.begin(), params.end(),
+    return CallInst::Create(beginCatchFunc, params.begin(), params.end(),
 	    "", bb);
 }
 
@@ -4245,7 +4299,6 @@
 		    compile_landing_pad_header();
 		    compile_node(node->nd_ensr);
 		    compile_rethrow_exception();
-		    //compile_landing_pad_footer();
 		}
 		else {
 		    val = compile_node(node->nd_head);
@@ -4331,7 +4384,7 @@
 		NODE *old_current_block_node = current_block_node;
 		ID old_current_mid = current_mid;
 		bool old_current_block = current_block;
-		bool old_return_from_block = return_from_block;
+		int old_return_from_block = return_from_block;
 		BasicBlock *old_rescue_bb = rescue_bb;
 
 		current_mid = 0;
@@ -4342,7 +4395,7 @@
 		assert(Function::classof(block));
 
 		BasicBlock *return_from_block_bb = NULL;
-		if (!old_return_from_block && return_from_block) {
+		if (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
@@ -4381,10 +4434,10 @@
 		    caller = compile_dispatch_call(params);
 		}
 
-		if (return_from_block_bb) {
+		if (return_from_block != -1) {
 		    BasicBlock *old_bb = bb;
 		    bb = return_from_block_bb;
-		    compile_return_from_block_handler();	
+		    compile_return_from_block_handler(return_from_block);	
 		    rescue_bb = old_rescue_bb;
 		    bb = old_bb;
 		}
@@ -4415,10 +4468,10 @@
 		if (node->nd_head != NULL) {
 		    compile_dispatch_arguments(node->nd_head, params, &argc);
 		}
-		params.insert(params.begin(), ConstantInt::get(Type::Int32Ty, argc));
+		params.insert(params.begin(),
+			ConstantInt::get(Type::Int32Ty, argc));
 
-		return CallInst::Create(yieldFunc, params.begin(),
-			params.end(), "", bb);
+		return compile_protected_call(yieldFunc, params);
 	    }
 	    break;
 

Modified: MacRuby/branches/experimental/compiler.h
===================================================================
--- MacRuby/branches/experimental/compiler.h	2009-07-10 01:56:08 UTC (rev 2011)
+++ MacRuby/branches/experimental/compiler.h	2009-07-10 23:42:33 UTC (rev 2012)
@@ -30,9 +30,13 @@
 	static llvm::Module *module;
 	static RoxorCompiler *shared;
 
-	RoxorCompiler(const char *fname);
+	RoxorCompiler(void);
 	virtual ~RoxorCompiler(void) { }
 
+	void set_fname(const char *_fname) {
+	    fname = _fname;
+	}
+
 	Value *compile_node(NODE *node);
 
 	virtual Function *compile_main_function(NODE *node);
@@ -92,7 +96,8 @@
 	BasicBlock *current_loop_body_bb;
 	BasicBlock *current_loop_end_bb;
 	Value *current_loop_exit_val;
-	bool return_from_block;
+	int return_from_block;
+	int return_from_block_ids;
 
 	Function *dispatcherFunc;
 	Function *fastEqqFunc;
@@ -148,7 +153,7 @@
 	Function *getSpecialFunc;
 	Function *breakFunc;
 	Function *returnFromBlockFunc;
-	Function *returnFromBlockValueFunc;
+	Function *checkReturnFromBlockFunc;
 	Function *longjmpFunc;
 	Function *setjmpFunc;
 	Function *popBrokenValue;
@@ -229,8 +234,8 @@
 	Value *compile_dstr(NODE *node);
 	Value *compile_dvar_slot(ID name);
 	void compile_break_val(Value *val);
-	void compile_return_from_block(Value *val);
-	void compile_return_from_block_handler(void);
+	void compile_return_from_block(Value *val, int id);
+	void compile_return_from_block_handler(int id);
 	Value *compile_jump(NODE *node);
 	virtual Value *compile_mcache(SEL sel, bool super);
 	virtual Value *compile_ccache(ID id);
@@ -248,7 +253,8 @@
 	virtual Value *compile_immutable_literal(VALUE val);
 	virtual Value *compile_global_entry(NODE *node);
 
-	void compile_landing_pad_header(void);
+	Value *compile_landing_pad_header(void);
+	Value *compile_landing_pad_header(const std::type_info &eh_type);
 	void compile_landing_pad_footer(bool pop_exception=true);
 	void compile_rethrow_exception(void);
 	void compile_pop_exception(void);
@@ -286,7 +292,7 @@
 
 class RoxorAOTCompiler : public RoxorCompiler {
     public:
-	RoxorAOTCompiler(const char *fname);
+	RoxorAOTCompiler(void);
 
 	Function *compile_main_function(NODE *node);
 

Modified: MacRuby/branches/experimental/test_vm/block.rb
===================================================================
--- MacRuby/branches/experimental/test_vm/block.rb	2009-07-10 01:56:08 UTC (rev 2011)
+++ MacRuby/branches/experimental/test_vm/block.rb	2009-07-10 23:42:33 UTC (rev 2012)
@@ -527,6 +527,39 @@
   p bar
 }
 
+assert ':ok', %{
+  def foo
+    begin
+      yield
+    ensure
+      p :ok
+    end
+  end
+  def bar
+   foo { return }
+  end 
+  bar
+}
+
+assert 'false', %{
+  def foo(m); m.synchronize { return 42 }; end
+  m = Mutex.new
+  foo(m)
+  p m.locked?
+}
+
+assert ':ok', %{
+  def foo(v)
+    1.times do
+      return true if v
+      return false
+      p :nok1
+    end
+    p :nok2
+  end
+  p :ok if !foo(false) and foo(true)
+}
+
 assert ":ok\n:ok", %{
   def foo
     raise

Modified: MacRuby/branches/experimental/vm.cpp
===================================================================
--- MacRuby/branches/experimental/vm.cpp	2009-07-10 01:56:08 UTC (rev 2011)
+++ MacRuby/branches/experimental/vm.cpp	2009-07-10 23:42:33 UTC (rev 2012)
@@ -180,7 +180,7 @@
 #define VALUE_TO_GV(v) (value2gv((VALUE)v))
 
 extern "C" void *__cxa_allocate_exception(size_t);
-extern "C" void __cxa_throw(void *, void *, void *);
+extern "C" void __cxa_throw(void *, void *, void (*)(void*));
 
 RoxorCore::RoxorCore(void)
 {
@@ -237,7 +237,6 @@
     safe_level = 0;
     backref = Qnil;
     broken_with = Qundef;
-    returned_from_block = false;
     last_status = Qnil;
     errinfo = Qnil;
     parse_in_eval = false;
@@ -293,7 +292,6 @@
 
     backref = Qnil;
     broken_with = Qundef;
-    returned_from_block = false;
     last_status = Qnil;
     errinfo = Qnil;
     parse_in_eval = false;
@@ -3835,6 +3833,45 @@
     return val;
 }
 
+extern "C"
+void
+rb_vm_return_from_block(VALUE val, int id)
+{
+    RoxorReturnFromBlockException *exc = new RoxorReturnFromBlockException();
+
+    rb_objc_retain((void *)val);
+    exc->val = val;
+    exc->id = id;
+
+    throw exc;
+}
+
+extern "C" std::type_info *__cxa_current_exception_type(void);
+
+static inline bool
+current_exception_is_return_from_block(void)
+{
+    const std::type_info *exc_type = __cxa_current_exception_type();
+    return exc_type != NULL
+	&& *exc_type == typeid(RoxorReturnFromBlockException *);
+}
+
+extern "C"
+VALUE
+rb_vm_check_return_from_block_exc(RoxorReturnFromBlockException **pexc, int id)
+{
+    if (current_exception_is_return_from_block()) {
+	RoxorReturnFromBlockException *exc = *pexc;
+	if (id == -1 || exc->id == id) {
+	    VALUE val = exc->val;
+	    rb_objc_release((void *)val);
+	    delete exc;
+	    return val;
+	}
+    }
+    return Qundef;
+}
+
 static inline void
 rb_vm_rethrow(void)
 {
@@ -3842,27 +3879,19 @@
     __cxa_throw(exc, NULL, NULL);
 }
 
+#if 0
 extern "C"
-void
-rb_vm_return_from_block(VALUE val)
-{
-    GET_VM()->set_broken_with(val);
-    GET_VM()->set_returned_from_block(true);
-    rb_vm_rethrow();
-}
-
-extern "C"
 VALUE
-rb_vm_pop_return_from_block_value(void)
+rb_vm_pop_return_from_block_value(int id)
 {
-    if (GET_VM()->get_returned_from_block()) {
-	GET_VM()->set_returned_from_block(false);
+    if (GET_VM()->check_return_from_block(id)) {
 	VALUE val = rb_vm_pop_broken_value();
 	assert(val != Qundef);
 	return val;
     }
     return Qundef;
 }
+#endif
 
 extern "C"
 VALUE
@@ -4023,8 +4052,8 @@
 rb_vm_init_compiler(void)
 {
     RoxorCompiler::shared = ruby_aot_compile
-	? new RoxorAOTCompiler("")
-	: new RoxorCompiler("");
+	? new RoxorAOTCompiler()
+	: new RoxorCompiler();
 }
 
 extern "C"
@@ -4041,8 +4070,10 @@
     RoxorCompiler *compiler = RoxorCompiler::shared;
 
     bool old_inside_eval = compiler->is_inside_eval();
-    compiler->set_inside_eval(inside_eval); 
+    compiler->set_inside_eval(inside_eval);
+    compiler->set_fname(fname);
     Function *function = compiler->compile_main_function(node);
+    compiler->set_fname(NULL);
     compiler->set_inside_eval(old_inside_eval);
 
     if (binding != NULL) {
@@ -4514,7 +4545,8 @@
     }
     catch (...) {
 	VALUE exc;
-	if (rb_vm_pop_return_from_block_value() != Qundef) {
+	if (current_exception_is_return_from_block()) {
+	    // TODO: the exception is leaking!
 	    exc = rb_exc_new2(rb_eLocalJumpError,
 		    "unexpected return from Thread");
 	}

Modified: MacRuby/branches/experimental/vm.h
===================================================================
--- MacRuby/branches/experimental/vm.h	2009-07-10 01:56:08 UTC (rev 2011)
+++ MacRuby/branches/experimental/vm.h	2009-07-10 23:42:33 UTC (rev 2012)
@@ -750,7 +750,6 @@
 	VALUE current_top_object;
 	VALUE backref;
 	VALUE broken_with;
-	bool returned_from_block;
 	VALUE last_status;
 	VALUE errinfo;
 	int safe_level;
@@ -767,7 +766,6 @@
 	ACCESSOR(current_top_object, VALUE);
 	ACCESSOR(backref, VALUE);
 	ACCESSOR(broken_with, VALUE);
-	ACCESSOR(returned_from_block, bool);
 	ACCESSOR(last_status, VALUE);
 	ACCESSOR(errinfo, VALUE);
 	ACCESSOR(safe_level, int);
@@ -877,6 +875,13 @@
 #define GET_VM() (RoxorVM::current())
 #define GET_THREAD() (GetThreadPtr(GET_VM()->get_thread()))
 
+// Custom C++ exception class used to implement "return-from-block".
+class RoxorReturnFromBlockException {
+    public:
+	VALUE val;
+	int id;
+};
+
 #endif /* __cplusplus */
 
 #endif /* __VM_H_ */
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20090710/55e60e8d/attachment-0001.html>


More information about the macruby-changes mailing list