Revision: 1921 http://trac.macosforge.org/projects/ruby/changeset/1921 Author: lsansonetti@apple.com Date: 2009-06-24 01:12:52 -0700 (Wed, 24 Jun 2009) Log Message: ----------- 1st design changes for multithreaded MacRuby Modified Paths: -------------- MacRuby/branches/experimental/bridgesupport.cpp MacRuby/branches/experimental/compiler.cpp MacRuby/branches/experimental/inits.c MacRuby/branches/experimental/sprintf.cpp MacRuby/branches/experimental/thread.c MacRuby/branches/experimental/vm.cpp MacRuby/branches/experimental/vm.h Modified: MacRuby/branches/experimental/bridgesupport.cpp =================================================================== --- MacRuby/branches/experimental/bridgesupport.cpp 2009-06-23 20:47:10 UTC (rev 1920) +++ MacRuby/branches/experimental/bridgesupport.cpp 2009-06-24 08:12:52 UTC (rev 1921) @@ -48,25 +48,20 @@ VALUE rb_vm_resolve_const_value(VALUE v, VALUE klass, ID id) { - void *sym; - bs_element_constant_t *bs_const; - if (v == bs_const_magic_cookie) { - std::map<ID, bs_element_constant_t *>::iterator iter = - GET_VM()->bs_consts.find(id); - if (iter == GET_VM()->bs_consts.end()) { + bs_element_constant_t *bs_const = GET_CORE()->find_bs_const(id); + if (bs_const == NULL) { rb_bug("unresolved BridgeSupport constant `%s'", rb_id2name(id)); } - bs_const = iter->second; - sym = dlsym(RTLD_DEFAULT, bs_const->name); + void *sym = dlsym(RTLD_DEFAULT, bs_const->name); if (sym == NULL) { rb_bug("cannot locate symbol for BridgeSupport constant `%s'", bs_const->name); } - void *convertor = GET_VM()->gen_to_rval_convertor(bs_const->type); + void *convertor = GET_CORE()->gen_to_rval_convertor(bs_const->type); v = ((VALUE (*)(void *))convertor)(sym); CFMutableDictionaryRef iv_dict = rb_class_ivar_dict(rb_cObject); @@ -202,7 +197,7 @@ params.push_back(new BitCastInst(fval, PtrTy, "", bb)); params.push_back(ConstantInt::get(Type::Int8Ty, 0)); params.push_back(ConstantInt::get(IntTy, - GET_VM()->get_sizeof(llvm_type))); + GET_CORE()->get_sizeof(llvm_type))); params.push_back(ConstantInt::get(Type::Int32Ty, 0)); CallInst::Create(memset_func, params.begin(), params.end(), "", bb); @@ -248,7 +243,7 @@ { VALUE type = rb_ivar_get(klass, boxed_ivar_type); assert(type != Qnil); - rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_boxed(RSTRING_PTR(type)); + rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_boxed(RSTRING_PTR(type)); assert(bs_boxed != NULL); if (struct_only) { assert(bs_boxed->is_struct()); @@ -262,7 +257,7 @@ // Generate the real #new method. rb_vm_bs_boxed_t *bs_boxed = locate_bs_boxed(rcv, true); Function *f = RoxorCompiler::shared->compile_bs_struct_new(bs_boxed); - IMP imp = GET_VM()->compile(f); + IMP imp = GET_CORE()->compile(f); // Replace the fake method with the new one in the runtime. rb_objc_define_method(*(VALUE *)rcv, "new", (void *)imp, -1); @@ -297,7 +292,7 @@ // Generate the new setter method. Function *f = RoxorCompiler::shared->compile_bs_struct_writer( bs_boxed, field); - IMP imp = GET_VM()->compile(f); + IMP imp = GET_CORE()->compile(f); // Replace the fake method with the new one in the runtime. buf[s - 1] = '='; @@ -454,15 +449,15 @@ rb_class2name(rcv)); } -static bool -register_bs_boxed(bs_element_type_t type, void *value) +bool +RoxorCore::register_bs_boxed(bs_element_type_t type, void *value) { std::string octype(((bs_element_opaque_t *)value)->type); std::map<std::string, rb_vm_bs_boxed_t *>::iterator iter = - GET_VM()->bs_boxed.find(octype); + bs_boxed.find(octype); - if (iter != GET_VM()->bs_boxed.end()) { + if (iter != bs_boxed.end()) { // A boxed class of this type already exists, so let's create an // alias to it. rb_vm_bs_boxed_t *boxed = iter->second; @@ -524,7 +519,7 @@ // Common methods. rb_objc_define_method(boxed->klass, "==", (void *)rb_vm_boxed_equal, 1); - GET_VM()->bs_boxed[octype] = boxed; + bs_boxed[octype] = boxed; return true; } @@ -579,12 +574,14 @@ rb_vm_pointer_t *ptr = (rb_vm_pointer_t *)xmalloc(sizeof(rb_vm_pointer_t)); GC_WB(&ptr->type, rb_str_new2(type_str)); + RoxorCore *core = GET_CORE(); + ptr->convert_to_rval = - (VALUE (*)(void *))GET_VM()->gen_to_rval_convertor(type_str); + (VALUE (*)(void *))core->gen_to_rval_convertor(type_str); ptr->convert_to_ocval = - (void (*)(VALUE, void *))GET_VM()->gen_to_ocval_convertor(type_str); + (void (*)(VALUE, void *))core->gen_to_ocval_convertor(type_str); - ptr->type_size = GET_VM()->get_sizeof(type_str); + ptr->type_size = core->get_sizeof(type_str); assert(ptr->type_size > 0); GC_WB(&ptr->val, val); @@ -607,7 +604,7 @@ type_str); } p = rb_pointer_new(type_str, - xmalloc(GET_VM()->get_sizeof(type_str) * len)); + xmalloc(GET_CORE()->get_sizeof(type_str) * len)); int i; for (i = 0; i < len; i++) { rb_pointer_aset(p, 0, INT2FIX(i), RARRAY_AT(rval, i)); @@ -615,7 +612,7 @@ } else { p = rb_pointer_new(type_str, - xmalloc(GET_VM()->get_sizeof(type_str))); + xmalloc(GET_CORE()->get_sizeof(type_str))); rb_pointer_aset(p, 0, INT2FIX(0), rval); } @@ -633,7 +630,7 @@ const char *type_str = convert_ffi_type(type, false); return rb_pointer_new(type_str, - xmalloc(GET_VM()->get_sizeof(type_str) * rlen)); + xmalloc(GET_CORE()->get_sizeof(type_str) * rlen)); } void * @@ -702,32 +699,17 @@ return ptr->type; } -static inline void -index_bs_class_methods(const char *name, - std::map<std::string, std::map<SEL, bs_element_method_t *> *> &map, - bs_element_method_t *methods, - unsigned method_count) +bs_element_constant_t * +RoxorCore::find_bs_const(ID name) { - std::map<std::string, std::map<SEL, bs_element_method_t *> *>::iterator - iter = map.find(name); + std::map<ID, bs_element_constant_t *>::iterator iter = + bs_consts.find(name); - std::map<SEL, bs_element_method_t *> *methods_map = NULL; - if (iter == map.end()) { - methods_map = new std::map<SEL, bs_element_method_t *>(); - map[name] = methods_map; - } - else { - methods_map = iter->second; - } + return iter == bs_consts.end() ? NULL : iter->second; +} - for (unsigned i = 0; i < method_count; i++) { - bs_element_method_t *m = &methods[i]; - methods_map->insert(std::make_pair(m->name, m)); - } -} - bs_element_method_t * -RoxorVM::find_bs_method(Class klass, SEL sel) +RoxorCore::find_bs_method(Class klass, SEL sel) { std::map<std::string, std::map<SEL, bs_element_method_t *> *> &map = class_isMetaClass(klass) ? bs_classes_class_methods @@ -756,7 +738,7 @@ } rb_vm_bs_boxed_t * -RoxorVM::find_bs_boxed(std::string type) +RoxorCore::find_bs_boxed(std::string type) { std::map<std::string, rb_vm_bs_boxed_t *>::iterator iter = bs_boxed.find(type); @@ -769,40 +751,99 @@ } rb_vm_bs_boxed_t * -RoxorVM::find_bs_struct(std::string type) +RoxorCore::find_bs_struct(std::string type) { rb_vm_bs_boxed_t *boxed = find_bs_boxed(type); return boxed == NULL ? NULL : boxed->is_struct() ? boxed : NULL; } rb_vm_bs_boxed_t * -RoxorVM::find_bs_opaque(std::string type) +RoxorCore::find_bs_opaque(std::string type) { rb_vm_bs_boxed_t *boxed = find_bs_boxed(type); return boxed == NULL ? NULL : boxed->is_struct() ? NULL : boxed; } -static inline void -register_bs_class(bs_element_class_t *bs_class) +bs_element_cftype_t * +RoxorCore::find_bs_cftype(std::string type) { + std::map<std::string, bs_element_cftype_t *>::iterator iter = + bs_cftypes.find(type); + + return iter == bs_cftypes.end() ? NULL : iter->second; +} + +std::string * +RoxorCore::find_bs_informal_protocol_method(SEL sel, bool class_method) +{ + std::map<SEL, std::string *> &map = class_method + ? bs_informal_protocol_cmethods : bs_informal_protocol_imethods; + + std::map<SEL, std::string *>::iterator iter = map.find(sel); + + return iter == map.end() ? NULL : iter->second; +} + +bs_element_function_t * +RoxorCore::find_bs_function(std::string &name) +{ + std::map<std::string, bs_element_function_t *>::iterator iter = + bs_funcs.find(name); + + return iter == bs_funcs.end() ? NULL : iter->second; +} + +static void +index_bs_class_methods(const char *name, + std::map<std::string, std::map<SEL, bs_element_method_t *> *> &map, + bs_element_method_t *methods, + unsigned method_count) +{ + std::map<std::string, std::map<SEL, bs_element_method_t *> *>::iterator + iter = map.find(name); + + std::map<SEL, bs_element_method_t *> *methods_map = NULL; + if (iter == map.end()) { + methods_map = new std::map<SEL, bs_element_method_t *>(); + map[name] = methods_map; + } + else { + methods_map = iter->second; + } + + for (unsigned i = 0; i < method_count; i++) { + bs_element_method_t *m = &methods[i]; + methods_map->insert(std::make_pair(m->name, m)); + } +} + +void +RoxorCore::register_bs_class(bs_element_class_t *bs_class) +{ if (bs_class->class_methods_count > 0) { index_bs_class_methods(bs_class->name, - GET_VM()->bs_classes_class_methods, + bs_classes_class_methods, bs_class->class_methods, bs_class->class_methods_count); } if (bs_class->instance_methods_count > 0) { index_bs_class_methods(bs_class->name, - GET_VM()->bs_classes_instance_methods, + bs_classes_instance_methods, bs_class->instance_methods, bs_class->instance_methods_count); } } static void -bs_parse_cb(bs_parser_t *parser, const char *path, bs_element_type_t type, - void *value, void *ctx) +__bs_parse_cb(bs_parser_t *parser, const char *path, bs_element_type_t type, + void *value, void *ctx) { + GET_CORE()->bs_parse_cb(type, value, ctx); +} + +void +RoxorCore::bs_parse_cb(bs_element_type_t type, void *value, void *ctx) +{ bool do_not_free = false; CFMutableDictionaryRef rb_cObject_dict = (CFMutableDictionaryRef)ctx; @@ -833,7 +874,7 @@ if (!CFDictionaryGetValueIfPresent(rb_cObject_dict, (const void *)name, NULL)) { - GET_VM()->bs_consts[name] = bs_const; + bs_consts[name] = bs_const; CFDictionarySetValue(rb_cObject_dict, (const void *)name, (const void *)bs_const_magic_cookie); do_not_free = true; @@ -880,9 +921,9 @@ std::string name(bs_func->name); std::map<std::string, bs_element_function_t *>::iterator iter = - GET_VM()->bs_funcs.find(name); - if (iter == GET_VM()->bs_funcs.end()) { - GET_VM()->bs_funcs[name] = bs_func; + bs_funcs.find(name); + if (iter == bs_funcs.end()) { + bs_funcs[name] = bs_func; do_not_free = true; } else { @@ -940,12 +981,13 @@ bs_element_informal_protocol_method_t *bs_inf_prot_method = (bs_element_informal_protocol_method_t *)value; - std::map<SEL, std::string> &map = + std::map<SEL, std::string *> &map = bs_inf_prot_method->class_method - ? GET_VM()->bs_informal_protocol_cmethods - : GET_VM()->bs_informal_protocol_imethods; + ? bs_informal_protocol_cmethods + : bs_informal_protocol_imethods; - map[bs_inf_prot_method->name] = bs_inf_prot_method->type; + map[bs_inf_prot_method->name] = + new std::string(bs_inf_prot_method->type); break; } @@ -954,9 +996,9 @@ bs_element_cftype_t *bs_cftype = (bs_element_cftype_t *)value; std::map<std::string, bs_element_cftype_t *>::iterator - iter = GET_VM()->bs_cftypes.find(bs_cftype->type); - if (iter == GET_VM()->bs_cftypes.end()) { - GET_VM()->bs_cftypes[bs_cftype->type] = bs_cftype; + iter = bs_cftypes.find(bs_cftype->type); + if (iter == bs_cftypes.end()) { + bs_cftypes[bs_cftype->type] = bs_cftype; do_not_free = true; } else { @@ -975,22 +1017,29 @@ extern "C" void rb_vm_load_bridge_support(const char *path, const char *framework_path, - int options) + int options) { + GET_CORE()->load_bridge_support(path, framework_path, options); +} + +void +RoxorCore::load_bridge_support(const char *path, const char *framework_path, + int options) +{ char *error; bool ok; CFMutableDictionaryRef rb_cObject_dict; - if (GET_VM()->bs_parser == NULL) { - GET_VM()->bs_parser = bs_parser_new(); + if (bs_parser == NULL) { + bs_parser = bs_parser_new(); } rb_cObject_dict = rb_class_ivar_dict(rb_cObject); assert(rb_cObject_dict != NULL); - ok = bs_parser_parse(GET_VM()->bs_parser, path, framework_path, - (bs_parse_options_t)options, - bs_parse_cb, rb_cObject_dict, &error); + ok = bs_parser_parse(bs_parser, path, framework_path, + (bs_parse_options_t)options, + __bs_parse_cb, rb_cObject_dict, &error); if (!ok) { rb_raise(rb_eRuntimeError, "%s", error); } @@ -1221,11 +1270,11 @@ types.append(convert_ffi_type(RARRAY_AT(args, i), true)); } - rb_vm_c_stub_t *stub = (rb_vm_c_stub_t *)GET_VM()->gen_stub(types, argc, + rb_vm_c_stub_t *stub = (rb_vm_c_stub_t *)GET_CORE()->gen_stub(types, argc, false); Function *f = RoxorCompiler::shared->compile_ffi_function((void *)stub, sym, argc); - IMP imp = GET_VM()->compile(f); + IMP imp = GET_CORE()->compile(f); VALUE klass = rb_singleton_class(rcv); rb_objc_define_method(klass, symname, (void *)imp, argc); Modified: MacRuby/branches/experimental/compiler.cpp =================================================================== --- MacRuby/branches/experimental/compiler.cpp 2009-06-23 20:47:10 UTC (rev 1920) +++ MacRuby/branches/experimental/compiler.cpp 2009-06-24 08:12:52 UTC (rev 1921) @@ -385,7 +385,7 @@ std::vector<Value *> params; params.push_back(compile_mcache(selEqq, false)); - GlobalVariable *is_redefined = GET_VM()->redefined_op_gvar(selEqq, true); + GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(selEqq, true); params.push_back(new LoadInst(is_redefined, "", bb)); params.push_back(comparedToVal); params.push_back(splatVal); @@ -432,7 +432,7 @@ Value * RoxorCompiler::compile_mcache(SEL sel, bool super) { - struct mcache *cache = GET_VM()->method_cache_get(sel, super); + struct mcache *cache = GET_CORE()->method_cache_get(sel, super); return compile_const_pointer(cache); } @@ -467,7 +467,7 @@ Value * RoxorCompiler::compile_ccache(ID name) { - struct ccache *cache = GET_VM()->constant_cache_get(name); + struct ccache *cache = GET_CORE()->constant_cache_get(name); return compile_const_pointer(cache); } @@ -566,7 +566,7 @@ // Make sure the function is compiled before use, this way LLVM won't use // a stub. - GET_VM()->compile(new_function); + GET_CORE()->compile(new_function); params.push_back(new BitCastInst(new_function, PtrTy, "", bb)); uint64_t v; @@ -1392,8 +1392,6 @@ 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)); @@ -1735,7 +1733,7 @@ return NULL; } - GlobalVariable *is_redefined = GET_VM()->redefined_op_gvar(sel, true); + GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); Value *leftVal = params[1]; // self Value *rightVal = params.back(); @@ -2088,7 +2086,7 @@ } new_params.push_back(params[0]); // cache - GlobalVariable *is_redefined = GET_VM()->redefined_op_gvar(sel, true); + GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); new_params.push_back(new LoadInst(is_redefined, "", bb)); return compile_protected_call(opt_func, new_params); @@ -2109,7 +2107,7 @@ } SEL new_sel = mid_to_sel(SYM2ID(sym), argc - 1); - GlobalVariable *is_redefined = GET_VM()->redefined_op_gvar(sel, true); + GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); Value *is_redefined_val = new LoadInst(is_redefined, "", bb); Value *isOpRedefined = new ICmpInst(ICmpInst::ICMP_EQ, @@ -4783,8 +4781,7 @@ type = SkipTypeModifiers(type); - if (*type == _C_PTR - && GET_VM()->bs_cftypes.find(type) != GET_VM()->bs_cftypes.end()) { + if (*type == _C_PTR && GET_CORE()->find_bs_cftype(type) != NULL) { type = "@"; } @@ -4856,7 +4853,7 @@ case _C_STRUCT_B: { - rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_struct(type); + rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_struct(type); if (bs_boxed != NULL) { Value *fields = new AllocaInst(RubyObjTy, ConstantInt::get(Type::Int32Ty, @@ -4887,7 +4884,7 @@ fslot); } - if (GET_VM()->is_large_struct_type(bs_boxed->type)) { + if (GET_CORE()->is_large_struct_type(bs_boxed->type)) { // If this structure is too large, we need to pass its // address and not its value, to conform to the ABI. return slot; @@ -4899,7 +4896,7 @@ case _C_PTR: { - rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_opaque(type); + rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_opaque(type); if (bs_boxed != NULL) { return compile_get_opaque_data(val, bs_boxed, slot); } @@ -5075,8 +5072,7 @@ type = SkipTypeModifiers(type); - if (*type == _C_PTR - && GET_VM()->bs_cftypes.find(type) != GET_VM()->bs_cftypes.end()) { + if (*type == _C_PTR && GET_CORE()->find_bs_cftype(type) != NULL) { type = "@"; } @@ -5147,7 +5143,7 @@ case _C_STRUCT_B: { - rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_struct(type); + rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_struct(type); if (bs_boxed != NULL) { std::vector<Value *> params; @@ -5170,7 +5166,7 @@ case _C_PTR: { - rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_opaque(type); + rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_opaque(type); if (bs_boxed != NULL) { Value *klass = ConstantInt::get(RubyObjTy, bs_boxed->klass); return compile_new_opaque(klass, val); @@ -5245,7 +5241,7 @@ return Type::Int64Ty; case _C_STRUCT_B: - rb_vm_bs_boxed_t *bs_boxed = GET_VM()->find_bs_struct(type); + rb_vm_bs_boxed_t *bs_boxed = GET_CORE()->find_bs_struct(type); if (bs_boxed != NULL) { if (bs_boxed->type == NULL) { std::vector<const Type *> s_types; @@ -5306,7 +5302,7 @@ const Type *ret_type = convert_type(buf); Value *sret = NULL; - if (GET_VM()->is_large_struct_type(ret_type)) { + if (GET_CORE()->is_large_struct_type(ret_type)) { // We are returning a large struct, we need to pass a pointer as the // first argument to the structure data and return void to conform to // the ABI. @@ -5346,7 +5342,7 @@ const Type *llvm_type = convert_type(buf); const Type *f_type = llvm_type; - if (GET_VM()->is_large_struct_type(llvm_type)) { + if (GET_CORE()->is_large_struct_type(llvm_type)) { // We are passing a large struct, we need to mark this argument // with the byval attribute and configure the internal stub // call to pass a pointer to the structure, to conform to the @@ -5463,7 +5459,7 @@ std::string ret_type(buf); const Type *f_ret_type = convert_type(buf); const Type *f_sret_type = NULL; - if (GET_VM()->is_large_struct_type(f_ret_type)) { + if (GET_CORE()->is_large_struct_type(f_ret_type)) { // We are returning a large struct, we need to pass a pointer as the // first argument to the structure data and return void to conform to // the ABI. @@ -5486,7 +5482,7 @@ for (unsigned int i = 0; i < ruby_func->arg_size() - 2; i++) { p = GetFirstType(p, buf, sizeof buf); const Type *t = convert_type(buf); - if (GET_VM()->is_large_struct_type(t)) { + if (GET_CORE()->is_large_struct_type(t)) { // We are passing a large struct, we need to mark this argument // with the byval attribute and configure the internal stub // call to pass a pointer to the structure, to conform to the ABI. Modified: MacRuby/branches/experimental/inits.c =================================================================== --- MacRuby/branches/experimental/inits.c 2009-06-23 20:47:10 UTC (rev 1920) +++ MacRuby/branches/experimental/inits.c 2009-06-24 08:12:52 UTC (rev 1921) @@ -57,6 +57,7 @@ void Init_ObjC(void); void Init_BridgeSupport(void); void Init_FFI(void); +void Init_PostVM(void); void rb_call_inits() @@ -107,4 +108,5 @@ Init_ObjC(); Init_BridgeSupport(); Init_FFI(); + Init_PostVM(); } Modified: MacRuby/branches/experimental/sprintf.cpp =================================================================== --- MacRuby/branches/experimental/sprintf.cpp 2009-06-23 20:47:10 UTC (rev 1920) +++ MacRuby/branches/experimental/sprintf.cpp 2009-06-24 08:12:52 UTC (rev 1921) @@ -488,7 +488,7 @@ stub_args[3 + i] = argv[i]; } - rb_vm_c_stub_t *stub = (rb_vm_c_stub_t *)GET_VM()->gen_stub(types, + rb_vm_c_stub_t *stub = (rb_vm_c_stub_t *)GET_CORE()->gen_stub(types, 3, false); VALUE str = (*stub)((IMP)&CFStringCreateWithFormat, argc + 3, stub_args); Modified: MacRuby/branches/experimental/thread.c =================================================================== --- MacRuby/branches/experimental/thread.c 2009-06-23 20:47:10 UTC (rev 1920) +++ MacRuby/branches/experimental/thread.c 2009-06-24 08:12:52 UTC (rev 1921) @@ -7,23 +7,6 @@ VALUE rb_cThread; VALUE rb_cMutex; -typedef struct rb_vm_thread { - pthread_t thread; - rb_vm_block_t *body; - int argc; - const VALUE *argv; -} rb_vm_thread_t; - -#define GetThreadPtr(obj) ((rb_vm_thread_t *)DATA_PTR(obj)) - -static void * -rb_vm_thread_run(rb_vm_thread_t *t) -{ - rb_objc_gc_register_thread(); - rb_vm_block_eval(t->body, t->argc, t->argv); - return NULL; -} - #if 0 static VALUE thread_s_new(int argc, VALUE *argv, VALUE klass) @@ -79,8 +62,14 @@ } } + t->vm = rb_vm_create_vm(); + + // Retain the Thread object to avoid a potential GC, the corresponding + // release is done in rb_vm_thread_run(). + rb_objc_retain((void *)thread); + if (pthread_create(&t->thread, NULL, (void *(*)(void *))rb_vm_thread_run, - t) != 0) { + (void *)thread) != 0) { rb_sys_fail("pthread_create() failed"); } @@ -405,10 +394,9 @@ */ VALUE -rb_thread_list(void) +rb_thread_list(VALUE rcv, SEL sel) { - // TODO - return Qnil; + return rb_vm_threads(); } /* @@ -421,17 +409,15 @@ */ static VALUE -thread_s_current(VALUE klass) +thread_s_current(VALUE klass, SEL sel) { - // TODO - return Qnil; + return rb_vm_current_thread(); } static VALUE -rb_thread_s_main(VALUE klass) +rb_thread_s_main(VALUE klass, SEL sel) { - // TODO - return Qnil; + return rb_vm_main_thread(); } /* @@ -1266,13 +1252,13 @@ //rb_define_singleton_method(rb_cThread, "new", thread_s_new, -1); rb_define_singleton_method(rb_cThread, "start", thread_start, -2); rb_define_singleton_method(rb_cThread, "fork", thread_start, -2); - rb_define_singleton_method(rb_cThread, "main", rb_thread_s_main, 0); + rb_objc_define_method(*(VALUE *)rb_cThread, "main", rb_thread_s_main, 0); rb_objc_define_method(*(VALUE *)rb_cThread, "current", thread_s_current, 0); rb_define_singleton_method(rb_cThread, "stop", rb_thread_stop, 0); rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1); rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0); rb_define_singleton_method(rb_cThread, "pass", thread_s_pass, 0); - rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0); + rb_objc_define_method(*(VALUE *)rb_cThread, "list", rb_thread_list, 0); rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0); rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1); Modified: MacRuby/branches/experimental/vm.cpp =================================================================== --- MacRuby/branches/experimental/vm.cpp 2009-06-23 20:47:10 UTC (rev 1920) +++ MacRuby/branches/experimental/vm.cpp 2009-06-24 08:12:52 UTC (rev 1921) @@ -52,7 +52,9 @@ #define force_inline __attribute__((always_inline)) -RoxorVM *RoxorVM::current = NULL; +RoxorCore *RoxorCore::shared = NULL; +RoxorVM *RoxorVM::main = NULL; +pthread_key_t RoxorVM::vm_thread_key; VALUE rb_cTopLevel = 0; @@ -81,10 +83,10 @@ } struct RoxorFunction *find_function(unsigned char *addr) { - // TODO optimize me! if (functions.empty()) { return NULL; } + // TODO optimize me! RoxorFunction *front = functions.front(); RoxorFunction *back = functions.back(); if (addr < front->start || addr > back->end) { @@ -131,18 +133,18 @@ } unsigned char *startFunctionBody(const Function *F, - uintptr_t &ActualSize) { + uintptr_t &ActualSize) { return mm->startFunctionBody(F, ActualSize); } unsigned char *allocateStub(const GlobalValue* F, - unsigned StubSize, - unsigned Alignment) { + unsigned StubSize, + unsigned Alignment) { return mm->allocateStub(F, StubSize, Alignment); } void endFunctionBody(const Function *F, unsigned char *FunctionStart, - unsigned char *FunctionEnd) { + unsigned char *FunctionEnd) { mm->endFunctionBody(F, FunctionStart, FunctionEnd); functions.push_back(new RoxorFunction(const_cast<Function *>(F), FunctionStart, FunctionEnd)); @@ -153,13 +155,12 @@ } unsigned char* startExceptionTable(const Function* F, - uintptr_t &ActualSize) { + uintptr_t &ActualSize) { return mm->startExceptionTable(F, ActualSize); } void endExceptionTable(const Function *F, unsigned char *TableStart, - unsigned char *TableEnd, - unsigned char* FrameRegister) { + unsigned char *TableEnd, unsigned char* FrameRegister) { mm->endExceptionTable(F, TableStart, TableEnd, FrameRegister); } }; @@ -181,28 +182,22 @@ extern "C" void *__cxa_allocate_exception(size_t); extern "C" void __cxa_throw(void *, void *, void *); -RoxorVM::RoxorVM(void) +RoxorCore::RoxorCore(void) { running = false; - current_top_object = Qnil; - safe_level = 0; + multithreaded = false; - backref = Qnil; - broken_with = Qundef; - last_status = Qnil; - errinfo = Qnil; + assert(pthread_mutex_init(&gl, 0) == 0); - parse_in_eval = false; - -#if ROXOR_VM_DEBUG - functions_compiled = 0; -#endif - load_path = rb_ary_new(); rb_objc_retain((void *)load_path); + loaded_features = rb_ary_new(); rb_objc_retain((void *)loaded_features); + threads = rb_ary_new(); + rb_objc_retain((void *)threads); + bs_parser = NULL; emp = new ExistingModuleProvider(RoxorCompiler::module); @@ -228,8 +223,24 @@ iee = ExecutionEngine::create(emp, true); assert(iee != NULL); + +#if ROXOR_VM_DEBUG + functions_compiled = 0; +#endif } +RoxorVM::RoxorVM(void) +{ + current_top_object = Qnil; + current_class = NULL; + safe_level = 0; + backref = Qnil; + broken_with = Qundef; + last_status = Qnil; + errinfo = Qnil; + parse_in_eval = false; +} + static void append_ptr_address(std::string &s, void *ptr) { @@ -276,13 +287,13 @@ } inline void -RoxorVM::optimize(Function *func) +RoxorCore::optimize(Function *func) { fpm->run(*func); } IMP -RoxorVM::compile(Function *func) +RoxorCore::compile(Function *func) { std::map<Function *, IMP>::iterator iter = JITcache.find(func); if (iter != JITcache.end()) { @@ -326,17 +337,17 @@ } VALUE -RoxorVM::interpret(Function *func) +RoxorCore::interpret(Function *func) { std::vector<GenericValue> args; - args.push_back(PTOGV((void *)GET_VM()->current_top_object)); + args.push_back(PTOGV((void *)GET_VM()->get_current_top_object())); args.push_back(PTOGV(NULL)); return (VALUE)iee->runFunction(func, args).IntVal.getZExtValue(); } bool -RoxorVM::symbolize_call_address(void *addr, void **startp, unsigned long *ln, - char *name, size_t name_len) +RoxorCore::symbolize_call_address(void *addr, void **startp, unsigned long *ln, + char *name, size_t name_len) { void *start = NULL; @@ -381,7 +392,7 @@ } struct ccache * -RoxorVM::constant_cache_get(ID path) +RoxorCore::constant_cache_get(ID path) { std::map<ID, struct ccache *>::iterator iter = ccache.find(path); if (iter == ccache.end()) { @@ -398,12 +409,12 @@ void * rb_vm_get_constant_cache(const char *name) { - return GET_VM()->constant_cache_get(rb_intern(name)); + return GET_CORE()->constant_cache_get(rb_intern(name)); } struct mcache * -RoxorVM::method_cache_get(SEL sel, bool super) +RoxorCore::method_cache_get(SEL sel, bool super) { if (super) { struct mcache *cache = (struct mcache *)malloc(sizeof(struct mcache)); @@ -425,11 +436,11 @@ void * rb_vm_get_method_cache(SEL sel) { - return GET_VM()->method_cache_get(sel, false); + return GET_CORE()->method_cache_get(sel, false); } inline rb_vm_method_node_t * -RoxorVM::method_node_get(IMP imp) +RoxorCore::method_node_get(IMP imp) { std::map<IMP, rb_vm_method_node_t *>::iterator iter = ruby_imps.find(imp); if (iter == ruby_imps.end()) { @@ -442,30 +453,30 @@ rb_vm_method_node_t * rb_vm_get_method_node(IMP imp) { - return GET_VM()->method_node_get(imp); + return GET_CORE()->method_node_get(imp); } size_t -RoxorVM::get_sizeof(const Type *type) +RoxorCore::get_sizeof(const Type *type) { return ee->getTargetData()->getTypeSizeInBits(type) / 8; } size_t -RoxorVM::get_sizeof(const char *type) +RoxorCore::get_sizeof(const char *type) { return get_sizeof(RoxorCompiler::shared->convert_type(type)); } bool -RoxorVM::is_large_struct_type(const Type *type) +RoxorCore::is_large_struct_type(const Type *type) { return type->getTypeID() == Type::StructTyID && ee->getTargetData()->getTypeSizeInBits(type) > 128; } inline GlobalVariable * -RoxorVM::redefined_op_gvar(SEL sel, bool create) +RoxorCore::redefined_op_gvar(SEL sel, bool create) { std::map <SEL, GlobalVariable *>::iterator iter = redefined_ops_gvars.find(sel); @@ -490,7 +501,7 @@ } inline bool -RoxorVM::should_invalidate_inline_op(SEL sel, Class klass) +RoxorCore::should_invalidate_inline_op(SEL sel, Class klass) { if (sel == selEq || sel == selEqq || sel == selNeq) { return klass == (Class)rb_cFixnum @@ -517,8 +528,8 @@ } rb_vm_method_node_t * -RoxorVM::add_method(Class klass, SEL sel, IMP imp, IMP ruby_imp, - const rb_vm_arity_t &arity, int flags, const char *types) +RoxorCore::add_method(Class klass, SEL sel, IMP imp, IMP ruby_imp, + const rb_vm_arity_t &arity, int flags, const char *types) { #if ROXOR_VM_DEBUG printf("defining %c[%s %s] with imp %p types %s\n", @@ -581,7 +592,7 @@ RCLASS_HAS_ROBJECT_ALLOC)); } - if (is_running()) { + if (get_running()) { // Call method_added: or singleton_method_added:. VALUE sym = ID2SYM(rb_intern(sel_getName(sel))); if (RCLASS_SINGLETON(klass)) { @@ -619,7 +630,7 @@ } void -RoxorVM::const_defined(ID path) +RoxorCore::const_defined(ID path) { // Invalidate constant cache. std::map<ID, struct ccache *>::iterator iter = ccache.find(path); @@ -629,13 +640,13 @@ } inline int -RoxorVM::find_ivar_slot(VALUE klass, ID name, bool create) +RoxorCore::find_ivar_slot(VALUE klass, ID name, bool create) { VALUE k = klass; int slot = 0; while (k != 0) { - std::map <ID, int> *slots = GET_VM()->get_ivar_slots((Class)k); + std::map <ID, int> *slots = get_ivar_slots((Class)k); std::map <ID, int>::iterator iter = slots->find(name); if (iter != slots->end()) { #if ROXOR_VM_DEBUG @@ -662,7 +673,7 @@ } inline bool -RoxorVM::class_can_have_ivar_slots(VALUE klass) +RoxorCore::class_can_have_ivar_slots(VALUE klass) { const long klass_version = RCLASS_VERSION(klass); if ((klass_version & RCLASS_IS_RUBY_CLASS) != RCLASS_IS_RUBY_CLASS @@ -677,25 +688,26 @@ bool rb_vm_running(void) { - return GET_VM()->is_running(); + return GET_CORE()->get_running(); } extern "C" void rb_vm_set_running(bool flag) { - GET_VM()->set_running(flag); + GET_CORE()->set_running(flag); } extern "C" void rb_vm_set_const(VALUE outer, ID id, VALUE obj) { - if (GET_VM()->current_class != NULL) { - outer = (VALUE)GET_VM()->current_class; + Class k = GET_VM()->get_current_class(); + if (k != NULL) { + outer = (VALUE)k; } rb_const_set(outer, id, obj); - GET_VM()->const_defined(id); + GET_CORE()->const_defined(id); } static inline VALUE @@ -730,14 +742,17 @@ if (lexical) { // Let's do a lexical lookup before a hierarchical one, by looking for // the given constant in all modules under the given outer. - struct rb_vm_outer *o = GET_VM()->get_outer((Class)outer); + GET_CORE()->lock(); + struct rb_vm_outer *o = GET_CORE()->get_outer((Class)outer); while (o != NULL && o->klass != (Class)rb_cNSObject) { VALUE val = rb_const_get_direct((VALUE)o->klass, path); if (val != Qundef) { + GET_CORE()->unlock(); return defined ? Qtrue : val; } o = o->outer; } + GET_CORE()->unlock(); } // Nothing was found earlier so here we do a hierarchical lookup. @@ -747,22 +762,25 @@ extern "C" VALUE rb_vm_get_const(VALUE outer, unsigned char lexical_lookup, - struct ccache *cache, ID path) + struct ccache *cache, ID path) { - if (GET_VM()->current_class != NULL && lexical_lookup) { - outer = (VALUE)GET_VM()->current_class; + Class k = GET_VM()->get_current_class(); + if (lexical_lookup && k != NULL) { + outer = (VALUE)k; } assert(cache != NULL); + + VALUE val; if (cache->outer == outer && cache->val != Qundef) { - return cache->val; + val = cache->val; } + else { + val = rb_vm_const_lookup(outer, path, lexical_lookup, false); + cache->outer = outer; + cache->val = val; + } - VALUE val = rb_vm_const_lookup(outer, path, lexical_lookup, false); - - cache->outer = outer; - cache->val = val; - return val; } @@ -770,21 +788,21 @@ void rb_vm_const_is_defined(ID path) { - GET_VM()->const_defined(path); + GET_CORE()->const_defined(path); } extern "C" void rb_vm_set_outer(VALUE klass, VALUE under) { - GET_VM()->set_outer((Class)klass, (Class)under); + GET_CORE()->set_outer((Class)klass, (Class)under); } extern "C" VALUE rb_vm_get_outer(VALUE klass) { - rb_vm_outer_t *o = GET_VM()->get_outer((Class)klass); + rb_vm_outer_t *o = GET_CORE()->get_outer((Class)klass); return o == NULL ? Qundef : (VALUE)o->klass; } @@ -809,8 +827,9 @@ assert(path > 0); check_if_module(outer); - if (GET_VM()->current_class != NULL) { - outer = (VALUE)GET_VM()->current_class; + Class k = GET_VM()->get_current_class(); + if (k != NULL) { + outer = (VALUE)k; } VALUE klass; @@ -916,8 +935,9 @@ VALUE rb_vm_cvar_get(VALUE klass, ID id) { - if (GET_VM()->current_class != NULL) { - klass = (VALUE)GET_VM()->current_class; + Class k = GET_VM()->get_current_class(); + if (k != NULL) { + klass = (VALUE)k; } return rb_cvar_get(klass, id); } @@ -926,8 +946,9 @@ VALUE rb_vm_cvar_set(VALUE klass, ID id, VALUE val) { - if (GET_VM()->current_class != NULL) { - klass = (VALUE)GET_VM()->current_class; + Class k = GET_VM()->get_current_class(); + if (k != NULL) { + klass = (VALUE)k; } rb_cvar_set(klass, id, val); return val; @@ -982,7 +1003,7 @@ IMP imp = method_getImplementation(method); const char *types = method_getTypeEncoding(method); - rb_vm_method_node_t *node = GET_VM()->method_node_get(imp); + rb_vm_method_node_t *node = GET_CORE()->method_node_get(imp); if (node == NULL) { rb_raise(rb_eArgError, "cannot alias non-Ruby method `%s'", sel_getName(method_getName(method))); @@ -999,7 +1020,7 @@ sel = sel_registerName(tmp); } - GET_VM()->add_method(klass, sel, imp, node->ruby_imp, + GET_CORE()->add_method(klass, sel, imp, node->ruby_imp, node->arity, node->flags, types); } @@ -1007,8 +1028,9 @@ void rb_vm_alias(VALUE outer, ID name, ID def) { - if (GET_VM()->current_class != NULL) { - outer = (VALUE)GET_VM()->current_class; + Class k = GET_VM()->get_current_class(); + if (k != NULL) { + outer = (VALUE)k; } rb_frozen_class_p(outer); if (outer == rb_cObject) { @@ -1042,10 +1064,10 @@ void rb_vm_undef(VALUE klass, ID name) { - if (GET_VM()->current_class != NULL) { - klass = (VALUE)GET_VM()->current_class; + Class k = GET_VM()->get_current_class(); + if (k != NULL) { + klass = (VALUE)k; } - rb_undef(klass, name); } @@ -1078,7 +1100,7 @@ case DEFINED_LCONST: { if (rb_vm_const_lookup(what2, (ID)what, - type == DEFINED_LCONST, true)) { + type == DEFINED_LCONST, true)) { str = "constant"; } } @@ -1123,9 +1145,8 @@ assert(slot_cache != NULL); assert(*slot_cache == -1); - RoxorVM *vm = GET_VM(); - if (vm->class_can_have_ivar_slots(klass)) { - *slot_cache = vm->find_ivar_slot(klass, name, true); + if (GET_CORE()->class_can_have_ivar_slots(klass)) { + *slot_cache = GET_CORE()->find_ivar_slot(klass, name, true); } } @@ -1133,9 +1154,8 @@ int rb_vm_find_class_ivar_slot(VALUE klass, ID name) { - RoxorVM *vm = GET_VM(); - if (vm->class_can_have_ivar_slots(klass)) { - return vm->find_ivar_slot(klass, name, false); + if (GET_CORE()->class_can_have_ivar_slots(klass)) { + return GET_CORE()->find_ivar_slot(klass, name, false); } return -1; } @@ -1144,18 +1164,16 @@ resolve_method_type(char *buf, const size_t buflen, Class klass, Method m, SEL sel, const unsigned int oc_arity) { - bs_element_method_t *bs_method = GET_VM()->find_bs_method(klass, sel); + bs_element_method_t *bs_method = GET_CORE()->find_bs_method(klass, sel); if (m == NULL || !rb_objc_get_types(Qnil, klass, sel, m, bs_method, buf, buflen)) { - std::map<SEL, std::string> &map = class_isMetaClass(klass) - ? GET_VM()->bs_informal_protocol_cmethods - : GET_VM()->bs_informal_protocol_imethods; - - std::map<SEL, std::string>::iterator iter = map.find(sel); - if (iter != map.end()) { - strncpy(buf, iter->second.c_str(), buflen); + std::string *informal_type = + GET_CORE()->find_bs_informal_protocol_method(sel, + class_isMetaClass(klass)); + if (informal_type != NULL) { + strncpy(buf, informal_type->c_str(), buflen); } else { assert(oc_arity < buflen); @@ -1178,40 +1196,39 @@ } } -static void -resolve_method(Class klass, SEL sel, Function *func, NODE *node, IMP imp, - Method m) +rb_vm_method_node_t * +RoxorCore::resolve_method(Class klass, SEL sel, Function *func, NODE *node, + IMP imp, Method m) { const int oc_arity = rb_vm_node_arity(node).real + 3; char types[100]; resolve_method_type(types, sizeof types, klass, m, sel, oc_arity); - std::map<Function *, IMP>::iterator iter = - GET_VM()->objc_to_ruby_stubs.find(func); + std::map<Function *, IMP>::iterator iter = objc_to_ruby_stubs.find(func); IMP objc_imp; - if (iter == GET_VM()->objc_to_ruby_stubs.end()) { + if (iter == objc_to_ruby_stubs.end()) { Function *objc_func = RoxorCompiler::shared->compile_objc_stub(func, types); - objc_imp = GET_VM()->compile(objc_func); - GET_VM()->objc_to_ruby_stubs[func] = objc_imp; + objc_imp = compile(objc_func); + objc_to_ruby_stubs[func] = objc_imp; } else { objc_imp = iter->second; } if (imp == NULL) { - imp = GET_VM()->compile(func); + imp = compile(func); } const rb_vm_arity_t arity = rb_vm_node_arity(node); const int flags = rb_vm_node_flags(node); - GET_VM()->add_method(klass, sel, objc_imp, imp, arity, flags, types); + return add_method(klass, sel, objc_imp, imp, arity, flags, types); } -static bool -__rb_vm_resolve_method(std::map<Class, rb_vm_method_source_t *> *map, - Class klass, SEL sel) +bool +RoxorCore::resolve_methods(std::map<Class, rb_vm_method_source_t *> *map, + Class klass, SEL sel) { bool did_something = false; std::map<Class, rb_vm_method_source_t *>::iterator iter = map->begin(); @@ -1239,10 +1256,14 @@ static bool rb_vm_resolve_method(Class klass, SEL sel) { - if (!GET_VM()->is_running()) { + if (!GET_CORE()->get_running()) { return false; } + GET_CORE()->lock(); + + bool status = false; + #if ROXOR_VM_DEBUG printf("resolving %c[%s %s]\n", class_isMetaClass(klass) ? '+' : '-', @@ -1251,9 +1272,9 @@ #endif std::map<Class, rb_vm_method_source_t *> *map = - GET_VM()->method_sources_for_sel(sel, false); + GET_CORE()->method_sources_for_sel(sel, false); if (map == NULL) { - return false; + goto bails; } // Find the class where the method should be defined. @@ -1261,21 +1282,57 @@ klass = class_getSuperclass(klass); } if (klass == NULL) { - return false; + goto bails; } // Now let's resolve all methods of the given name on the given class // and superclasses. - return __rb_vm_resolve_method(map, klass, sel); + status = GET_CORE()->resolve_methods(map, klass, sel); + +bails: + GET_CORE()->unlock(); + return status; } +void +RoxorCore::prepare_method(Class klass, SEL sel, Function *func, NODE *node) +{ +#if ROXOR_VM_DEBUG + printf("preparing %c[%s %s] with LLVM func %p node %p\n", + class_isMetaClass(klass) ? '+' : '-', + class_getName(klass), + sel_getName(sel), + func, + node); +#endif + + std::map<Class, rb_vm_method_source_t *> *map = + method_sources_for_sel(sel, true); + + std::map<Class, rb_vm_method_source_t *>::iterator iter = map->find(klass); + + rb_vm_method_source_t *m = NULL; + if (iter == map->end()) { + m = (rb_vm_method_source_t *)malloc(sizeof(rb_vm_method_source_t)); + map->insert(std::make_pair(klass, m)); + method_source_sels.insert(std::make_pair(klass, sel)); + } + else { + m = iter->second; + } + + m->func = func; + m->node = node; +} + extern "C" void rb_vm_prepare_method(Class klass, SEL sel, Function *func, NODE *node) { - if (GET_VM()->current_class != NULL) { + Class k = GET_VM()->get_current_class(); + if (k != NULL) { const bool meta = class_isMetaClass(klass); - klass = GET_VM()->current_class; + klass = k; if (meta) { klass = *(Class *)klass; } @@ -1295,39 +1352,13 @@ if (m != NULL) { // The method already exists - we need to JIT it. if (imp == NULL) { - imp = GET_VM()->compile(func); + imp = GET_CORE()->compile(func); } - resolve_method(klass, sel, func, node, imp, m); + GET_CORE()->resolve_method(klass, sel, func, node, imp, m); } else { // Let's keep the method and JIT it later on demand. -#if ROXOR_VM_DEBUG - printf("preparing %c[%s %s] with LLVM func %p node %p\n", - class_isMetaClass(klass) ? '+' : '-', - class_getName(klass), - sel_getName(sel), - func, - node); -#endif - - std::map<Class, rb_vm_method_source_t *> *map = - GET_VM()->method_sources_for_sel(sel, true); - - std::map<Class, rb_vm_method_source_t *>::iterator iter = - map->find(klass); - - rb_vm_method_source_t *m = NULL; - if (iter == map->end()) { - m = (rb_vm_method_source_t *)malloc(sizeof(rb_vm_method_source_t)); - map->insert(std::make_pair(klass, m)); - GET_VM()->method_source_sels.insert(std::make_pair(klass, sel)); - } - else { - m = iter->second; - } - - m->func = func; - m->node = node; + GET_CORE()->prepare_method(klass, sel, func, node); } if (!redefined) { @@ -1379,7 +1410,7 @@ #define VISI_CHECK(x,f) (VISI(x) == (f)) static void -push_method(VALUE ary, VALUE mod, SEL sel, rb_vm_method_node_t *node, +push_method(VALUE ary, SEL sel, rb_vm_method_node_t *node, int (*filter) (VALUE, ID, VALUE)) { if (sel == sel_ignored) { @@ -1413,15 +1444,14 @@ } } -extern "C" void -rb_vm_push_methods(VALUE ary, VALUE mod, bool include_objc_methods, - int (*filter) (VALUE, ID, VALUE)) +RoxorCore::get_methods(VALUE ary, Class klass, bool include_objc_methods, + int (*filter) (VALUE, ID, VALUE)) { // TODO take into account undefined methods unsigned int count; - Method *methods = class_copyMethodList((Class)mod, &count); + Method *methods = class_copyMethodList(klass, &count); if (methods != NULL) { for (unsigned int i = 0; i < count; i++) { Method m = methods[i]; @@ -1431,24 +1461,24 @@ if (node == NULL && !include_objc_methods) { continue; } - push_method(ary, mod, sel, node, filter); + push_method(ary, sel, node, filter); } free(methods); } - Class k = (Class)mod; + Class k = klass; do { std::multimap<Class, SEL>::iterator iter = - GET_VM()->method_source_sels.find(k); + method_source_sels.find(k); - if (iter != GET_VM()->method_source_sels.end()) { + if (iter != method_source_sels.end()) { std::multimap<Class, SEL>::iterator last = - GET_VM()->method_source_sels.upper_bound(k); + method_source_sels.upper_bound(k); for (; iter != last; ++iter) { SEL sel = iter->second; // TODO retrieve method NODE* - push_method(ary, mod, sel, NULL, filter); + push_method(ary, sel, NULL, filter); } } @@ -1458,6 +1488,14 @@ } extern "C" +void +rb_vm_push_methods(VALUE ary, VALUE mod, bool include_objc_methods, + int (*filter) (VALUE, ID, VALUE)) +{ + GET_CORE()->get_methods(ary, (Class)mod, include_objc_methods, filter); +} + +extern "C" GenericValue lle_X_rb_vm_prepare_method(const FunctionType *FT, const std::vector<GenericValue> &Args) @@ -1479,6 +1517,12 @@ void rb_vm_copy_methods(Class from_class, Class to_class) { + GET_CORE()->copy_methods(from_class, to_class); +} + +void +RoxorCore::copy_methods(Class from_class, Class to_class) +{ Method *methods; unsigned int i, methods_count; @@ -1503,10 +1547,10 @@ method_getTypeEncoding(method)); std::map<Class, rb_vm_method_source_t *> *map = - GET_VM()->method_sources_for_sel(sel, false); + method_sources_for_sel(sel, false); if (map != NULL) { // There might be some non-JIT'ed yet methods on subclasses. - __rb_vm_resolve_method(map, to_class, sel); + resolve_methods(map, to_class, sel); } } free(methods); @@ -1514,18 +1558,18 @@ // Copy methods that have not been JIT'ed yet. std::multimap<Class, SEL>::iterator iter = - GET_VM()->method_source_sels.find(from_class); + method_source_sels.find(from_class); - if (iter != GET_VM()->method_source_sels.end()) { + if (iter != method_source_sels.end()) { std::multimap<Class, SEL>::iterator last = - GET_VM()->method_source_sels.upper_bound(from_class); + method_source_sels.upper_bound(from_class); std::vector<SEL> sels_to_add; for (; iter != last; ++iter) { SEL sel = iter->second; std::map<Class, rb_vm_method_source_t *> *dict = - GET_VM()->method_sources_for_sel(sel, false); + method_sources_for_sel(sel, false); if (dict == NULL) { continue; } @@ -1555,7 +1599,7 @@ for (std::vector<SEL>::iterator i = sels_to_add.begin(); i != sels_to_add.end(); ++i) { - GET_VM()->method_source_sels.insert(std::make_pair(to_class, *i)); + method_source_sels.insert(std::make_pair(to_class, *i)); } } } @@ -1597,7 +1641,7 @@ *pimp = imp; } if (pnode != NULL) { - *pnode = GET_VM()->method_node_get(imp); + *pnode = GET_CORE()->method_node_get(imp); } return true; } @@ -1653,7 +1697,7 @@ char types[100]; resolve_method_type(types, sizeof types, klass, method, sel, oc_arity); - GET_VM()->add_method(klass, sel, objc_imp, ruby_imp, arity, flags, types); + GET_CORE()->add_method(klass, sel, objc_imp, ruby_imp, arity, flags, types); if (!redefined) { if (!genuine_selector && arity.max != arity.min) { @@ -1707,7 +1751,7 @@ assert(block != NULL); Function *func = RoxorCompiler::shared->compile_block_caller(block); - IMP imp = GET_VM()->compile(func); + IMP imp = GET_CORE()->compile(func); NODE *body = rb_vm_cfunc_node_from_imp(klass, -1, imp, 0); rb_objc_retain(body); rb_objc_retain(block); @@ -1933,9 +1977,9 @@ std::vector<void *> callstack_funcs; for (int i = 0; i < callstack_n; i++) { void *start = NULL; - if (GET_VM()->symbolize_call_address(callstack[i], + if (GET_CORE()->symbolize_call_address(callstack[i], &start, NULL, NULL, 0)) { - rb_vm_method_node_t *node = GET_VM()->method_node_get((IMP)start); + rb_vm_method_node_t *node = GET_CORE()->method_node_get((IMP)start); if (node != NULL && node->ruby_imp == start) { start = (void *)node->objc_imp; } @@ -2002,7 +2046,8 @@ rb_raise(rb_eArgError, "no id given"); } - const unsigned char last_call_status = GET_VM()->method_missing_reason; + const unsigned char last_call_status = + GET_VM()->get_method_missing_reason(); const char *format = NULL; VALUE exc = rb_eNoMethodError; @@ -2050,7 +2095,7 @@ method_missing(VALUE obj, SEL sel, int argc, const VALUE *argv, unsigned char call_status) { - GET_VM()->method_missing_reason = call_status; + GET_VM()->set_method_missing_reason(call_status); if (sel == selMethodMissing) { rb_vm_method_missing(obj, argc, argv); @@ -2074,7 +2119,7 @@ } inline void * -RoxorVM::gen_stub(std::string types, int argc, bool is_objc) +RoxorCore::gen_stub(std::string types, int argc, bool is_objc) { std::map<std::string, void *> &stubs = is_objc ? objc_stubs : c_stubs; std::map<std::string, void *>::iterator iter = stubs.find(types); @@ -2091,7 +2136,7 @@ } void * -RoxorVM::gen_to_rval_convertor(std::string type) +RoxorCore::gen_to_rval_convertor(std::string type) { std::map<std::string, void *>::iterator iter = to_rval_convertors.find(type); @@ -2108,7 +2153,7 @@ } void * -RoxorVM::gen_to_ocval_convertor(std::string type) +RoxorCore::gen_to_ocval_convertor(std::string type) { std::map<std::string, void *>::iterator iter = to_ocval_convertors.find(type); @@ -2227,7 +2272,7 @@ cache->flag = MCACHE_OCALL; ocache.klass = klass; ocache.imp = imp; - ocache.bs_method = GET_VM()->find_bs_method(klass, sel); + ocache.bs_method = GET_CORE()->find_bs_method(klass, sel); char types[200]; if (!rb_objc_get_types(self, klass, sel, method, ocache.bs_method, @@ -2250,14 +2295,16 @@ argc = real_argc; } } - ocache.stub = (rb_vm_objc_stub_t *)GET_VM()->gen_stub(types, + GET_CORE()->lock(); + ocache.stub = (rb_vm_objc_stub_t *)GET_CORE()->gen_stub(types, argc, true); + GET_CORE()->unlock(); } static force_inline VALUE -__rb_vm_dispatch(struct mcache *cache, VALUE self, Class klass, SEL sel, - rb_vm_block_t *block, unsigned char opt, int argc, - const VALUE *argv) +__rb_vm_dispatch(RoxorVM *vm, struct mcache *cache, VALUE self, Class klass, + SEL sel, rb_vm_block_t *block, unsigned char opt, int argc, + const VALUE *argv) { assert(cache != NULL); @@ -2289,7 +2336,7 @@ goto call_method_missing; } - rb_vm_method_node_t *node = GET_VM()->method_node_get(imp); + rb_vm_method_node_t *node = GET_CORE()->method_node_get(imp); if (node != NULL) { // ruby call @@ -2369,10 +2416,8 @@ selname_len--; } std::string name(selname, selname_len); - std::map<std::string, bs_element_function_t *>::iterator iter = - GET_VM()->bs_funcs.find(name); - if (iter != GET_VM()->bs_funcs.end()) { - bs_element_function_t *bs_func = iter->second; + bs_element_function_t *bs_func = GET_CORE()->find_bs_function(name); + if (bs_func != NULL) { std::string types; vm_gen_bs_func_types(bs_func, types); @@ -2380,7 +2425,7 @@ fcache.bs_function = bs_func; fcache.imp = (IMP)dlsym(RTLD_DEFAULT, bs_func->name); assert(fcache.imp != NULL); - fcache.stub = (rb_vm_c_stub_t *)GET_VM()->gen_stub(types, + fcache.stub = (rb_vm_c_stub_t *)GET_CORE()->gen_stub(types, argc, false); } else { @@ -2410,9 +2455,9 @@ cached ? "true" : "false"); #endif - bool block_already_current = GET_VM()->is_block_current(block); + bool block_already_current = vm->is_block_current(block); if (!block_already_current) { - GET_VM()->add_current_block(block); + vm->add_current_block(block); } VALUE ret = Qnil; @@ -2421,12 +2466,12 @@ } catch (...) { if (!block_already_current) { - GET_VM()->pop_current_block(); + vm->pop_current_block(); } throw; } if (!block_already_current) { - GET_VM()->pop_current_block(); + vm->pop_current_block(); } return ret; } @@ -2438,16 +2483,16 @@ if (block != NULL) { if (self == rb_cNSMutableHash && sel == selNew) { // Because Hash.new can accept a block. - GET_VM()->add_current_block(block); + vm->add_current_block(block); VALUE h = Qnil; try { h = rb_hash_new2(argc, argv); } catch (...) { - GET_VM()->pop_current_block(); + vm->pop_current_block(); throw; } - GET_VM()->pop_current_block(); + vm->pop_current_block(); return h; } rb_warn("passing a block to an Objective-C method - " \ @@ -2549,8 +2594,8 @@ if (new_sel != 0) { Method m = class_getInstanceMethod(klass, new_sel); if (m != NULL - && GET_VM()->method_node_get(method_getImplementation(m)) - != NULL) { + && GET_CORE()->method_node_get(method_getImplementation(m)) + != NULL) { rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, argc_expected); } @@ -2614,14 +2659,13 @@ va_end(ar); } - VALUE retval = __rb_vm_dispatch(cache, self, NULL, sel, block, opt, argc, - argv); + RoxorVM *vm = GET_VM(); - if (!GET_VM()->bindings.empty()) { - rb_objc_release(GET_VM()->bindings.back()); - GET_VM()->bindings.pop_back(); - } + VALUE retval = __rb_vm_dispatch(vm, cache, self, NULL, sel, block, opt, + argc, argv); + vm->pop_current_binding(); + return retval; } @@ -2700,7 +2744,8 @@ return obj; } } - return __rb_vm_dispatch(cache, obj, NULL, selLTLT, NULL, 0, 1, &other); + return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selLTLT, NULL, 0, 1, + &other); } extern "C" @@ -2716,7 +2761,8 @@ extern VALUE rb_ary_aref(VALUE ary, SEL sel, int argc, VALUE *argv); return rb_ary_aref(obj, 0, 1, &other); } - return __rb_vm_dispatch(cache, obj, NULL, selAREF, NULL, 0, 1, &other); + return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selAREF, NULL, 0, 1, + &other); } extern "C" @@ -2734,7 +2780,8 @@ VALUE args[2]; args[0] = other1; args[1] = other2; - return __rb_vm_dispatch(cache, obj, NULL, selASET, NULL, 0, 2, args); + return __rb_vm_dispatch(GET_VM(), cache, obj, NULL, selASET, NULL, 0, 2, + args); } extern "C" @@ -2785,6 +2832,35 @@ return new_b; } +rb_vm_block_t * +RoxorCore::uncache_or_create_block(NODE *key, bool *cached, int dvars_size) +{ + std::map<NODE *, rb_vm_block_t *>::iterator iter = blocks.find(key); + + rb_vm_block_t *b; + + if ((iter == blocks.end()) + || (iter->second->flags & (VM_BLOCK_ACTIVE | VM_BLOCK_PROC))) { + + if (iter != blocks.end()) { + rb_objc_release(iter->second); + } + + b = (rb_vm_block_t *)xmalloc(sizeof(rb_vm_block_t) + + (sizeof(VALUE *) * dvars_size)); + rb_objc_retain(b); + blocks[key] = b; + + *cached = false; + } + else { + b = iter->second; + *cached = true; + } + + return b; +} + extern "C" rb_vm_block_t * rb_vm_prepare_block(void *llvm_function, NODE *node, VALUE self, @@ -2802,22 +2878,13 @@ cache_key = node; } - std::map<NODE *, rb_vm_block_t *>::iterator iter = - GET_VM()->blocks.find(cache_key); + GET_CORE()->lock(); - rb_vm_block_t *b; bool cached = false; + rb_vm_block_t *b = GET_CORE()->uncache_or_create_block(cache_key, &cached, + dvars_size); - if ((iter == GET_VM()->blocks.end()) - || (iter->second->flags & (VM_BLOCK_ACTIVE | VM_BLOCK_PROC))) { - - if (iter != GET_VM()->blocks.end()) { - rb_objc_release(iter->second); - } - - b = (rb_vm_block_t *)xmalloc(sizeof(rb_vm_block_t) - + (sizeof(VALUE *) * dvars_size)); - + if (!cached) { if (nd_type(node) == NODE_IFUNC) { assert(llvm_function == NULL); b->imp = (IMP)node->u1.node; @@ -2825,23 +2892,20 @@ } else { assert(llvm_function != NULL); - b->imp = GET_VM()->compile((Function *)llvm_function); + b->imp = GET_CORE()->compile((Function *)llvm_function); b->arity = rb_vm_node_arity(node); } b->flags = 0; b->dvars_size = dvars_size; b->parent_var_uses = NULL; b->parent_block = NULL; - - rb_objc_retain(b); - GET_VM()->blocks[cache_key] = b; } else { - b = iter->second; assert(b->dvars_size == dvars_size); - cached = true; } + GET_CORE()->unlock(); + b->self = self; b->node = node; b->parent_var_uses = parent_var_uses; @@ -3020,29 +3084,28 @@ } va_end(ar); - rb_objc_retain(binding); - GET_VM()->bindings.push_back(binding); + GET_VM()->push_current_binding(binding); } extern "C" rb_vm_binding_t * rb_vm_current_binding(void) { - return GET_VM()->bindings.empty() ? NULL : GET_VM()->bindings.back(); + return GET_VM()->current_binding(); } extern "C" void rb_vm_add_binding(rb_vm_binding_t *binding) { - GET_VM()->bindings.push_back(binding); + GET_VM()->push_current_binding(binding, false); } extern "C" void rb_vm_pop_binding(void) { - GET_VM()->bindings.pop_back(); + GET_VM()->pop_current_binding(false); } extern "C" @@ -3057,27 +3120,29 @@ flg = DISPATCH_SUPER; } else { - cache = GET_VM()->method_cache_get(sel, false); + cache = GET_CORE()->method_cache_get(sel, false); } - return __rb_vm_dispatch(cache, self, NULL, sel, NULL, flg, argc, argv); + return __rb_vm_dispatch(GET_VM(), cache, self, NULL, sel, NULL, flg, argc, + argv); } extern "C" VALUE rb_vm_call_with_cache(void *cache, VALUE self, SEL sel, int argc, - const VALUE *argv) + const VALUE *argv) { - return __rb_vm_dispatch((struct mcache *)cache, self, NULL, sel, NULL, 0, - argc, argv); + return __rb_vm_dispatch(GET_VM(), (struct mcache *)cache, self, NULL, sel, + NULL, 0, argc, argv); } extern "C" VALUE rb_vm_call_with_cache2(void *cache, rb_vm_block_t *block, VALUE self, - VALUE klass, SEL sel, int argc, const VALUE *argv) + VALUE klass, SEL sel, int argc, const VALUE *argv) { - return __rb_vm_dispatch((struct mcache *)cache, self, (Class)klass, sel, + return __rb_vm_dispatch(GET_VM(), (struct mcache *)cache, self, + (Class)klass, sel, block, 0, argc, argv); } @@ -3085,7 +3150,7 @@ void * rb_vm_get_call_cache(SEL sel) { - return GET_VM()->method_cache_get(sel, false); + return GET_CORE()->method_cache_get(sel, false); } // Should be used inside a method implementation. @@ -3187,7 +3252,7 @@ fill_ocache(c, obj, oklass, imp, sel, method, arity); } else { - rb_vm_method_node_t *node = GET_VM()->method_node_get(imp); + rb_vm_method_node_t *node = GET_CORE()->method_node_get(imp); assert(node != NULL); fill_rcache(c, oklass, node); } @@ -3384,12 +3449,12 @@ VALUE old_self = b->self; b->self = self; - Class old_class = GET_VM()->current_class; + Class old_class = GET_VM()->get_current_class(); if (klass == self) { // We only toggle the VM current klass in case #module_eval or // #class_eval is used (where the given klass and self objects are // actually the same instances). - GET_VM()->current_class = (Class)klass; + GET_VM()->set_current_class((Class)klass); } VALUE retval = Qnil; @@ -3398,13 +3463,13 @@ } catch (...) { b->self = old_self; - GET_VM()->current_class = old_class; + GET_VM()->set_current_class(old_class); GET_VM()->add_current_block(b); throw; } b->self = old_self; - GET_VM()->current_class = old_class; + GET_VM()->set_current_class(old_class); GET_VM()->add_current_block(b); return retval; @@ -3451,7 +3516,7 @@ } IMP obj_imp = method_getImplementation(m); rb_vm_method_node_t *node = obj_imp == NULL - ? NULL : GET_VM()->method_node_get(obj_imp); + ? NULL : GET_CORE()->method_node_get(obj_imp); if (node != NULL && (reject_pure_ruby_methods @@ -3578,15 +3643,15 @@ rb_raise(rb_eLocalJumpError, "break from proc-closure"); } #endif - GET_VM()->broken_with = val; + GET_VM()->set_broken_with(val); } extern "C" VALUE rb_vm_pop_broken_value(void) { - VALUE val = GET_VM()->broken_with; - GET_VM()->broken_with = Qundef; + VALUE val = GET_VM()->get_broken_with(); + GET_VM()->set_broken_with(Qundef); return val; } @@ -3605,8 +3670,8 @@ char name[100]; unsigned long ln = 0; - if (GET_VM()->symbolize_call_address(callstack[i], NULL, &ln, name, - sizeof name)) { + if (GET_CORE()->symbolize_call_address(callstack[i], NULL, &ln, name, + sizeof name)) { char entry[100]; snprintf(entry, sizeof entry, "%ld:in `%s'", ln, name); rb_ary_push(ary, rb_str_new2(entry)); @@ -3717,14 +3782,14 @@ bool rb_vm_parse_in_eval(void) { - return GET_VM()->parse_in_eval; + return GET_VM()->get_parse_in_eval(); } extern "C" void rb_vm_set_parse_in_eval(bool flag) { - GET_VM()->parse_in_eval = flag; + GET_VM()->set_parse_in_eval(flag); } extern "C" @@ -3764,8 +3829,10 @@ rb_vm_run(const char *fname, NODE *node, rb_vm_binding_t *binding, bool inside_eval) { + RoxorVM *vm = GET_VM(); + if (binding != NULL) { - GET_VM()->bindings.push_back(binding); + vm->push_current_binding(binding, false); } __init_shared_compiler(); @@ -3777,20 +3844,20 @@ compiler->set_inside_eval(old_inside_eval); if (binding != NULL) { - GET_VM()->bindings.pop_back(); + vm->pop_current_binding(false); } #if ROXOR_INTERPRET_EVAL if (inside_eval) { - return GET_VM()->interpret(function); + return GET_CORE()->interpret(function); } else { - IMP imp = GET_VM()->compile(function); - return ((VALUE(*)(VALUE, SEL))imp)(GET_VM()->current_top_object, 0); + IMP imp = GET_CORE()->compile(function); + return ((VALUE(*)(VALUE, SEL))imp)(vm->get_current_top_object(), 0); } #else - IMP imp = GET_VM()->compile(function); - return ((VALUE(*)(VALUE, SEL))imp)(GET_VM()->current_top_object, 0); + IMP imp = GET_CORE()->compile(function); + return ((VALUE(*)(VALUE, SEL))imp)(vm->get_current_top_object(), 0); #endif } @@ -3799,22 +3866,22 @@ rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node, rb_vm_binding_t *binding, bool inside_eval) { - VALUE old_top_object = GET_VM()->current_top_object; + VALUE old_top_object = GET_VM()->get_current_top_object(); if (binding != NULL) { self = binding->self; } if (self != 0) { - GET_VM()->current_top_object = self; + GET_VM()->set_current_top_object(self); } - Class old_class = GET_VM()->current_class; + Class old_class = GET_VM()->get_current_class(); if (klass != 0) { - GET_VM()->current_class = (Class)klass; + GET_VM()->set_current_class((Class)klass); } VALUE val = rb_vm_run(fname, node, binding, inside_eval); - GET_VM()->current_top_object = old_top_object; - GET_VM()->current_class = old_class; + GET_VM()->set_current_top_object(old_top_object); + GET_VM()->set_current_class(old_class); return val; } @@ -3829,7 +3896,7 @@ __init_shared_compiler(); Function *f = RoxorCompiler::shared->compile_main_function(node); f->setName("rb_main"); - GET_VM()->optimize(f); + GET_CORE()->optimize(f); // Save the bitcode into a temporary file. const char *tmpdir = getenv("TMPDIR"); @@ -3927,52 +3994,53 @@ VALUE rb_vm_top_self(void) { - return GET_VM()->current_top_object; + return GET_VM()->get_current_top_object(); } extern "C" VALUE rb_vm_loaded_features(void) { - return GET_VM()->loaded_features; + return GET_CORE()->get_loaded_features(); } extern "C" VALUE rb_vm_load_path(void) { - return GET_VM()->load_path; + return GET_CORE()->get_load_path(); } extern "C" int rb_vm_safe_level(void) { - return GET_VM()->safe_level; + return GET_VM()->get_safe_level(); } extern "C" void rb_vm_set_safe_level(int level) { - GET_VM()->safe_level = level; + GET_VM()->set_safe_level(level); } extern "C" VALUE rb_last_status_get(void) { - return GET_VM()->last_status; + return GET_VM()->get_last_status(); } extern "C" void rb_last_status_set(int status, rb_pid_t pid) { - if (GET_VM()->last_status != Qnil) { - rb_objc_release((void *)GET_VM()->last_status); + VALUE last_status = GET_VM()->get_last_status(); + if (last_status != Qnil) { + rb_objc_release((void *)last_status); } - VALUE last_status; + if (pid == -1) { last_status = Qnil; } @@ -3982,14 +4050,14 @@ rb_iv_set(last_status, "pid", PIDT2NUM(pid)); rb_objc_retain((void *)last_status); } - GET_VM()->last_status = last_status; + GET_VM()->set_last_status(last_status); } extern "C" VALUE rb_errinfo(void) { - return GET_VM()->errinfo; + return GET_VM()->get_errinfo(); } void @@ -3998,10 +4066,11 @@ if (!NIL_P(err) && !rb_obj_is_kind_of(err, rb_eException)) { rb_raise(rb_eTypeError, "assigning non-exception to $!"); } - if (GET_VM()->errinfo != Qnil) { - rb_objc_release((void *)GET_VM()->errinfo); + VALUE errinfo = GET_VM()->get_errinfo(); + if (errinfo != Qnil) { + rb_objc_release((void *)errinfo); } - GET_VM()->errinfo = err; + GET_VM()->set_errinfo(err); rb_objc_retain((void *)err); } @@ -4040,40 +4109,39 @@ void rb_iter_break(void) { - GET_VM()->broken_with = Qnil; + GET_VM()->set_broken_with(Qnil); } extern "C" VALUE rb_backref_get(void) { - return GET_VM()->backref; + return GET_VM()->get_backref(); } extern "C" void rb_backref_set(VALUE val) { - VALUE old = GET_VM()->backref; + VALUE old = GET_VM()->get_backref(); if (old != val) { rb_objc_release((void *)old); - GET_VM()->backref = val; + GET_VM()->set_backref(val); rb_objc_retain((void *)val); } } -extern "C" VALUE -rb_vm_catch(VALUE tag) +RoxorVM::ruby_catch(VALUE tag) { std::map<VALUE, rb_vm_catch_t *>::iterator iter = - GET_VM()->catch_jmp_bufs.find(tag); + catch_jmp_bufs.find(tag); rb_vm_catch_t *s = NULL; - if (iter == GET_VM()->catch_jmp_bufs.end()) { + if (iter == catch_jmp_bufs.end()) { s = (rb_vm_catch_t *)malloc(sizeof(rb_vm_catch_t)); s->throw_value = Qnil; s->nested = 1; - GET_VM()->catch_jmp_bufs[tag] = s; + catch_jmp_bufs[tag] = s; rb_objc_retain((void *)tag); } else { @@ -4091,13 +4159,13 @@ s->throw_value = Qnil; } - iter = GET_VM()->catch_jmp_bufs.find(tag); - assert(iter != GET_VM()->catch_jmp_bufs.end()); + iter = catch_jmp_bufs.find(tag); + assert(iter != catch_jmp_bufs.end()); s->nested--; if (s->nested == 0) { s = iter->second; free(s); - GET_VM()->catch_jmp_bufs.erase(iter); + catch_jmp_bufs.erase(iter); rb_objc_release((void *)tag); } @@ -4106,11 +4174,17 @@ extern "C" VALUE -rb_vm_throw(VALUE tag, VALUE value) +rb_vm_catch(VALUE tag) { + return GET_VM()->ruby_catch(tag); +} + +VALUE +RoxorVM::ruby_throw(VALUE tag, VALUE value) +{ std::map<VALUE, rb_vm_catch_t *>::iterator iter = - GET_VM()->catch_jmp_bufs.find(tag); - if (iter == GET_VM()->catch_jmp_bufs.end()) { + catch_jmp_bufs.find(tag); + if (iter == catch_jmp_bufs.end()) { VALUE desc = rb_inspect(tag); rb_raise(rb_eArgError, "uncaught throw %s", RSTRING_PTR(desc)); } @@ -4124,6 +4198,99 @@ return Qnil; // never reached } +extern "C" +VALUE +rb_vm_throw(VALUE tag, VALUE value) +{ + return GET_VM()->ruby_throw(tag, value); +} + +extern "C" +void * +rb_vm_create_vm(void) +{ + GET_CORE()->set_multithreaded(true); + + RoxorVM *vm = new RoxorVM(); + vm->set_current_top_object(GET_VM()->get_current_top_object()); + return (void *)vm; +} + +void +RoxorCore::register_thread(VALUE thread) +{ + rb_ary_push(threads, thread); + + rb_vm_thread_t *t = GetThreadPtr(thread); + assert(pthread_setspecific(RoxorVM::vm_thread_key, t->vm) == 0); + + RoxorVM *vm = (RoxorVM *)t->vm; + vm->set_thread(thread); +} + +void +RoxorCore::unregister_thread(VALUE thread) +{ + if (rb_ary_delete(threads, thread) != thread) { + printf("trying to unregister a thread (%p) that was never registered!", + (void *)thread); + abort(); + } + + rb_vm_thread_t *t = GetThreadPtr(thread); + RoxorVM *vm = (RoxorVM *)t->vm; + delete vm; + t->vm = NULL; + + assert(pthread_setspecific(RoxorVM::vm_thread_key, NULL) == 0); +} + +extern "C" +void * +rb_vm_thread_run(VALUE thread) +{ + rb_objc_gc_register_thread(); + GET_CORE()->register_thread(thread); + + // Release the thread now. + rb_objc_release((void *)thread); + + try { + rb_vm_thread_t *t = GetThreadPtr(thread); + rb_vm_block_eval(t->body, t->argc, t->argv); + } + catch (...) { + // TODO handle thread-level exceptions. + } + + GET_CORE()->unregister_thread(thread); + rb_objc_gc_unregister_thread(); + + return NULL; + +} + +extern "C" +VALUE +rb_vm_threads(void) +{ + return GET_CORE()->get_threads(); +} + +extern "C" +VALUE +rb_vm_current_thread(void) +{ + return GET_VM()->get_thread(); +} + +extern "C" +VALUE +rb_vm_main_thread(void) +{ + return RoxorVM::main->get_thread(); +} + static VALUE builtin_ostub1(IMP imp, id self, SEL sel, int argc, VALUE *argv) { @@ -4133,8 +4300,8 @@ static void setup_builtin_stubs(void) { - GET_VM()->insert_stub("@@:", (void *)builtin_ostub1, true); - GET_VM()->insert_stub("#@:", (void *)builtin_ostub1, true); + GET_CORE()->insert_stub("@@:", (void *)builtin_ostub1, true); + GET_CORE()->insert_stub("#@:", (void *)builtin_ostub1, true); } static IMP old_resolveClassMethod_imp = NULL; @@ -4165,8 +4332,11 @@ llvm::ExceptionHandling = true; // required! RoxorCompiler::module = new llvm::Module("Roxor"); - RoxorVM::current = new RoxorVM(); + RoxorCore::shared = new RoxorCore(); + RoxorVM::main = new RoxorVM(); + assert(pthread_key_create(&RoxorVM::vm_thread_key, NULL) == 0); + setup_builtin_stubs(); Method m; @@ -4197,15 +4367,27 @@ rb_cTopLevel = rb_define_class("TopLevel", rb_cObject); rb_objc_define_method(rb_cTopLevel, "to_s", (void *)rb_toplevel_to_s, 0); - GET_VM()->current_class = NULL; + GET_VM()->set_current_class(NULL); VALUE top_self = rb_obj_alloc(rb_cTopLevel); rb_objc_retain((void *)top_self); - GET_VM()->current_top_object = top_self; + GET_VM()->set_current_top_object(top_self); } extern "C" void +Init_PostVM(void) +{ + // Create and register the main thread; + rb_vm_thread_t *t = (rb_vm_thread_t *)xmalloc(sizeof(rb_vm_thread_t)); + t->thread = pthread_self(); + t->vm = (void *)GET_VM(); + VALUE main = Data_Wrap_Struct(rb_cThread, NULL, NULL, t); + GET_CORE()->register_thread(main); +} + +extern "C" +void rb_vm_finalize(void) { if (getenv("VM_DUMP_IR") != NULL) { Modified: MacRuby/branches/experimental/vm.h =================================================================== --- MacRuby/branches/experimental/vm.h 2009-06-23 20:47:10 UTC (rev 1920) +++ MacRuby/branches/experimental/vm.h 2009-06-24 08:12:52 UTC (rev 1921) @@ -69,6 +69,16 @@ void *cache; } rb_vm_method_t; +#define GetThreadPtr(obj) ((rb_vm_thread_t *)DATA_PTR(obj)) + +typedef struct rb_vm_thread { + pthread_t thread; + rb_vm_block_t *body; + int argc; + const VALUE *argv; + void *vm; // an instance of RoxorVM +} rb_vm_thread_t; + typedef struct rb_vm_outer { Class klass; struct rb_vm_outer *outer; @@ -312,6 +322,12 @@ void rb_vm_add_binding(rb_vm_binding_t *binding); void rb_vm_pop_binding(); +void *rb_vm_create_vm(void); +void *rb_vm_thread_run(VALUE thread); +VALUE rb_vm_current_thread(void); +VALUE rb_vm_main_thread(void); +VALUE rb_vm_threads(void); + static inline VALUE rb_robject_allocate_instance(VALUE klass) { @@ -425,123 +441,130 @@ class RoxorCompiler; class RoxorJITManager; -class RoxorVM { +#define READER(name, type) \ + type get_##name(void) { return name; } + +#define WRITER(name, type) \ + void set_##name(type v) { name = v; } + +#define ACCESSOR(name, type) \ + READER(name, type) \ + WRITER(name, type) + +// The Core class is a singleton, it's only created once and it's used by the +// VMs. All calls to the Core are thread-safe, they acquire a shared lock. +class RoxorCore { + public: + static RoxorCore *shared; + private: + // LLVM objects. ExistingModuleProvider *emp; RoxorJITManager *jmm; ExecutionEngine *ee; ExecutionEngine *iee; FunctionPassManager *fpm; + + // Running threads. + VALUE threads; + + // State. bool running; + bool multithreaded; + pthread_mutex_t gl; + VALUE loaded_features; + VALUE load_path; + + // Cache to avoid compiling the same Function twice. std::map<Function *, IMP> JITcache; + // Cache to avoid compiling the same block twice. + std::map<NODE *, rb_vm_block_t *> blocks; + + // Cache to identify pure Ruby methods. std::map<IMP, rb_vm_method_node_t *> ruby_imps; + + // Method and constant caches. std::map<SEL, struct mcache *> mcache; std::map<ID, struct ccache *> ccache; + + // Instance variable slots cache. std::map<Class, std::map<ID, int> *> ivar_slots; + + // Optimized selectors redefinition cache. std::map<SEL, GlobalVariable *> redefined_ops_gvars; + + // Outers map (where a class is actually defined). std::map<Class, struct rb_vm_outer *> outers; + + // Maps to cache compiled stubs for a given Objective-C runtime type. std::map<std::string, void *> c_stubs, objc_stubs, to_rval_convertors, to_ocval_convertors; + std::map<Function *, IMP> objc_to_ruby_stubs; - std::vector<rb_vm_block_t *> current_blocks; - std::vector<VALUE> current_exceptions; + // Caches for the lazy JIT. + std::map<SEL, std::map<Class, rb_vm_method_source_t *> *> + method_sources; + std::multimap<Class, SEL> method_source_sels; - public: - static RoxorVM *current; + // BridgeSupport caches. + bs_parser_t *bs_parser; + std::map<std::string, rb_vm_bs_boxed_t *> bs_boxed; + std::map<std::string, bs_element_function_t *> bs_funcs; + std::map<ID, bs_element_constant_t *> bs_consts; + std::map<std::string, std::map<SEL, bs_element_method_t *> *> + bs_classes_class_methods, bs_classes_instance_methods; + std::map<std::string, bs_element_cftype_t *> bs_cftypes; + std::map<SEL, std::string *> bs_informal_protocol_imethods, + bs_informal_protocol_cmethods; - Class current_class; - VALUE current_top_object; - VALUE loaded_features; - VALUE load_path; - VALUE backref; - VALUE broken_with; - 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; - bool parse_in_eval; +#if ROXOR_VM_DEBUG + long functions_compiled; +#endif - std::string debug_blocks(void); + public: + RoxorCore(void); - bool is_block_current(rb_vm_block_t *b) { - return b == NULL - ? false - : current_blocks.empty() - ? false - : current_blocks.back() == b; - } + ACCESSOR(running, bool); + ACCESSOR(multithreaded, bool); + READER(loaded_features, VALUE); + READER(load_path, VALUE); + READER(threads, VALUE); - void add_current_block(rb_vm_block_t *b) { - current_blocks.push_back(b); - } - - void pop_current_block(void) { - assert(!current_blocks.empty()); - current_blocks.pop_back(); - } - - rb_vm_block_t *current_block(void) { - return current_blocks.empty() ? NULL : current_blocks.back(); - } - - rb_vm_block_t *previous_block(void) { - if (current_blocks.size() > 1) { - return current_blocks[current_blocks.size() - 2]; + void lock(void) { + if (multithreaded) { + assert(pthread_mutex_lock(&gl) == 0); } - return NULL; } - - rb_vm_block_t *first_block(void) { - rb_vm_block_t *b = current_block(); - if (b == NULL) { - b = previous_block(); + void unlock(void) { + if (multithreaded) { + assert(pthread_mutex_unlock(&gl) == 0); } - return b; } - std::string debug_exceptions(void); + void register_thread(VALUE thread); + void unregister_thread(VALUE thread); - VALUE current_exception(void) { - return current_exceptions.empty() - ? Qnil : current_exceptions.back(); - } + void optimize(Function *func); + IMP compile(Function *func); + VALUE interpret(Function *func); - void push_current_exception(VALUE exc) { - assert(!NIL_P(exc)); - rb_objc_retain((void *)exc); - current_exceptions.push_back(exc); - } + void load_bridge_support(const char *path, const char *framework_path, + int options); - VALUE pop_current_exception(void) { - assert(!current_exceptions.empty()); - VALUE exc = current_exceptions.back(); - rb_objc_release((void *)exc); - current_exceptions.pop_back(); - return exc; - } - - std::map<VALUE, rb_vm_catch_t *> catch_jmp_bufs; - std::vector<jmp_buf *> return_from_block_jmp_bufs; - - bs_parser_t *bs_parser; - std::map<std::string, rb_vm_bs_boxed_t *> bs_boxed; - std::map<std::string, bs_element_function_t *> bs_funcs; - std::map<ID, bs_element_constant_t *> bs_consts; - std::map<std::string, std::map<SEL, bs_element_method_t *> *> - bs_classes_class_methods, bs_classes_instance_methods; - std::map<std::string, bs_element_cftype_t *> bs_cftypes; - std::map<SEL, std::string> bs_informal_protocol_imethods, - bs_informal_protocol_cmethods; - + bs_element_constant_t *find_bs_const(ID name); bs_element_method_t *find_bs_method(Class klass, SEL sel); rb_vm_bs_boxed_t *find_bs_boxed(std::string type); rb_vm_bs_boxed_t *find_bs_struct(std::string type); rb_vm_bs_boxed_t *find_bs_opaque(std::string type); + bs_element_cftype_t *find_bs_cftype(std::string type); + std::string *find_bs_informal_protocol_method(SEL sel, + bool class_method); + bs_element_function_t *find_bs_function(std::string &name); + // This callback is public for the only reason it's called by C. + void bs_parse_cb(bs_element_type_t type, void *value, void *ctx); + void *gen_stub(std::string types, int argc, bool is_objc); void *gen_to_rval_convertor(std::string type); void *gen_to_ocval_convertor(std::string type); @@ -552,13 +575,8 @@ m.insert(std::make_pair(types, stub)); } - std::map<SEL, std::map<Class, rb_vm_method_source_t *> *> - method_sources; - std::multimap<Class, SEL> method_source_sels; - std::map<Class, rb_vm_method_source_t *> * - method_sources_for_sel(SEL sel, bool create) - { + method_sources_for_sel(SEL sel, bool create) { std::map<SEL, std::map<Class, rb_vm_method_source_t *> *>::iterator iter = method_sources.find(sel); @@ -576,29 +594,23 @@ return map; } - std::map<Function *, IMP> objc_to_ruby_stubs; - -#if ROXOR_VM_DEBUG - long functions_compiled; -#endif - - RoxorVM(void); - - void optimize(Function *func); - IMP compile(Function *func); - VALUE interpret(Function *func); - bool symbolize_call_address(void *addr, void **startp, unsigned long *ln, char *name, size_t name_len); - bool is_running(void) { return running; } - void set_running(bool flag) { running = flag; } - struct mcache *method_cache_get(SEL sel, bool super); rb_vm_method_node_t *method_node_get(IMP imp); + + void prepare_method(Class klass, SEL sel, Function *func, NODE *node); rb_vm_method_node_t *add_method(Class klass, SEL sel, IMP imp, IMP ruby_imp, const rb_vm_arity_t &arity, int flags, const char *types); + rb_vm_method_node_t *resolve_method(Class klass, SEL sel, + Function *func, NODE *node, IMP imp, Method m); + bool resolve_methods(std::map<Class, rb_vm_method_source_t *> *map, + Class klass, SEL sel); + void copy_methods(Class from_class, Class to_class); + void get_methods(VALUE ary, Class klass, bool include_objc_methods, + int (*filter) (VALUE, ID, VALUE)); GlobalVariable *redefined_op_gvar(SEL sel, bool create); bool should_invalidate_inline_op(SEL sel, Class klass); @@ -640,6 +652,161 @@ } } + rb_vm_block_t *uncache_or_create_block(NODE *key, bool *cached, + int dvars_size); + + size_t get_sizeof(const Type *type); + size_t get_sizeof(const char *type); + bool is_large_struct_type(const Type *type); + + private: + bool register_bs_boxed(bs_element_type_t type, void *value); + void register_bs_class(bs_element_class_t *bs_class); +}; + +#define GET_CORE() (RoxorCore::shared) + +// The VM class is instantiated per thread. There is always at least one +// instance. The VM class is purely thread-safe and concurrent, it does not +// acquire any lock, except when it calls the Core. +class RoxorVM { + public: + // The main VM object. + static RoxorVM *main; + + // The pthread specific key to retrieve the current VM thread. + static pthread_key_t vm_thread_key; + + static RoxorVM *current(void) { + if (GET_CORE()->get_multithreaded()) { + void *vm = pthread_getspecific(vm_thread_key); + if (vm == NULL) { + // The value does not exist yet, which means we are called + // from a thread that was not created by MacRuby directly + // (potentially the GC thread or Cocoa). In this case, we + // create a new VM object just for this thread. + // XXX the VM object is never detroyed. + RoxorVM *new_vm = new RoxorVM(); + pthread_setspecific(vm_thread_key, (void *)new_vm); + return new_vm; + } + return (RoxorVM *)vm; + } + return RoxorVM::main; + } + + private: + std::vector<rb_vm_block_t *> current_blocks; + std::vector<VALUE> current_exceptions; + std::vector<rb_vm_binding_t *> bindings; + std::map<VALUE, rb_vm_catch_t *> catch_jmp_bufs; + + VALUE thread; + Class current_class; + VALUE current_top_object; + VALUE backref; + VALUE broken_with; + VALUE last_status; + VALUE errinfo; + int safe_level; + unsigned char method_missing_reason; + bool parse_in_eval; + + public: + RoxorVM(void); + + ACCESSOR(thread, VALUE); + ACCESSOR(current_class, Class); + ACCESSOR(current_top_object, VALUE); + ACCESSOR(backref, VALUE); + ACCESSOR(broken_with, VALUE); + ACCESSOR(last_status, VALUE); + ACCESSOR(errinfo, VALUE); + ACCESSOR(safe_level, int); + ACCESSOR(method_missing_reason, unsigned char); + ACCESSOR(parse_in_eval, bool); + + std::string debug_blocks(void); + + bool is_block_current(rb_vm_block_t *b) { + return b == NULL + ? false + : current_blocks.empty() + ? false + : current_blocks.back() == b; + } + + void add_current_block(rb_vm_block_t *b) { + current_blocks.push_back(b); + } + + void pop_current_block(void) { + assert(!current_blocks.empty()); + current_blocks.pop_back(); + } + + rb_vm_block_t *current_block(void) { + return current_blocks.empty() + ? NULL : current_blocks.back(); + } + + rb_vm_block_t *previous_block(void) { + if (current_blocks.size() > 1) { + return current_blocks[current_blocks.size() - 2]; + } + return NULL; + } + + rb_vm_block_t *first_block(void) { + rb_vm_block_t *b = current_block(); + if (b == NULL) { + b = previous_block(); + } + return b; + } + + rb_vm_binding_t *current_binding(void) { + return bindings.empty() + ? NULL : bindings.back(); + } + + void push_current_binding(rb_vm_binding_t *binding, bool retain=true) { + if (retain) { + rb_objc_retain(binding); + } + bindings.push_back(binding); + } + + void pop_current_binding(bool release=true) { + if (!bindings.empty()) { + if (release) { + rb_objc_release(bindings.back()); + } + bindings.pop_back(); + } + } + + std::string debug_exceptions(void); + + VALUE current_exception(void) { + return current_exceptions.empty() + ? Qnil : current_exceptions.back(); + } + + void push_current_exception(VALUE exc) { + assert(!NIL_P(exc)); + rb_objc_retain((void *)exc); + current_exceptions.push_back(exc); + } + + VALUE pop_current_exception(void) { + assert(!current_exceptions.empty()); + VALUE exc = current_exceptions.back(); + rb_objc_release((void *)exc); + current_exceptions.pop_back(); + return exc; + } + VALUE *get_binding_lvar(ID name) { if (!bindings.empty()) { rb_vm_binding_t *b = bindings.back(); @@ -652,12 +819,11 @@ return NULL; } - size_t get_sizeof(const Type *type); - size_t get_sizeof(const char *type); - bool is_large_struct_type(const Type *type); + VALUE ruby_catch(VALUE tag); + VALUE ruby_throw(VALUE tag, VALUE value); }; -#define GET_VM() (RoxorVM::current) +#define GET_VM() (RoxorVM::current()) #endif /* __cplusplus */