[macruby-changes] [1306] MacRuby/branches/experimental/roxor.cpp
source_changes at macosforge.org
source_changes at macosforge.org
Fri Apr 3 16:40:34 PDT 2009
Revision: 1306
http://trac.macosforge.org/projects/ruby/changeset/1306
Author: lsansonetti at apple.com
Date: 2009-04-03 16:40:34 -0700 (Fri, 03 Apr 2009)
Log Message:
-----------
a SjLj-based implementation for 'return-from-a-block', a bit slow but we can live with it for now (note: some tests in spec:ci are now failing, apparently this change fixed something in mspec and now some specs that used to pass (but shouldn't in theory) do not pass anymore)
Modified Paths:
--------------
MacRuby/branches/experimental/roxor.cpp
Modified: MacRuby/branches/experimental/roxor.cpp
===================================================================
--- MacRuby/branches/experimental/roxor.cpp 2009-04-03 18:24:57 UTC (rev 1305)
+++ MacRuby/branches/experimental/roxor.cpp 2009-04-03 23:40:34 UTC (rev 1306)
@@ -193,6 +193,7 @@
BasicBlock *rescue_bb;
NODE *current_block_node;
Function *current_block_func;
+ jmp_buf *return_from_block_jmpbuf;
GlobalVariable *current_opened_class;
BasicBlock *current_loop_begin_bb;
BasicBlock *current_loop_body_bb;
@@ -236,6 +237,9 @@
Function *popExceptionFunc;
Function *getSpecialFunc;
Function *breakFunc;
+ Function *longjmpFunc;
+ Function *setjmpFunc;
+ Function *brokenWithFunc;
Constant *zeroVal;
Constant *oneVal;
@@ -289,6 +293,7 @@
Value *compile_defined_expression(NODE *node);
Value *compile_dstr(NODE *node);
Value *compile_dvar_slot(ID name);
+ Value *compile_jump(NODE *node);
void compile_dead_branch(void);
void compile_landing_pad_header(void);
@@ -410,6 +415,8 @@
bool block_saved; // used by block_given?
bool parse_in_eval;
+ std::vector<jmp_buf *> return_from_block_jmp_bufs;
+
RoxorVM(void);
ExecutionEngine *execution_engine(void) { return ee; }
@@ -547,6 +554,7 @@
current_loop_body_bb = NULL;
current_loop_end_bb = NULL;
current_loop_exit_val = NULL;
+ return_from_block_jmpbuf = NULL;
dispatcherFunc = NULL;
fastEqqFunc = NULL;
@@ -585,6 +593,9 @@
popExceptionFunc = NULL;
getSpecialFunc = NULL;
breakFunc = NULL;
+ longjmpFunc = NULL;
+ setjmpFunc = NULL;
+ brokenWithFunc = NULL;
#if __LP64__
RubyObjTy = IntTy = Type::Int64Ty;
@@ -1371,6 +1382,108 @@
}
Value *
+RoxorCompiler::compile_jump(NODE *node)
+{
+ if (nd_type(node) == NODE_RETRY) {
+ // Simply jump to the nearest begin label.
+ if (begin_bb == NULL) {
+ rb_raise(rb_eSyntaxError, "unexpected retry");
+ }
+ // TODO raise a SyntaxError if called outside of a "rescue"
+ // block.
+ BranchInst::Create(begin_bb, bb);
+ compile_dead_branch();
+ return nilVal;
+ }
+
+ const bool within_loop = current_loop_begin_bb != NULL
+ && current_loop_body_bb != NULL
+ && current_loop_end_bb != NULL;
+
+ // These cases should never happen.
+ if (!current_block && !within_loop) {
+ if (nd_type(node) == NODE_BREAK) {
+ rb_raise(rb_eLocalJumpError, "unexpected break");
+ }
+ if (nd_type(node) == NODE_NEXT) {
+ rb_raise(rb_eLocalJumpError, "unexpected next");
+ }
+ if (nd_type(node) == NODE_REDO) {
+ rb_raise(rb_eLocalJumpError, "unexpected redo");
+ }
+ }
+
+ // The return value of the jump.
+ Value *val = node->nd_head != NULL
+ ? compile_node(node->nd_head) : nilVal;
+
+ if (within_loop) {
+ // Simple branching.
+ if (nd_type(node) == NODE_BREAK) {
+ current_loop_exit_val = val;
+ BranchInst::Create(current_loop_end_bb, bb);
+ }
+ else if (nd_type(node) == NODE_NEXT) {
+ BranchInst::Create(current_loop_begin_bb, bb);
+ }
+ else if (nd_type(node) == NODE_REDO) {
+ BranchInst::Create(current_loop_body_bb, bb);
+ }
+ else {
+ ReturnInst::Create(val, bb);
+ }
+ }
+ else {
+ if (nd_type(node) == NODE_BREAK
+ || (current_block && current_mid == 0
+ && nd_type(node) == NODE_RETURN)) {
+ // First let's save the return value in the VM.
+ if (breakFunc == NULL) {
+ // void rb_vm_break(VALUE val);
+ breakFunc = cast<Function>(
+ module->getOrInsertFunction("rb_vm_break",
+ Type::VoidTy, RubyObjTy, NULL));
+ }
+ std::vector<Value *> params;
+ params.push_back(val);
+ CallInst::Create(breakFunc, params.begin(),
+ params.end(), "", bb);
+ if (nd_type(node) == NODE_RETURN) {
+ // Return-from-block is implemented using a setjmp() call.
+ if (longjmpFunc == NULL) {
+ // void longjmp(jmp_buf, int);
+ longjmpFunc = cast<Function>(
+ module->getOrInsertFunction("longjmp",
+ Type::VoidTy, PtrTy, Type::Int32Ty, NULL));
+ }
+ params.clear();
+ if (return_from_block_jmpbuf == NULL) {
+ return_from_block_jmpbuf =
+ (jmp_buf *)malloc(sizeof(jmp_buf));
+ GET_VM()->return_from_block_jmp_bufs.push_back(
+ return_from_block_jmpbuf);
+ }
+ params.push_back(compile_const_pointer(
+ return_from_block_jmpbuf));
+ params.push_back(ConstantInt::get(Type::Int32Ty, 1));
+ CallInst::Create(longjmpFunc, params.begin(), params.end(), "",
+ bb);
+ }
+ ReturnInst::Create(val, bb);
+ }
+ else if (nd_type(node) == NODE_REDO) {
+ assert(entry_bb != NULL);
+ BranchInst::Create(entry_bb, bb);
+ }
+ else {
+ ReturnInst::Create(val, bb);
+ }
+ }
+ compile_dead_branch();
+ return val;
+}
+
+Value *
RoxorCompiler::compile_class_path(NODE *node)
{
if (nd_type(node) == NODE_COLON3) {
@@ -2060,7 +2173,6 @@
current_block = NULL;
previous_block = NULL;
block_saved = false;
-
parse_in_eval = false;
load_path = rb_ary_new();
@@ -3393,80 +3505,8 @@
case NODE_NEXT:
case NODE_REDO:
case NODE_RETURN:
- {
- const bool within_loop = current_loop_begin_bb != NULL
- && current_loop_body_bb != NULL
- && current_loop_end_bb != NULL;
-
- if (!current_block && !within_loop) {
- if (nd_type(node) == NODE_BREAK) {
- rb_raise(rb_eLocalJumpError, "unexpected break");
- }
- if (nd_type(node) == NODE_NEXT) {
- rb_raise(rb_eLocalJumpError, "unexpected next");
- }
- if (nd_type(node) == NODE_REDO) {
- rb_raise(rb_eLocalJumpError, "unexpected redo");
- }
- }
-
- Value *val = node->nd_head != NULL
- ? compile_node(node->nd_head) : nilVal;
-
- if (within_loop) {
- if (nd_type(node) == NODE_BREAK) {
- current_loop_exit_val = val;
- BranchInst::Create(current_loop_end_bb, bb);
- }
- else if (nd_type(node) == NODE_NEXT) {
- BranchInst::Create(current_loop_begin_bb, bb);
- }
- else if (nd_type(node) == NODE_REDO) {
- BranchInst::Create(current_loop_body_bb, bb);
- }
- else {
- ReturnInst::Create(val, bb);
- }
- }
- else {
- if (nd_type(node) == NODE_BREAK) {
- if (breakFunc == NULL) {
- // void rb_vm_break(VALUE val);
- breakFunc = cast<Function>(
- module->getOrInsertFunction("rb_vm_break",
- Type::VoidTy, RubyObjTy, NULL));
- }
- std::vector<Value *> params;
- params.push_back(val);
- CallInst::Create(breakFunc, params.begin(),
- params.end(), "", bb);
- ReturnInst::Create(val, bb);
- }
- else if (nd_type(node) == NODE_REDO) {
- assert(entry_bb != NULL);
- BranchInst::Create(entry_bb, bb);
- }
- else {
- ReturnInst::Create(val, bb);
- }
- }
- compile_dead_branch();
- return val;
- }
- break;
-
case NODE_RETRY:
- {
- if (begin_bb == NULL) {
- rb_raise(rb_eSyntaxError, "unexpected retry");
- }
- // TODO raise a SyntaxError if called outside of a "rescue"
- // block.
- BranchInst::Create(begin_bb, bb);
- compile_dead_branch();
- return nilVal;
- }
- break;
+ return compile_jump(node);
case NODE_CONST:
assert(node->nd_vid > 0);
@@ -4184,13 +4224,58 @@
NODE *old_current_block_node = current_block_node;
ID old_current_mid = current_mid;
bool old_current_block = current_block;
+ jmp_buf *old_return_from_block_jmpbuf =
+ return_from_block_jmpbuf;
+
current_mid = 0;
current_block = true;
+ return_from_block_jmpbuf = NULL;
assert(node->nd_body != NULL);
Value *block = compile_node(node->nd_body);
assert(Function::classof(block));
+ if (return_from_block_jmpbuf != NULL) {
+ // The block we just compiled contains one (or more)
+ // return expressions and provided us a longjmp buffer.
+ // Let's compile a call to setjmp() and make sure to
+ // return if its return value is non-zero.
+ if (setjmpFunc == NULL) {
+ // int setjmp(jmp_buf);
+ setjmpFunc = cast<Function>(
+ module->getOrInsertFunction("setjmp",
+ Type::Int32Ty, PtrTy, NULL));
+ }
+ std::vector<Value *> params;
+ params.push_back(compile_const_pointer(
+ return_from_block_jmpbuf));
+ Value *retval = CallInst::Create(setjmpFunc,
+ params.begin(), params.end(), "", bb);
+
+ Function *f = bb->getParent();
+ BasicBlock *ret_bb = BasicBlock::Create("ret", f);
+ BasicBlock *no_ret_bb = BasicBlock::Create("no_ret", f);
+ Value *need_ret = new ICmpInst(ICmpInst::ICMP_NE,
+ retval, ConstantInt::get(Type::Int32Ty, 0),
+ "", bb);
+ BranchInst::Create(ret_bb, no_ret_bb, need_ret, bb);
+
+ bb = ret_bb;
+ if (brokenWithFunc == NULL) {
+ // VALUE rb_vm_pop_broken_value(void);
+ brokenWithFunc = cast<Function>(
+ module->getOrInsertFunction(
+ "rb_vm_pop_broken_value",
+ RubyObjTy, NULL));
+ }
+ params.clear();
+ Value *val = CallInst::Create(brokenWithFunc,
+ params.begin(), params.end(), "", bb);
+ ReturnInst::Create(val, bb);
+
+ bb = no_ret_bb;
+ }
+
current_loop_begin_bb = old_current_loop_begin_bb;
current_loop_body_bb = old_current_loop_body_bb;
current_loop_end_bb = old_current_loop_end_bb;
@@ -4223,6 +4308,7 @@
current_block_func = old_current_block_func;
current_block_node = old_current_block_node;
+ return_from_block_jmpbuf = old_return_from_block_jmpbuf;
dvars = old_dvars;
return caller;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20090403/4b9b9d7f/attachment-0001.html>
More information about the macruby-changes
mailing list