Revision: 1288 http://trac.macosforge.org/projects/ruby/changeset/1288 Author: lsansonetti@apple.com Date: 2009-04-02 19:11:56 -0700 (Thu, 02 Apr 2009) Log Message: ----------- a faster implementation of dvars + fixed eval bugs + preliminary implementation of binding Modified Paths: -------------- MacRuby/branches/experimental/TODO MacRuby/branches/experimental/bench.rb MacRuby/branches/experimental/eval.c MacRuby/branches/experimental/id.c MacRuby/branches/experimental/id.h MacRuby/branches/experimental/load.c MacRuby/branches/experimental/main.c MacRuby/branches/experimental/numeric.c MacRuby/branches/experimental/proc.c MacRuby/branches/experimental/roxor.cpp MacRuby/branches/experimental/roxor.h MacRuby/branches/experimental/tool/compile_prelude.rb MacRuby/branches/experimental/vm_eval.c Modified: MacRuby/branches/experimental/TODO =================================================================== --- MacRuby/branches/experimental/TODO 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/TODO 2009-04-03 02:11:56 UTC (rev 1288) @@ -16,6 +16,7 @@ [X] fast #send [ ] fast regexp =~ [X] fast break +[ ] write a pass manager to eliminate unnecessary arrays generated by massigns [ ] Array and Hash subclasses to hold immediate Ruby objects without boxing [/] implement backtracing/symbolication [/] port all rb_define_method() calls to rb_objc_define_method() Modified: MacRuby/branches/experimental/bench.rb =================================================================== --- MacRuby/branches/experimental/bench.rb 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/bench.rb 2009-04-03 02:11:56 UTC (rev 1288) @@ -147,7 +147,25 @@ o = proc {} i=0; while i<30000000; o.call; i+=1; end end + bm.report('30000000 dvar write') do + i=0 + 30000000.times { i=1 } + end + # Eval + bm.report('1000 eval') do + i=0 + s = "#{1+1}+#{20+20}" + while i<1000 + eval(s) + i+=1 + end + end + bm.report('30000000 binding-var write') do + i=0 + eval('while i<30000000; i+=1; end') + end + # Break bm.report('30000000 while break') do i=0; while i<30000000; while true; i+=1; break; end; end @@ -198,16 +216,6 @@ end end - # Eval - bm.report('1000 eval') do - i=0 - s = "#{1+1}+#{20+20}" - while i<1000 - eval(s) - i+=1 - end - end - # Method bm.report('3000000 Method#call w/ 0 arg') do o = Class1.new Modified: MacRuby/branches/experimental/eval.c =================================================================== --- MacRuby/branches/experimental/eval.c 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/eval.c 2009-04-03 02:11:56 UTC (rev 1288) @@ -97,6 +97,7 @@ static void ruby_finalize_1(void) { + rb_vm_finalize(); ruby_sig_finalize(); //GET_THREAD()->errinfo = Qnil; rb_gc_call_finalizer_at_exit(); @@ -195,7 +196,7 @@ return FIX2INT(n); } rb_vm_set_running(true); - rb_vm_run(RSTRING_PTR(rb_progname), (NODE *)n); + rb_vm_run(RSTRING_PTR(rb_progname), (NODE *)n, NULL); return ruby_cleanup(0); } @@ -736,8 +737,17 @@ static VALUE rb_f_local_variables(VALUE rcv, SEL sel) { - // TODO - return Qnil; + rb_vm_binding_t *binding = rb_vm_current_binding(); + assert(binding != NULL); + + VALUE ary = rb_ary_new(); + rb_vm_local_t *l; + + for (l = binding->locals; l != NULL; l = l->next) { + rb_ary_push(ary, ID2SYM(l->name)); + } + + return ary; } @@ -820,27 +830,3 @@ //rb_ivar_set(exception_error, idThrowState, INT2FIX(TAG_FATAL)); rb_objc_retain((void *)exception_error); } - - -/* for parser */ - -int -rb_dvar_defined(ID id) -{ - // TODO - return 0; -} - -int -rb_local_defined(ID id) -{ - // TODO - return 0; -} - -int -rb_parse_in_eval(void) -{ - // TODO - return 0; -} Modified: MacRuby/branches/experimental/id.c =================================================================== --- MacRuby/branches/experimental/id.c 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/id.c 2009-04-03 02:11:56 UTC (rev 1288) @@ -74,6 +74,11 @@ sel__send__ = sel_registerName("__send__:"); selEqTilde = sel_registerName("=~:"); selEval = sel_registerName("eval:"); + selInstanceEval = sel_registerName("instance_eval:"); + selClassEval = sel_registerName("class_eval:"); + selModuleEval = sel_registerName("module_eval:"); + selLocalVariables = sel_registerName("local_variables"); + selBinding = sel_registerName("binding"); selEach = sel_registerName("each"); selEqq = sel_registerName("===:"); selBackquote = sel_registerName("`:"); Modified: MacRuby/branches/experimental/id.h =================================================================== --- MacRuby/branches/experimental/id.h 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/id.h 2009-04-03 02:11:56 UTC (rev 1288) @@ -85,6 +85,11 @@ extern SEL sel__send__; extern SEL selEqTilde; extern SEL selEval; +extern SEL selInstanceEval; +extern SEL selClassEval; +extern SEL selModuleEval; +extern SEL selLocalVariables; +extern SEL selBinding; extern SEL selEach; extern SEL selEqq; extern SEL selBackquote; Modified: MacRuby/branches/experimental/load.c =================================================================== --- MacRuby/branches/experimental/load.c 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/load.c 2009-04-03 02:11:56 UTC (rev 1288) @@ -270,7 +270,7 @@ if (node == NULL) { rb_raise(rb_eSyntaxError, "compile error"); } - rb_vm_run(fname_str, node); + rb_vm_run(fname_str, node, NULL); } void Modified: MacRuby/branches/experimental/main.c =================================================================== --- MacRuby/branches/experimental/main.c 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/main.c 2009-04-03 02:11:56 UTC (rev 1288) @@ -30,6 +30,6 @@ ruby_sysinit(&argc, &argv); { ruby_init(); - return ruby_run_node(ruby_options(argc, argv)); + rb_exit(ruby_run_node(ruby_options(argc, argv))); } } Modified: MacRuby/branches/experimental/numeric.c =================================================================== --- MacRuby/branches/experimental/numeric.c 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/numeric.c 2009-04-03 02:11:56 UTC (rev 1288) @@ -532,30 +532,11 @@ VALUE rb_float_new(double d) { -#if 0 - //printf("double %f\n", d); - struct rb_float_cache *cache = rb_vm_float_cache(d); - - if (cache->obj != Qnil) { - //printf("cached\n"); - return cache->obj; - } - - cache->count++; -#endif - NEWOBJ(flt, struct RFloat); OBJSETUP(flt, rb_cFloat, T_FLOAT); flt->float_value = d; -#if 0 - rb_objc_retain((void *)flt); - //if (cache->count == 10) { - cache->obj = (VALUE)flt; - //} -#endif - return (VALUE)flt; } Modified: MacRuby/branches/experimental/proc.c =================================================================== --- MacRuby/branches/experimental/proc.c 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/proc.c 2009-04-03 02:11:56 UTC (rev 1288) @@ -17,10 +17,6 @@ #define GetProcPtr(obj, ptr) GetCoreDataFromValue(obj, rb_vm_block_t, ptr) -typedef struct { - // TODO -} rb_binding_t; - VALUE rb_cUnboundMethod; VALUE rb_cMethod; VALUE rb_cBinding; @@ -202,8 +198,8 @@ binding_alloc(VALUE klass) { VALUE obj; - rb_binding_t *bind; - obj = Data_Make_Struct(klass, rb_binding_t, + rb_vm_binding_t *bind; + obj = Data_Make_Struct(klass, rb_vm_binding_t, NULL, NULL, bind); return obj; } @@ -232,17 +228,9 @@ VALUE rb_binding_new(void) { -#if 0 // TODO - rb_thread_t *th = GET_THREAD(); - rb_control_frame_t *cfp = vm_get_ruby_level_cfp(th, th->cfp); - VALUE bindval = binding_alloc(rb_cBinding); - rb_binding_t *bind; - - GetBindingPtr(bindval, bind); - GC_WB(&bind->env, vm_make_env_object(th, cfp)); - return bindval; -#endif - return Qnil; + rb_vm_binding_t *bind = rb_vm_current_binding(); + assert(bind != NULL); + return Data_Wrap_Struct(rb_cBinding, NULL, NULL, bind); } /* @@ -1497,22 +1485,16 @@ static VALUE proc_binding(VALUE self, SEL sel) { -#if 0 // TODO - rb_proc_t *proc; - VALUE bindval = binding_alloc(rb_cBinding); - rb_binding_t *bind; + rb_vm_block_t *block; + GetProcPtr(self, block); - GetProcPtr(self, proc); - GetBindingPtr(bindval, bind); + rb_vm_binding_t *binding = (rb_vm_binding_t *)xmalloc( + sizeof(rb_vm_binding_t)); - if (TYPE(proc->block.iseq) == T_NODE) { - rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); - } + GC_WB(&binding->self, self); + GC_WB(&binding->locals, block->locals); - bind->env = proc->envval; - return bindval; -#endif - return Qnil; + return Data_Wrap_Struct(rb_cBinding, NULL, NULL, binding); } static VALUE curry(VALUE dummy, VALUE args, int argc, VALUE *argv); Modified: MacRuby/branches/experimental/roxor.cpp =================================================================== --- MacRuby/branches/experimental/roxor.cpp 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/roxor.cpp 2009-04-03 02:11:56 UTC (rev 1288) @@ -1,8 +1,8 @@ /* ROXOR: the new MacRuby VM that rocks! */ -#define ROXOR_COMPILER_DEBUG 0 -#define ROXOR_VM_DEBUG 0 -#define ROXOR_DUMP_IR 0 +#define ROXOR_COMPILER_DEBUG 0 +#define ROXOR_VM_DEBUG 0 +#define ROXOR_DUMP_IR_BEFORE_EXIT 0 #include <llvm/Module.h> #include <llvm/DerivedTypes.h> @@ -170,7 +170,7 @@ const char *fname; std::map<ID, Value *> lvars; - std::map<ID, Value *> dvars; + std::vector<ID> dvars; std::map<ID, int *> ivar_slots_cache; #if ROXOR_COMPILER_DEBUG @@ -203,6 +203,7 @@ Function *fastEqqFunc; Function *whenSplatFunc; Function *prepareBlockFunc; + Function *pushBindingFunc; Function *getBlockFunc; Function *currentBlockObjectFunc; Function *getConstFunc; @@ -245,7 +246,8 @@ Constant *undefVal; Constant *splatArgFollowsVal; const Type *RubyObjTy; - const Type *RubyObjPtrTy; + const Type *RubyObjPtrTy; + const Type *RubyObjPtrPtrTy; const Type *PtrTy; const Type *IntTy; @@ -275,6 +277,7 @@ Value *compile_fast_eqq_call(Value *selfVal, Value *comparedToVal); Value *compile_attribute_assign(NODE *node, Value *extra_val); Value *compile_block_create(NODE *node=NULL); + Value *compile_binding(void); Value *compile_optimized_dispatch_call(SEL sel, int argc, std::vector<Value *> ¶ms); Value *compile_ivar_read(ID vid); Value *compile_ivar_assignment(ID vid, Value *val); @@ -284,30 +287,14 @@ Value *compile_singleton_class(Value *obj); Value *compile_defined_expression(NODE *node); Value *compile_dstr(NODE *node); + Value *compile_dvar_slot(ID name); + void compile_dead_branch(void); void compile_landing_pad_header(void); void compile_landing_pad_footer(void); void compile_rethrow_exception(void); + Value *compile_lvar_slot(ID name); - Value *get_var(ID name, std::map<ID, Value *> &container, - bool do_assert) { - std::map<ID, Value *>::iterator iter = container.find(name); - if (do_assert) { -#if ROXOR_COMPILER_DEBUG - printf("get_var %s\n", rb_id2name(name)); -#endif - assert(iter != container.end()); - return iter->second; - } - else { - return iter != container.end() ? iter->second : NULL; - } - } - - Value *get_lvar(ID name, bool do_assert=true) { - return get_var(name, lvars, do_assert); - } - int *get_slot_cache(ID id) { if (current_block || !current_instance_method) { // TODO should also return NULL if we are inside a module @@ -413,6 +400,7 @@ VALUE last_status; VALUE errinfo; int safe_level; + std::vector<rb_vm_binding_t *> bindings; std::map<NODE *, rb_vm_block_t *> blocks; std::map<double, struct rb_float_cache *> float_cache; unsigned char method_missing_reason; @@ -520,6 +508,18 @@ outers[klass] = class_outer; } } + + VALUE *get_binding_lvar(ID name) { + if (!bindings.empty()) { + rb_vm_binding_t *b = bindings.back(); + for (rb_vm_local_t *l = b->locals; l != NULL; l = l->next) { + if (l->name == name) { + return l->value; + } + } + } + return NULL; + } }; RoxorVM *RoxorVM::current = NULL; @@ -551,6 +551,7 @@ fastEqqFunc = NULL; whenSplatFunc = NULL; prepareBlockFunc = NULL; + pushBindingFunc = NULL; getBlockFunc = NULL; currentBlockObjectFunc = NULL; getConstFunc = NULL; @@ -595,6 +596,7 @@ twoVal = ConstantInt::get(IntTy, 2); RubyObjPtrTy = PointerType::getUnqual(RubyObjTy); + RubyObjPtrPtrTy = PointerType::getUnqual(RubyObjPtrTy); nilVal = ConstantInt::get(RubyObjTy, Qnil); trueVal = ConstantInt::get(RubyObjTy, Qtrue); falseVal = ConstantInt::get(RubyObjTy, Qfalse); @@ -943,7 +945,8 @@ assert(current_block_func != NULL && current_block_node != NULL); if (prepareBlockFunc == NULL) { - // void *rb_vm_prepare_block(Function *func, NODE *node, VALUE self, int dvars_size, ...); + // void *rb_vm_prepare_block(Function *func, NODE *node, VALUE self, + // int dvars_size, ...); std::vector<const Type *> types; types.push_back(PtrTy); types.push_back(PtrTy); @@ -959,20 +962,58 @@ params.push_back(compile_const_pointer(current_block_node)); params.push_back(current_self); + // Dvars. params.push_back(ConstantInt::get(Type::Int32Ty, (int)dvars.size())); + for (std::vector<ID>::iterator iter = dvars.begin(); + iter != dvars.end(); ++iter) { + params.push_back(compile_lvar_slot(*iter)); + } - std::map<ID, Value *>::iterator iter = dvars.begin(); - while (iter != dvars.end()) { - std::map<ID, Value *>::iterator iter2 = lvars.find(iter->first); - assert(iter2 != lvars.end()); - params.push_back(iter2->second); - ++iter; + // Lvars. + params.push_back(ConstantInt::get(Type::Int32Ty, (int)lvars.size())); + for (std::map<ID, Value *>::iterator iter = lvars.begin(); + iter != lvars.end(); ++iter) { + ID name = iter->first; + Value *slot = iter->second; + if (std::find(dvars.begin(), dvars.end(), name) == dvars.end()) { + params.push_back(ConstantInt::get(IntTy, (long)name)); + params.push_back(slot); + } } - return CallInst::Create(prepareBlockFunc, params.begin(), params.end(), "", bb); + return CallInst::Create(prepareBlockFunc, params.begin(), params.end(), + "", bb); } Value * +RoxorCompiler::compile_binding(void) +{ + if (pushBindingFunc == NULL) { + // void rb_vm_push_binding(VALUE self, int lvars_size, ...); + std::vector<const Type *> types; + types.push_back(RubyObjTy); + types.push_back(Type::Int32Ty); + FunctionType *ft = FunctionType::get(Type::VoidTy, types, true); + pushBindingFunc = cast<Function> + (module->getOrInsertFunction("rb_vm_push_binding", ft)); + } + + std::vector<Value *> params; + params.push_back(current_self); + + // Lvars. + params.push_back(ConstantInt::get(Type::Int32Ty, (int)lvars.size())); + for (std::map<ID, Value *>::iterator iter = lvars.begin(); + iter != lvars.end(); ++iter) { + params.push_back(ConstantInt::get(IntTy, (long)iter->first)); + params.push_back(iter->second); + } + + return CallInst::Create(pushBindingFunc, params.begin(), params.end(), + "", bb); +} + +Value * RoxorCompiler::compile_ivar_read(ID vid) { if (getIvarFunc == NULL) { @@ -1277,6 +1318,34 @@ } Value * +RoxorCompiler::compile_dvar_slot(ID name) +{ + // TODO we should cache this + int i = 0, idx = -1; + for (std::vector<ID>::iterator iter = dvars.begin(); + iter != dvars.end(); ++iter) { + if (*iter == name) { + idx = i; + break; + } + i++; + } + if (idx == -1) { + return NULL; + } + + Function::ArgumentListType::iterator fargs_i = + bb->getParent()->getArgumentList().begin(); + ++fargs_i; // skip self + ++fargs_i; // skip sel + Value *dvars_ary = fargs_i; + + Value *index = ConstantInt::get(Type::Int32Ty, idx); + Value *slot = GetElementPtrInst::Create(dvars_ary, index, rb_id2name(name), bb); + return new LoadInst(slot, "", bb); +} + +Value * RoxorCompiler::compile_class_path(NODE *node) { if (nd_type(node) == NODE_COLON3) { @@ -2273,15 +2342,22 @@ { rb_vm_arity_t arity = rb_vm_node_arity(node); const int nargs = bb == NULL ? 0 : arity.real; + const bool has_dvars = current_block && current_mid == 0; // Get dynamic vars. - if (current_block && node->nd_tbl != NULL) { + if (has_dvars && node->nd_tbl != NULL) { const int args_count = (int)node->nd_tbl[0]; const int lvar_count = (int)node->nd_tbl[args_count + 1]; for (int i = 0; i < lvar_count; i++) { ID id = node->nd_tbl[i + args_count + 2]; if (lvars.find(id) != lvars.end()) { - dvars[id] = NULL; + std::vector<ID>::iterator iter = std::find(dvars.begin(), dvars.end(), id); + if (iter == dvars.end()) { +#if ROXOR_COMPILER_DEBUG + printf("dvar %s\n", rb_id2name(id)); +#endif + dvars.push_back(id); + } } } } @@ -2290,8 +2366,8 @@ std::vector<const Type *> types; types.push_back(RubyObjTy); // self types.push_back(PtrTy); // sel - for (int i = 0, count = dvars.size(); i < count; i++) { - types.push_back(RubyObjPtrTy); + if (has_dvars) { + types.push_back(RubyObjPtrPtrTy); // dvars array } for (int i = 0; i < nargs; ++i) { types.push_back(RubyObjTy); @@ -2319,19 +2395,9 @@ Value *sel = arg++; sel->setName("sel"); - for (std::map<ID, Value *>::iterator iter = dvars.begin(); - iter != dvars.end(); - ++iter) { - - ID id = iter->first; - assert(iter->second == NULL); - - Value *val = arg++; - val->setName(std::string("dyna_") + rb_id2name(id)); -#if ROXOR_COMPILER_DEBUG - printf("dvar %s\n", rb_id2name(id)); -#endif - lvars[id] = val; + if (has_dvars) { + Value *dvars_arg = arg++; + dvars_arg->setName("dvars"); } if (node->nd_tbl != NULL) { @@ -2373,12 +2439,14 @@ if (lvars.find(id) != lvars.end()) { continue; } + if (std::find(dvars.begin(), dvars.end(), id) == dvars.end()) { #if ROXOR_COMPILER_DEBUG - printf("var %s\n", rb_id2name(id)); + printf("lvar %s\n", rb_id2name(id)); #endif - Value *store = new AllocaInst(RubyObjTy, "", bb); - new StoreInst(nilVal, store, bb); - lvars[id] = store; + Value *store = new AllocaInst(RubyObjTy, "", bb); + new StoreInst(nilVal, store, bb); + lvars[id] = store; + } } NODE *args_node = node->nd_args; @@ -2410,9 +2478,12 @@ ++iter; // skip sel NODE *opt_node = args_node->nd_opt; if (opt_node != NULL) { - int to_skip = dvars.size() + args_node->nd_frml; + int to_skip = args_node->nd_frml; + if (has_dvars) { + to_skip++; // dvars array + } for (i = 0; i < to_skip; i++) { - ++iter; // skip dvars and args required on the left-side + ++iter; // skip dvars and args required on the left-side } iter = compile_optional_arguments(iter, opt_node); } @@ -2448,8 +2519,8 @@ case NODE_LVAR: { assert(node->nd_vid > 0); - - return new LoadInst(get_lvar(node->nd_vid), "", bb); + + return new LoadInst(compile_lvar_slot(node->nd_vid), "", bb); } break; @@ -2577,7 +2648,7 @@ case NODE_DASGN: case NODE_DASGN_CURR: { - Value *slot = get_lvar(ln->nd_vid); + Value *slot = compile_lvar_slot(ln->nd_vid); new StoreInst(elt, slot, bb); } break; @@ -2616,12 +2687,9 @@ assert(node->nd_vid > 0); assert(node->nd_value != NULL); - Value *slot = get_lvar(node->nd_vid); - Value *new_val = compile_node(node->nd_value); + new StoreInst(new_val, compile_lvar_slot(node->nd_vid), bb); - new StoreInst(new_val, slot, bb); - return new_val; } break; @@ -3279,9 +3347,23 @@ } params[3] = blockVal; + // If we are calling a method that needs a top-level binding + // object, let's create it. + // (Note: this won't work if the method is aliased, but we can + // live with that for now) + if (sel == selEval + || sel == selInstanceEval + || sel == selClassEval + || sel == selModuleEval + || sel == selLocalVariables + || sel == selBinding) { + compile_binding(); + } + // Can we optimize the call? if (!super_call && !splat_args) { - Value *optimizedCall = compile_optimized_dispatch_call(sel, argc, params); + Value *optimizedCall = + compile_optimized_dispatch_call(sel, argc, params); if (optimizedCall != NULL) { return optimizedCall; } @@ -4080,7 +4162,7 @@ case NODE_FOR: case NODE_ITER: { - std::map<ID, Value *> old_dvars = dvars; + std::vector<ID> old_dvars = dvars; BasicBlock *old_current_loop_begin_bb = current_loop_begin_bb; BasicBlock *old_current_loop_body_bb = current_loop_body_bb; @@ -4302,6 +4384,33 @@ return f; } +Value * +RoxorCompiler::compile_lvar_slot(ID name) +{ + std::map<ID, Value *>::iterator iter = lvars.find(name); + if (iter != lvars.end()) { +#if ROXOR_COMPILER_DEBUG + printf("get_lvar %s\n", rb_id2name(name)); +#endif + return iter->second; + } + VALUE *var = GET_VM()->get_binding_lvar(name); + if (var != NULL) { +#if ROXOR_COMPILER_DEBUG + printf("get_binding_lvar %s (%p)\n", rb_id2name(name), *(void **)var); +#endif + Value *int_val = ConstantInt::get(IntTy, (long)var); + return new IntToPtrInst(int_val, RubyObjPtrTy, "", bb); + } + assert(current_block); + Value *slot = compile_dvar_slot(name); + assert(slot != NULL); +#if ROXOR_COMPILER_DEBUG + printf("get_dvar %s\n", rb_id2name(name)); +#endif + return slot; +} + // VM primitives #define MAX_ARITY 20 @@ -4872,6 +4981,87 @@ } static inline VALUE +__rb_vm_bcall(VALUE self, VALUE dvars, IMP pimp, const rb_vm_arity_t &arity, int argc, const VALUE *argv) +{ + if ((arity.real != argc) || (arity.max == -1)) { + VALUE *new_argv = (VALUE *)alloca(sizeof(VALUE) * arity.real); + assert(argc >= arity.min); + assert((arity.max == -1) || (argc <= arity.max)); + int used_opt_args = argc - arity.min; + int opt_args, rest_pos; + if (arity.max == -1) { + opt_args = arity.real - arity.min - 1; + rest_pos = arity.left_req + opt_args; + } + else { + opt_args = arity.real - arity.min; + rest_pos = -1; + } + for (int i = 0; i < arity.real; ++i) { + if (i < arity.left_req) { + // required args before optional args + new_argv[i] = argv[i]; + } + else if (i < arity.left_req + opt_args) { + // optional args + int opt_arg_index = i - arity.left_req; + if (opt_arg_index >= used_opt_args) { + new_argv[i] = Qundef; + } + else { + new_argv[i] = argv[i]; + } + } + else if (i == rest_pos) { + // rest + int rest_size = argc - arity.real + 1; + if (rest_size <= 0) { + new_argv[i] = rb_ary_new(); + } + else { + new_argv[i] = rb_ary_new4(rest_size, &argv[i]); + } + } + else { + // required args after optional args + new_argv[i] = argv[argc-(arity.real - i)]; + } + } + argv = new_argv; + argc = arity.real; + } + + assert(pimp != NULL); + + VALUE (*imp)(VALUE, SEL, VALUE, ...) = (VALUE (*)(VALUE, SEL, VALUE, ...))pimp; + + switch (argc) { + case 0: + return (*imp)(self, 0, dvars); + case 1: + return (*imp)(self, 0, dvars, argv[0]); + case 2: + return (*imp)(self, 0, dvars, argv[0], argv[1]); + case 3: + return (*imp)(self, 0, dvars, argv[0], argv[1], argv[2]); + case 4: + return (*imp)(self, 0, dvars, argv[0], argv[1], argv[2], argv[3]); + case 5: + return (*imp)(self, 0, dvars, argv[0], argv[1], argv[2], argv[3], argv[4]); + case 6: + return (*imp)(self, 0, dvars, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); + case 7: + return (*imp)(self, 0, dvars, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + case 8: + return (*imp)(self, 0, dvars, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); + case 9: + return (*imp)(self, 0, dvars, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); + } + printf("invalid argc %d\n", argc); + abort(); +} + +static inline VALUE __rb_vm_rcall(VALUE self, NODE *node, IMP pimp, const rb_vm_arity_t &arity, int argc, const VALUE *argv) { @@ -4952,7 +5142,6 @@ } printf("invalid argc %d\n", argc); abort(); - return Qnil; } static inline Method @@ -5151,8 +5340,11 @@ ocache.helper = (struct ocall_helper *)malloc( sizeof(struct ocall_helper)); ocache.helper->bs_method = rb_bs_find_method(klass, sel); - assert(rb_objc_fill_sig(self, klass, sel, - &ocache.helper->sig, ocache.helper->bs_method)); + const bool ok = rb_objc_fill_sig(self, klass, sel, + &ocache.helper->sig, ocache.helper->bs_method); + if (!ok) { + abort(); + } } } else { @@ -5301,6 +5493,12 @@ GET_VM()->current_block = old_b; GET_VM()->block_saved = old_block_saved; + + if (!GET_VM()->bindings.empty()) { + rb_objc_release(GET_VM()->bindings.back()); + GET_VM()->bindings.pop_back(); + } + return retval; } @@ -5428,10 +5626,11 @@ GET_VM()->blocks.find(node); rb_vm_block_t *b; + bool cached = false; if (iter == GET_VM()->blocks.end()) { b = (rb_vm_block_t *)xmalloc(sizeof(rb_vm_block_t) - + (sizeof(VALUE) * dvars_size)); + + (sizeof(VALUE *) * dvars_size)); if (nd_type(node) == NODE_IFUNC) { assert(llvm_function == NULL); @@ -5452,24 +5651,85 @@ else { b = iter->second; assert(b->dvars_size == dvars_size); + cached = true; } b->self = self; b->node = node; - if (dvars_size > 0) { - va_list ar; - va_start(ar, dvars_size); - for (int i = 0; i < dvars_size; ++i) { - b->dvars[i] = va_arg(ar, VALUE *); + va_list ar; + va_start(ar, dvars_size); + for (int i = 0; i < dvars_size; ++i) { + b->dvars[i] = va_arg(ar, VALUE *); + } + int lvars_size = va_arg(ar, int); + if (lvars_size > 0) { + if (!cached) { + rb_vm_local_t **l = &b->locals; + for (int i = 0; i < lvars_size; i++) { + GC_WB(l, xmalloc(sizeof(rb_vm_local_t))); + l = &(*l)->next; + } } - va_end(ar); + rb_vm_local_t *l = b->locals; + for (int i = 0; i < lvars_size; ++i) { + assert(l != NULL); + l->name = va_arg(ar, ID); + l->value = va_arg(ar, VALUE *); + l = l->next; + } } + va_end(ar); return b; } extern "C" +void +rb_vm_push_binding(VALUE self, int lvars_size, ...) +{ + rb_vm_binding_t *b = (rb_vm_binding_t *)xmalloc(sizeof(rb_vm_binding_t)); + GC_WB(&b->self, self); + + va_list ar; + va_start(ar, lvars_size); + rb_vm_local_t **l = &b->locals; + for (int i = 0; i < lvars_size; ++i) { + GC_WB(l, xmalloc(sizeof(rb_vm_local_t))); + (*l)->name = va_arg(ar, ID); + (*l)->value = va_arg(ar, VALUE *); + (*l)->next = NULL; + l = &(*l)->next; + } + va_end(ar); + + rb_objc_retain(b); + GET_VM()->bindings.push_back(b); +} + +extern "C" +rb_vm_binding_t * +rb_vm_current_binding(void) +{ + return GET_VM()->bindings.empty() ? NULL : GET_VM()->bindings.back(); +} + +extern "C" +void +rb_vm_add_binding(rb_vm_binding_t *binding) +{ + GET_VM()->bindings.push_back(binding); +} + +extern "C" +void +rb_vm_pop_binding(void) +{ + GET_VM()->bindings.pop_back(); +} + + +extern "C" VALUE rb_vm_call(VALUE self, SEL sel, int argc, const VALUE *argv, bool super) { @@ -5636,10 +5896,9 @@ } } - int dvars_size = b->dvars_size; rb_vm_arity_t arity = b->arity; - if (dvars_size > 0 || argc < arity.min || argc > arity.max) { + if (argc < arity.min || argc > arity.max) { VALUE *new_argv; if (argc == 1 && TYPE(argv[0]) == T_ARRAY && (arity.min > 1 || (arity.min == 1 && arity.min != arity.max))) { // Expand the array @@ -5650,63 +5909,47 @@ } argv = new_argv; argc = ary_len; - if (dvars_size == 0 && argc >= arity.min - && (argc <= arity.max || b->arity.max == -1)) { - return __rb_vm_rcall(b->self, b->node, b->imp, arity, argc, - argv); + if (argc >= arity.min && (argc <= arity.max || b->arity.max == -1)) { + goto block_call; } } int new_argc; if (argc <= arity.min) { - new_argc = dvars_size + arity.min; + new_argc = arity.min; } else if (argc > arity.max && b->arity.max != -1) { - new_argc = dvars_size + arity.max; + new_argc = arity.max; } else { - new_argc = dvars_size + argc; + new_argc = argc; } new_argv = (VALUE *)alloca(sizeof(VALUE) * new_argc); - for (int i = 0; i < dvars_size; i++) { - new_argv[i] = (VALUE)b->dvars[i]; + for (int i = 0; i < new_argc; i++) { + new_argv[i] = i < argc ? argv[i] : Qnil; } - for (int i = 0; i < new_argc - dvars_size; i++) { - new_argv[dvars_size + i] = i < argc ? argv[i] : Qnil; - } argc = new_argc; argv = new_argv; - if (dvars_size > 0) { - arity.min += dvars_size; - if (arity.max != -1) { - arity.max += dvars_size; - } - arity.real += dvars_size; - arity.left_req += dvars_size; - } } #if ROXOR_VM_DEBUG - printf("yield block %p argc %d arity %d dvars %d\n", b, argc, - arity.real, b->dvars_size); + printf("yield block %p argc %d arity %d\n", b, argc, arity.real); #endif +block_call: + // We need to preserve dynamic variable slots here because our block may // call the parent method which may call the block again, and since dvars // are currently implemented using alloca() we will painfully die if the // previous slots are not restored. - - VALUE **old_dvars; - if (dvars_size > 0) { - old_dvars = (VALUE **)alloca(sizeof(VALUE *) * dvars_size); - memcpy(old_dvars, b->dvars, sizeof(VALUE) * dvars_size); + VALUE *old_dvars[100]; + assert(b->dvars_size < 100); + for (int i = 0; i < b->dvars_size; i++) { + old_dvars[i] = b->dvars[i]; } - else { - old_dvars = NULL; - } - VALUE v = __rb_vm_rcall(b->self, b->node, b->imp, arity, argc, argv); + VALUE v = __rb_vm_bcall(b->self, (VALUE)b->dvars, b->imp, b->arity, argc, argv); - if (old_dvars != NULL) { - memcpy(b->dvars, old_dvars, sizeof(VALUE) * dvars_size); + for (int i = 0; i < b->dvars_size; i++) { + b->dvars[i] = old_dvars[i]; } return v; @@ -5780,27 +6023,6 @@ return rb_vm_yield0(argc, argv); } -extern "C" -struct rb_float_cache * -rb_vm_float_cache(double d) -{ - std::map<double, struct rb_float_cache *>::iterator iter = - GET_VM()->float_cache.find(d); - if (iter == GET_VM()->float_cache.end()) { - struct rb_float_cache *fc = (struct rb_float_cache *)malloc( - sizeof(struct rb_float_cache)); - assert(fc != NULL); - - fc->count = 0; - fc->obj = Qnil; - GET_VM()->float_cache[d] = fc; - - return fc; - } - - return iter->second; -} - extern IMP basic_respond_to_imp; // vm_method.c extern "C" @@ -6065,6 +6287,28 @@ } extern "C" +int +rb_parse_in_eval(void) +{ + return rb_vm_parse_in_eval() ? 1 : 0; +} + +extern "C" +int +rb_local_defined(ID id) +{ + return GET_VM()->get_binding_lvar(id) != NULL ? 1 : 0; +} + +extern "C" +int +rb_dvar_defined(ID id) +{ + // TODO + return 0; +} + +extern "C" IMP rb_vm_compile(const char *fname, NODE *node) { @@ -6099,12 +6343,6 @@ printf("compilation/optimization done, took %lld ns\n", elapsedNano); #endif -#if ROXOR_DUMP_IR - printf("IR dump ----------------------------------------------\n"); - RoxorCompiler::module->dump(); - printf("------------------------------------------------------\n"); -#endif - delete compiler; return imp; @@ -6112,10 +6350,18 @@ extern "C" VALUE -rb_vm_run(const char *fname, NODE *node) +rb_vm_run(const char *fname, NODE *node, rb_vm_binding_t *binding) { + if (binding != NULL) { + GET_VM()->bindings.push_back(binding); + } + IMP imp = rb_vm_compile(fname, node); + if (binding != NULL) { + GET_VM()->bindings.pop_back(); + } + try { return ((VALUE(*)(VALUE, SEL))imp)(GET_VM()->current_top_object, 0); } @@ -6133,18 +6379,22 @@ extern "C" VALUE -rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node) +rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node, + rb_vm_binding_t *binding) { assert(klass != 0); VALUE old_top_object = GET_VM()->current_top_object; + if (binding != NULL) { + self = binding->self; + } if (self != 0) { GET_VM()->current_top_object = self; } Class old_class = GET_VM()->current_class; GET_VM()->current_class = (Class)klass; - VALUE val = rb_vm_run(fname, node); + VALUE val = rb_vm_run(fname, node, binding); GET_VM()->current_top_object = old_top_object; GET_VM()->current_class = old_class; @@ -6314,3 +6564,14 @@ rb_objc_retain((void *)top_self); GET_VM()->current_top_object = top_self; } + +extern "C" +void +rb_vm_finalize(void) +{ +#if ROXOR_DUMP_IR_BEFORE_EXIT + printf("IR dump ----------------------------------------------\n"); + RoxorCompiler::module->dump(); + printf("------------------------------------------------------\n"); +#endif +} Modified: MacRuby/branches/experimental/roxor.h =================================================================== --- MacRuby/branches/experimental/roxor.h 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/roxor.h 2009-04-03 02:11:56 UTC (rev 1288) @@ -5,8 +5,52 @@ extern "C" { #endif -VALUE rb_vm_run(const char *fname, NODE *node); -VALUE rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node); +typedef struct { + short min; // min number of args that we accept + short max; // max number of args that we accept (-1 if rest) + short left_req; // number of args required on the left side + short real; // number of args of the low level function +} rb_vm_arity_t; + +struct rb_vm_local { + ID name; + VALUE *value; + struct rb_vm_local *next; +}; +typedef struct rb_vm_local rb_vm_local_t; + +#define VM_BLOCK_PROC 0x0001 // block is a Proc object +#define VM_BLOCK_LAMBDA 0x0002 // block is a lambda + +typedef struct { + VALUE self; + NODE *node; + rb_vm_arity_t arity; + IMP imp; + int flags; + rb_vm_local_t *locals; + int dvars_size; + VALUE *dvars[1]; +} rb_vm_block_t; + +typedef struct { + VALUE self; + rb_vm_local_t *locals; +} rb_vm_binding_t; + +typedef struct { + VALUE oclass; + VALUE rclass; + VALUE recv; + SEL sel; + int arity; + NODE *node; // can be NULL (if pure Objective-C) + void *cache; +} rb_vm_method_t; + +VALUE rb_vm_run(const char *fname, NODE *node, rb_vm_binding_t *binding); +VALUE rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node, + rb_vm_binding_t *binding); IMP rb_vm_compile(const char *fname, NODE *node); bool rb_vm_running(void); @@ -79,38 +123,8 @@ GC_WB(&robj->slots[slot], val); } -typedef struct { - VALUE oclass; - VALUE rclass; - VALUE recv; - SEL sel; - int arity; - NODE *node; // can be NULL (if pure Objective-C) - void *cache; -} rb_vm_method_t; - rb_vm_method_t *rb_vm_get_method(VALUE klass, VALUE obj, ID mid, int scope); -typedef struct { - short min; // min number of args that we accept - short max; // max number of args that we accept (-1 if rest) - short left_req; // number of args required on the left side - short real; // number of args of the low level function -} rb_vm_arity_t; - -#define VM_BLOCK_PROC 0x0001 // block is a Proc object -#define VM_BLOCK_LAMBDA 0x0002 // block is a lambda - -typedef struct { - VALUE self; - NODE *node; - rb_vm_arity_t arity; - IMP imp; - int flags; - int dvars_size; - VALUE *dvars[1]; -} rb_vm_block_t; - static inline rb_vm_block_t * rb_proc_get_block(VALUE proc) { @@ -124,13 +138,10 @@ void rb_vm_restore_current_block(void); VALUE rb_vm_block_eval(rb_vm_block_t *block, int argc, const VALUE *argv); -struct rb_float_cache { - unsigned int count; - VALUE obj; -}; +rb_vm_binding_t *rb_vm_current_binding(void); +void rb_vm_add_binding(rb_vm_binding_t *binding); +void rb_vm_pop_binding(); -struct rb_float_cache *rb_vm_float_cache(double d); - static inline VALUE rb_robject_allocate_instance(VALUE klass) { @@ -168,6 +179,8 @@ } \ while (0) +void rb_vm_finalize(void); + VALUE rb_iseq_compile(VALUE src, VALUE file, VALUE line); VALUE rb_iseq_eval(VALUE iseq); VALUE rb_iseq_new(NODE *node, VALUE filename); Modified: MacRuby/branches/experimental/tool/compile_prelude.rb =================================================================== --- MacRuby/branches/experimental/tool/compile_prelude.rb 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/tool/compile_prelude.rb 2009-04-03 02:11:56 UTC (rev 1288) @@ -80,7 +80,7 @@ rb_vm_run(prelude_name<%=i%>, rb_compile_string( prelude_name<%=i%>, rb_str_new(prelude_code<%=i%>, sizeof(prelude_code<%=i%>) - 1), - 1)); + 1), NULL); % } } Modified: MacRuby/branches/experimental/vm_eval.c =================================================================== --- MacRuby/branches/experimental/vm_eval.c 2009-04-01 12:49:41 UTC (rev 1287) +++ MacRuby/branches/experimental/vm_eval.c 2009-04-03 02:11:56 UTC (rev 1288) @@ -278,7 +278,7 @@ VALUE (*bl_proc) (ANYARGS), VALUE data2) { NODE *node = NEW_IFUNC(bl_proc, data2); - rb_vm_block_t *b = rb_vm_prepare_block(NULL, node, obj, 0); + rb_vm_block_t *b = rb_vm_prepare_block(NULL, node, obj, 0, 0); rb_vm_change_current_block(b); if (cache == NULL) { cache = rb_vm_get_call_cache(sel); @@ -311,13 +311,34 @@ } static VALUE -eval_string(VALUE self, VALUE klass, VALUE src, VALUE scope, const char *file, int line) +eval_string(VALUE self, VALUE klass, VALUE src, VALUE scope, const char *file, + const int line) { - // TODO honor scope + rb_vm_binding_t *b = NULL; + if (scope != Qnil) { + if (!rb_obj_is_kind_of(scope, rb_cBinding)) { + rb_raise(rb_eTypeError, "wrong argument type %s (expected Binding)", + rb_obj_classname(scope)); + } + b = (rb_vm_binding_t *)DATA_PTR(scope); + } + bool old_parse_in_eval = rb_vm_parse_in_eval(); rb_vm_set_parse_in_eval(true); + if (b != NULL) { + // Binding must be added because the parser needs it. + rb_vm_add_binding(b); + } + NODE *node = rb_compile_string(file, src, line); + + if (b != NULL) { + // We remove the binding now but we still pass it to the VM, which + // will use it for compilation. + rb_vm_pop_binding(); + } rb_vm_set_parse_in_eval(old_parse_in_eval); + if (node == NULL) { VALUE exc = rb_vm_current_exception(); if (exc != Qnil) { @@ -327,10 +348,11 @@ rb_raise(rb_eSyntaxError, "compile error"); } } + if (klass == 0) { klass = rb_cObject; } - return rb_vm_run_under(klass, self, file, node); + return rb_vm_run_under(klass, self, file, node, b); } static VALUE @@ -417,13 +439,13 @@ if (!NIL_P(vfile)) { file = RSTRING_PTR(vfile); } - return eval_string(0, self, src, scope, file, line); + return eval_string(self, 0, src, scope, file, line); } VALUE rb_eval_string(const char *str) { - return eval_string(0, rb_vm_top_self(), rb_str_new2(str), Qnil, "(eval)", 1); + return eval_string(rb_vm_top_self(), 0, rb_str_new2(str), Qnil, "(eval)", 1); } VALUE
participants (1)
-
source_changes@macosforge.org