Browse files

a better exception-based implementation for return-from-block

  • Loading branch information...
1 parent fceb5ba commit 3432fd1957c70ec35142b120ffdff2e2c33ff209 @lrz lrz committed Jul 10, 2009
Showing with 189 additions and 60 deletions.
  1. +85 −32 compiler.cpp
  2. +13 −7 compiler.h
  3. +33 −0 test_vm/block.rb
  4. +51 −19 vm.cpp
  5. +7 −2 vm.h
View
117 compiler.cpp
@@ -32,9 +32,9 @@ extern "C" const char *ruby_node_name(int node);
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 @@ RoxorCompiler::RoxorCompiler(const char *_fname)
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 @@ RoxorCompiler::RoxorCompiler(const char *_fname)
getSpecialFunc = NULL;
breakFunc = NULL;
returnFromBlockFunc = NULL;
- returnFromBlockValueFunc = NULL;
+ checkReturnFromBlockFunc = NULL;
longjmpFunc = NULL;
setjmpFunc = NULL;
popBrokenValue = NULL;
@@ -147,8 +148,8 @@ RoxorCompiler::RoxorCompiler(const char *_fname)
#endif
}
-RoxorAOTCompiler::RoxorAOTCompiler(const char *_fname)
- : RoxorCompiler(_fname)
+RoxorAOTCompiler::RoxorAOTCompiler(void)
+: RoxorCompiler()
{
cObject_gvar = NULL;
name2symFunc = NULL;
@@ -1445,32 +1446,40 @@ RoxorCompiler::compile_break_val(Value *val)
}
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 @@ RoxorCompiler::compile_jump(NODE *node)
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 @@ RoxorCompiler::compile_class_path(NODE *node)
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 @@ RoxorCompiler::compile_landing_pad_header(void)
}
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 @@ RoxorCompiler::compile_node(NODE *node)
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 @@ RoxorCompiler::compile_node(NODE *node)
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 @@ RoxorCompiler::compile_node(NODE *node)
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 @@ RoxorCompiler::compile_node(NODE *node)
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 @@ RoxorCompiler::compile_node(NODE *node)
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;
View
20 compiler.h
@@ -30,9 +30,13 @@ class RoxorCompiler {
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 @@ class RoxorCompiler {
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 @@ class RoxorCompiler {
Function *getSpecialFunc;
Function *breakFunc;
Function *returnFromBlockFunc;
- Function *returnFromBlockValueFunc;
+ Function *checkReturnFromBlockFunc;
Function *longjmpFunc;
Function *setjmpFunc;
Function *popBrokenValue;
@@ -229,8 +234,8 @@ class RoxorCompiler {
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 @@ class RoxorCompiler {
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 RoxorCompiler {
class RoxorAOTCompiler : public RoxorCompiler {
public:
- RoxorAOTCompiler(const char *fname);
+ RoxorAOTCompiler(void);
Function *compile_main_function(NODE *node);
View
33 test_vm/block.rb
@@ -527,6 +527,39 @@ def bar; foo { return 42 }; p :nok; end
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
View
70 vm.cpp
@@ -180,7 +180,7 @@ value2gv(VALUE v)
#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 @@ RoxorVM::RoxorVM(void)
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 @@ RoxorVM::RoxorVM(const RoxorVM &vm)
backref = Qnil;
broken_with = Qundef;
- returned_from_block = false;
last_status = Qnil;
errinfo = Qnil;
parse_in_eval = false;
@@ -3835,34 +3833,65 @@ rb_vm_pop_broken_value(void)
return val;
}
-static inline void
-rb_vm_rethrow(void)
+extern "C"
+void
+rb_vm_return_from_block(VALUE val, int id)
{
- void *exc = __cxa_allocate_exception(0);
- __cxa_throw(exc, NULL, NULL);
+ 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"
-void
-rb_vm_return_from_block(VALUE val)
+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)
{
- GET_VM()->set_broken_with(val);
- GET_VM()->set_returned_from_block(true);
- rb_vm_rethrow();
+ void *exc = __cxa_allocate_exception(0);
+ __cxa_throw(exc, NULL, NULL);
}
+#if 0
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 @@ void
rb_vm_init_compiler(void)
{
RoxorCompiler::shared = ruby_aot_compile
- ? new RoxorAOTCompiler("")
- : new RoxorCompiler("");
+ ? new RoxorAOTCompiler()
+ : new RoxorCompiler();
}
extern "C"
@@ -4041,8 +4070,10 @@ rb_vm_run(const char *fname, NODE *node, rb_vm_binding_t *binding,
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 @@ rb_vm_thread_run(VALUE thread)
}
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");
}
View
9 vm.h
@@ -750,7 +750,6 @@ class RoxorVM {
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 @@ class RoxorVM {
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 @@ class RoxorVM {
#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_ */

0 comments on commit 3432fd1

Please sign in to comment.