diff --git a/HACKING.rdoc b/HACKING.rdoc index f9c6b9c7a..ddd1d5e28 100644 --- a/HACKING.rdoc +++ b/HACKING.rdoc @@ -124,6 +124,11 @@ The following environment variables might help you debug easy bugs. * VM_VERIFY_IR: set it to any value to force a LLVM module verification before the interpreter quits. +* VM_OPT_LEVEL: set it either to 0, 1, 2 or 3 to change the optimization level + of the LLVM code generator. + +* VM_DISABLE_INLINING: set it to any value to disable function inlining. + * DYLD_LIBRARY_PATH: in case you are debugging a Cocoa application, set this variable to "." before starting gdb, and you won't have to re-install MacRuby every time you re-compile it. diff --git a/array.c b/array.c index 3c134b057..cd3ec7767 100644 --- a/array.c +++ b/array.c @@ -89,27 +89,6 @@ rary_erase(VALUE ary, size_t idx, size_t len) return item; } -void -rary_store(VALUE ary, long idx, VALUE item) -{ - if (idx < 0) { - const long len = RARY(ary)->len; - idx += len; - if (idx < 0) { - rb_raise(rb_eIndexError, "index %ld out of array", - idx - len); - } - } - if (idx >= RARY(ary)->len) { - rary_reserve(ary, idx + 1); - for (size_t i = RARY(ary)->len; i < idx + 1; i++) { - rary_elt_set(ary, i, Qnil); - } - RARY(ary)->len = idx + 1; - } - rary_elt_set(ary, idx, item); -} - static void rary_resize(VALUE ary, size_t newlen) { @@ -217,8 +196,6 @@ rb_ary_new(void) return rb_ary_new2(ARY_DEFAULT_SIZE); } -static void rary_push(VALUE ary, VALUE item); - VALUE rb_ary_new3(long n, ...) { @@ -488,14 +465,6 @@ rary_push_m(VALUE ary, SEL sel, VALUE item) * #=> ["a", "b", "c", "d", "e", "f"] */ -static void -rary_push(VALUE ary, VALUE item) -{ - rary_reserve(ary, RARY(ary)->len + 1); - rary_elt_set(ary, RARY(ary)->len, item); - RARY(ary)->len++; -} - static VALUE rary_push_m2(VALUE ary, SEL sel, int argc, VALUE *argv) { diff --git a/array.h b/array.h index 92fa911b6..0c6c2ff2d 100644 --- a/array.h +++ b/array.h @@ -89,6 +89,39 @@ rary_entry(VALUE ary, long offset) return rary_elt(ary, offset); } +void rary_reserve(VALUE ary, size_t newlen); + +static inline void +rary_store(VALUE ary, long idx, VALUE item) +{ + if (idx < 0) { + const long len = RARY(ary)->len; + idx += len; + if (idx < 0) { + rb_raise(rb_eIndexError, "index %ld out of array", + idx - len); + } + } + size_t uidx = (size_t)idx; + if (uidx >= RARY(ary)->len) { + rary_reserve(ary, uidx + 1); + size_t i; + for (i = RARY(ary)->len; i < uidx + 1; i++) { + rary_elt_set(ary, i, Qnil); + } + RARY(ary)->len = uidx + 1; + } + rary_elt_set(ary, uidx, item); +} + +static inline void +rary_push(VALUE ary, VALUE item) +{ + rary_reserve(ary, RARY(ary)->len + 1); + rary_elt_set(ary, RARY(ary)->len, item); + RARY(ary)->len++; +} + static inline void rb_ary_modify(VALUE ary) { @@ -120,7 +153,6 @@ VALUE rary_unshift(VALUE ary, SEL sel, int argc, VALUE *argv); VALUE rary_each(VALUE ary, SEL sel); VALUE rary_sort(VALUE ary, SEL sel); VALUE rary_sort_bang(VALUE ary, SEL sel); -void rary_store(VALUE ary, long idx, VALUE item); VALUE rary_subseq(VALUE ary, long beg, long len); void rary_insert(VALUE ary, long idx, VALUE val); diff --git a/b.rb b/b.rb index ab7c5ccf4..93bc30e53 100644 --- a/b.rb +++ b/b.rb @@ -1,6 +1,6 @@ def bench(e, options) puts e - ['./miniruby', 'ruby19'].each do |r| + ['./miniruby', 'ruby1.9'].each do |r| puts `#{r} -v`.strip line = File.exist?(e) ? "#{r} \"#{e}\"" : "#{r} -e \"#{e}\"" n = options.include?('--no-rehearsal') ? 1 : 3 diff --git a/bignum.c b/bignum.c index 1e0b20c92..e67d2c11c 100644 --- a/bignum.c +++ b/bignum.c @@ -270,20 +270,6 @@ rb_int2big(SIGNED_VALUE n) return big; } -VALUE -rb_uint2inum(VALUE n) -{ - if (POSFIXABLE(n)) return LONG2FIX(n); - return rb_uint2big(n); -} - -VALUE -rb_int2inum(SIGNED_VALUE n) -{ - if (FIXABLE(n)) return LONG2FIX(n); - return rb_int2big(n); -} - #ifdef HAVE_LONG_LONG void @@ -628,8 +614,8 @@ rb_str_to_inum(VALUE str, int base, int badcheck) #if HAVE_LONG_LONG -static VALUE -rb_ull2big(unsigned LONG_LONG n) +VALUE +rb_ull2big(unsigned long long n) { BDIGIT_DBL num = n; long i = 0; @@ -649,8 +635,8 @@ rb_ull2big(unsigned LONG_LONG n) return big; } -static VALUE -rb_ll2big(LONG_LONG n) +VALUE +rb_ll2big(long long n) { long neg = 0; VALUE big; @@ -666,20 +652,6 @@ rb_ll2big(LONG_LONG n) return big; } -VALUE -rb_ull2inum(unsigned LONG_LONG n) -{ - if (POSFIXABLE(n)) return LONG2FIX(n); - return rb_ull2big(n); -} - -VALUE -rb_ll2inum(LONG_LONG n) -{ - if (FIXABLE(n)) return LONG2FIX(n); - return rb_ll2big(n); -} - #endif /* HAVE_LONG_LONG */ VALUE diff --git a/bridgesupport.cpp b/bridgesupport.cpp index a28472fec..0238683ba 100644 --- a/bridgesupport.cpp +++ b/bridgesupport.cpp @@ -701,6 +701,7 @@ rb_boxed_objc_type(VALUE rcv, SEL sel) return rb_ivar_get(rcv, boxed_ivar_type); } +extern "C" bool rb_boxed_is_type(VALUE klass, const char *type) { @@ -764,6 +765,7 @@ rb_pointer_init_type(rb_vm_pointer_t *ptr, VALUE type) assert(ptr->type_size > 0); } +extern "C" VALUE rb_pointer_new(const char *type_str, void *val, size_t len) { @@ -779,6 +781,7 @@ rb_pointer_new(const char *type_str, void *val, size_t len) static VALUE rb_pointer_aset(VALUE rcv, SEL sel, VALUE idx, VALUE val); +extern "C" VALUE rb_pointer_new2(const char *type_str, VALUE rval) { @@ -828,6 +831,7 @@ rb_pointer_s_new(VALUE rcv, SEL sel, int argc, VALUE *argv) xmalloc(GET_CORE()->get_sizeof(type_str) * rlen), rlen); } +extern "C" void * rb_pointer_get_data(VALUE rcv, const char *type) { @@ -1115,17 +1119,7 @@ RoxorCore::bs_parse_cb(bs_element_type_t type, void *value, void *ctx) if (!CFDictionaryGetValueIfPresent(rb_cObject_dict, (const void *)name, NULL)) { - VALUE val; -#if 0 // this is likely not needed anymore - if (bs_strconst->nsstring) { - CFStringRef string = CFStringCreateWithCString(NULL, - bs_strconst->value, kCFStringEncodingUTF8); - val = (VALUE)string; - } - else { -#endif - val = rb_str_new2(bs_strconst->value); -// } + VALUE val = rb_str_new2(bs_strconst->value); CFDictionarySetValue(rb_cObject_dict, (const void *)name, (const void *)val); } diff --git a/bridgesupport.h b/bridgesupport.h index f21bb15d6..0c641998a 100644 --- a/bridgesupport.h +++ b/bridgesupport.h @@ -10,6 +10,16 @@ #define __BRIDGESUPPORT_H_ #if defined(__cplusplus) +extern "C" { +#endif + +void *rb_pointer_get_data(VALUE rcv, const char *type); +VALUE rb_pointer_new(const char *type_str, void *val, size_t len); +VALUE rb_pointer_new2(const char *type_str, VALUE val); +bool rb_boxed_is_type(VALUE klass, const char *type); + +#if defined(__cplusplus) +} // extern "C" #include "bs.h" @@ -25,12 +35,6 @@ typedef struct rb_vm_bs_boxed { VALUE klass; } rb_vm_bs_boxed_t; -VALUE rb_pointer_new(const char *type_str, void *val, size_t len); -VALUE rb_pointer_new2(const char *type_str, VALUE val); -void *rb_pointer_get_data(VALUE rcv, const char *type); - -bool rb_boxed_is_type(VALUE klass, const char *type); - #endif /* __cplusplus */ #endif /* __BRIDGESUPPORT_H_ */ diff --git a/class.h b/class.h index 23a833fba..51d6118eb 100644 --- a/class.h +++ b/class.h @@ -39,6 +39,67 @@ CFMutableDictionaryRef rb_class_ivar_dict(VALUE); CFMutableDictionaryRef rb_class_ivar_dict_or_create(VALUE); void rb_class_ivar_set_dict(VALUE, CFMutableDictionaryRef); +typedef enum { + SCOPE_DEFAULT = 0, // public for everything but Object + SCOPE_PUBLIC, + SCOPE_PRIVATE, + SCOPE_PROTECTED, + SCOPE_MODULE_FUNC, +} rb_vm_scope_t; + +static inline void +rb_vm_check_if_module(VALUE mod) +{ + switch (TYPE(mod)) { + case T_CLASS: + case T_MODULE: + break; + + default: + rb_raise(rb_eTypeError, "%s is not a class/module", + RSTRING_PTR(rb_inspect(mod))); + } +} + +static inline void +rb_vm_set_current_scope(VALUE mod, rb_vm_scope_t scope) +{ + if (scope == SCOPE_DEFAULT) { + scope = mod == rb_cObject ? SCOPE_PRIVATE : SCOPE_PUBLIC; + } + long v = RCLASS_VERSION(mod); + switch (scope) { + case SCOPE_PUBLIC: + v &= ~RCLASS_SCOPE_PRIVATE; + v &= ~RCLASS_SCOPE_PROTECTED; + v &= ~RCLASS_SCOPE_MOD_FUNC; + break; + + case SCOPE_PRIVATE: + v |= RCLASS_SCOPE_PRIVATE; + v &= ~RCLASS_SCOPE_PROTECTED; + v &= ~RCLASS_SCOPE_MOD_FUNC; + break; + + case SCOPE_PROTECTED: + v &= ~RCLASS_SCOPE_PRIVATE; + v |= RCLASS_SCOPE_PROTECTED; + v &= ~RCLASS_SCOPE_MOD_FUNC; + break; + + case SCOPE_MODULE_FUNC: + v &= ~RCLASS_SCOPE_PRIVATE; + v &= ~RCLASS_SCOPE_PROTECTED; + v |= RCLASS_SCOPE_MOD_FUNC; + break; + + case SCOPE_DEFAULT: + abort(); // handled earlier + } + + RCLASS_SET_VERSION(mod, v); +} + #if defined(__cplusplus) } // extern "C" #endif diff --git a/compiler.cpp b/compiler.cpp index 85abb0655..60b6f3624 100644 --- a/compiler.cpp +++ b/compiler.cpp @@ -13,6 +13,7 @@ #endif #include +#include #include "llvm.h" #include "ruby/ruby.h" @@ -26,6 +27,7 @@ #include "encoding.h" #include "re.h" #include "bs.h" +#include "class.h" extern "C" const char *ruby_node_name(int node); @@ -76,47 +78,52 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode) block_declaration = false; dispatcherFunc = NULL; - fastPlusFunc = NULL; - fastMinusFunc = NULL; - fastMultFunc = NULL; - fastDivFunc = NULL; - fastLtFunc = NULL; - fastLeFunc = NULL; - fastGtFunc = NULL; - fastGeFunc = NULL; - fastEqFunc = NULL; - fastNeqFunc = NULL; - fastEqqFunc = NULL; - whenSplatFunc = NULL; + fastPlusFunc = get_function("vm_fast_plus"); + fastMinusFunc = get_function("vm_fast_minus"); + fastMultFunc = get_function("vm_fast_mult"); + fastDivFunc = get_function("vm_fast_div"); + fastLtFunc = get_function("vm_fast_lt"); + fastLeFunc = get_function("vm_fast_le"); + fastGtFunc = get_function("vm_fast_gt"); + fastGeFunc = get_function("vm_fast_ge"); + fastEqFunc = get_function("vm_fast_eq"); + fastNeqFunc = get_function("vm_fast_neq"); + fastEqqFunc = get_function("vm_fast_eqq"); + fastArefFunc = get_function("vm_fast_aref"); + fastAsetFunc = get_function("vm_fast_aset"); + fastShiftFunc = get_function("vm_fast_shift"); + whenSplatFunc = get_function("vm_when_splat"); prepareBlockFunc = NULL; pushBindingFunc = NULL; getBlockFunc = NULL; currentBlockObjectFunc = NULL; - getConstFunc = NULL; - setConstFunc = NULL; + getConstFunc = get_function("vm_get_const"); + setConstFunc = get_function("vm_set_const"); prepareMethodFunc = NULL; singletonClassFunc = NULL; defineClassFunc = NULL; - prepareIvarSlotFunc = NULL; - getIvarFunc = NULL; - setIvarFunc = NULL; - setKVOIvarFunc = NULL; + getIvarFunc = get_function("vm_ivar_get"); + setIvarFunc = get_function("vm_ivar_set"); + willChangeValueFunc = NULL; + didChangeValueFunc = NULL; definedFunc = NULL; undefFunc = NULL; aliasFunc = NULL; valiasFunc = NULL; - newHashFunc = NULL; - toAFunc = NULL; - toAryFunc = NULL; - catArrayFunc = NULL; - dupArrayFunc = NULL; - newArrayFunc = NULL; + newHashFunc = get_function("vm_rhash_new"); + storeHashFunc = get_function("vm_rhash_store"); + toAFunc = get_function("vm_to_a"); + toAryFunc = get_function("vm_to_ary"); + catArrayFunc = get_function("vm_ary_cat"); + dupArrayFunc = get_function("vm_ary_dup"); + newArrayFunc = get_function("vm_rary_new"); + asetArrayFunc = get_function("vm_rary_aset"); newStructFunc = NULL; newOpaqueFunc = NULL; newPointerFunc = NULL; getStructFieldsFunc = NULL; getOpaqueDataFunc = NULL; - getPointerPtrFunc = NULL; + getPointerPtrFunc = get_function("vm_rval_to_cptr"); xmallocFunc = NULL; checkArityFunc = NULL; setStructFunc = NULL; @@ -124,9 +131,10 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode) newRegexpFunc = NULL; strInternFunc = NULL; keepVarsFunc = NULL; - masgnGetElemBeforeSplatFunc = NULL; - masgnGetElemAfterSplatFunc = NULL; - masgnGetSplatFunc = NULL; + masgnGetElemBeforeSplatFunc = + get_function("vm_masgn_get_elem_before_splat"); + masgnGetElemAfterSplatFunc = get_function("vm_masgn_get_elem_after_splat"); + masgnGetSplatFunc = get_function("vm_masgn_get_splat"); newStringFunc = NULL; newString2Func = NULL; newString3Func = NULL; @@ -135,25 +143,54 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode) blockEvalFunc = NULL; gvarSetFunc = NULL; gvarGetFunc = NULL; - cvarSetFunc = NULL; - cvarGetFunc = NULL; + cvarSetFunc = get_function("vm_cvar_set"); + cvarGetFunc = get_function("vm_cvar_get"); currentExceptionFunc = NULL; popExceptionFunc = NULL; - getSpecialFunc = NULL; + getSpecialFunc = get_function("vm_get_special"); breakFunc = NULL; returnFromBlockFunc = NULL; returnedFromBlockFunc = NULL; checkReturnFromBlockFunc = NULL; setHasEnsureFunc = NULL; - longjmpFunc = NULL; - setjmpFunc = NULL; - setScopeFunc = NULL; + setScopeFunc = get_function("vm_set_current_scope"); setCurrentClassFunc = NULL; getCacheFunc = NULL; debugTrapFunc = NULL; getFFStateFunc = NULL; setFFStateFunc = NULL; takeOwnershipFunc = NULL; + ocvalToRvalFunc = get_function("vm_ocval_to_rval"); + charToRvalFunc = get_function("vm_char_to_rval"); + ucharToRvalFunc = get_function("vm_uchar_to_rval"); + shortToRvalFunc = get_function("vm_short_to_rval"); + ushortToRvalFunc = get_function("vm_ushort_to_rval"); + intToRvalFunc = get_function("vm_int_to_rval"); + uintToRvalFunc = get_function("vm_uint_to_rval"); + longToRvalFunc = get_function("vm_long_to_rval"); + ulongToRvalFunc = get_function("vm_ulong_to_rval"); + longLongToRvalFunc = get_function("vm_long_long_to_rval"); + ulongLongToRvalFunc = get_function("vm_ulong_long_to_rval"); + floatToRvalFunc = get_function("vm_float_to_rval"); + doubleToRvalFunc = get_function("vm_double_to_rval"); + selToRvalFunc = get_function("vm_sel_to_rval"); + charPtrToRvalFunc = get_function("vm_charptr_to_rval"); + rvalToOcvalFunc = get_function("vm_rval_to_ocval"); + rvalToBoolFunc = get_function("vm_rval_to_bool"); + rvalToCharFunc = get_function("vm_rval_to_char"); + rvalToUcharFunc = get_function("vm_rval_to_uchar"); + rvalToShortFunc = get_function("vm_rval_to_short"); + rvalToUshortFunc = get_function("vm_rval_to_ushort"); + rvalToIntFunc = get_function("vm_rval_to_int"); + rvalToUintFunc = get_function("vm_rval_to_uint"); + rvalToLongFunc = get_function("vm_rval_to_long"); + rvalToUlongFunc = get_function("vm_rval_to_ulong"); + rvalToLongLongFunc = get_function("vm_rval_to_long_long"); + rvalToUlongLongFunc = get_function("vm_rval_to_ulong_long"); + rvalToFloatFunc = get_function("vm_rval_to_float"); + rvalToDoubleFunc = get_function("vm_rval_to_double"); + rvalToSelFunc = get_function("vm_rval_to_sel"); + rvalToCharPtrFunc = get_function("vm_rval_to_charptr"); VoidTy = Type::getVoidTy(context); Int1Ty = Type::getInt1Ty(context); @@ -212,7 +249,7 @@ RoxorAOTCompiler::RoxorAOTCompiler(void) cStandardError_gvar = NULL; } -inline SEL +SEL RoxorCompiler::mid_to_sel(ID mid, int arity) { SEL sel; @@ -228,24 +265,6 @@ RoxorCompiler::mid_to_sel(ID mid, int arity) return sel; } -inline bool -RoxorCompiler::unbox_ruby_constant(Value *val, VALUE *rval) -{ - if (ConstantInt::classof(val)) { - long tmp = cast(val)->getZExtValue(); - *rval = tmp; - return true; - } - return false; -} - -inline ICmpInst * -RoxorCompiler::is_value_a_fixnum(Value *val) -{ - Value *andOp = BinaryOperator::CreateAnd(val, oneVal, "", bb); - return new ICmpInst(*bb, ICmpInst::ICMP_EQ, andOp, oneVal); -} - Instruction * RoxorCompiler::compile_protected_call(Value *imp, std::vector ¶ms) { @@ -422,81 +441,11 @@ RoxorCompiler::compile_dispatch_arguments(NODE *args, *pargc = argc; } -Value * -RoxorCompiler::compile_fast_op_call(SEL sel, Value *selfVal, Value *otherVal) -{ - Function *func = NULL; - - // VALUE rb_vm_fast_op(struct mcache *cache, VALUE left, VALUE right); -#define fast_op(storage, name) \ - do { \ - if (storage == NULL) { \ - storage = cast(module->getOrInsertFunction(name, \ - RubyObjTy, PtrTy, RubyObjTy, RubyObjTy, NULL)); \ - } \ - func = storage; \ - } \ - while (0) - - if (sel == selPLUS) { - fast_op(fastPlusFunc, "rb_vm_fast_plus"); - } - else if (sel == selMINUS) { - fast_op(fastMinusFunc, "rb_vm_fast_minus"); - } - else if (sel == selDIV) { - fast_op(fastDivFunc, "rb_vm_fast_div"); - } - else if (sel == selMULT) { - fast_op(fastMultFunc, "rb_vm_fast_mult"); - } - else if (sel == selLT) { - fast_op(fastLtFunc, "rb_vm_fast_lt"); - } - else if (sel == selLE) { - fast_op(fastLeFunc, "rb_vm_fast_le"); - } - else if (sel == selGT) { - fast_op(fastGtFunc, "rb_vm_fast_gt"); - } - else if (sel == selGE) { - fast_op(fastGeFunc, "rb_vm_fast_ge"); - } - else if (sel == selEq) { - fast_op(fastEqFunc, "rb_vm_fast_eq"); - } - else if (sel == selNeq) { - fast_op(fastNeqFunc, "rb_vm_fast_neq"); - } - else if (sel == selEqq) { - fast_op(fastEqqFunc, "rb_vm_fast_eqq"); - } - else { - return NULL; - } - - std::vector params; - params.push_back(compile_mcache(sel, false)); - params.push_back(selfVal); - params.push_back(otherVal); - - return compile_protected_call(func, params); -} - Value * RoxorCompiler::compile_when_splat(Value *comparedToVal, Value *splatVal) { - if (whenSplatFunc == NULL) { - // VALUE rb_vm_when_splat(struct mcache *cache, - // unsigned char overriden, - // VALUE comparedTo, VALUE splat) - whenSplatFunc = cast - (module->getOrInsertFunction("rb_vm_when_splat", - RubyObjTy, PtrTy, Int1Ty, - RubyObjTy, RubyObjTy, NULL)); - } - std::vector params; + params.push_back(compile_mcache(selEqq, false)); GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(selEqq, true); params.push_back(new LoadInst(is_redefined, "", bb)); @@ -667,7 +616,7 @@ RoxorAOTCompiler::compile_sel(SEL sel, bool add_to_bb) : new LoadInst(gvar, ""); } -inline Value * +Value * RoxorCompiler::compile_arity(rb_vm_arity_t &arity) { uint64_t v; @@ -880,37 +829,14 @@ Value * RoxorCompiler::compile_multiple_assignment(NODE *node, Value *val) { assert(nd_type(node) == NODE_MASGN); - if (toAryFunc == NULL) { - // VALUE rb_vm_to_ary(VALUE ary); - toAryFunc = cast(module->getOrInsertFunction( - "rb_vm_to_ary", - RubyObjTy, RubyObjTy, NULL)); - } - if (masgnGetElemBeforeSplatFunc == NULL) { - // VALUE rb_vm_masgn_get_elem_before_splat(VALUE ary, int offset); - masgnGetElemBeforeSplatFunc = cast(module->getOrInsertFunction( - "rb_vm_masgn_get_elem_before_splat", - RubyObjTy, RubyObjTy, Int32Ty, NULL)); - } - if (masgnGetElemAfterSplatFunc == NULL) { - // VALUE rb_vm_masgn_get_elem_after_splat(VALUE ary, int before_splat_count, int after_splat_count, int offset); - masgnGetElemAfterSplatFunc = cast(module->getOrInsertFunction( - "rb_vm_masgn_get_elem_after_splat", - RubyObjTy, RubyObjTy, Int32Ty, Int32Ty, Int32Ty, NULL)); - } - if (masgnGetSplatFunc == NULL) { - // VALUE rb_vm_masgn_get_splat(VALUE ary, int before_splat_count, int after_splat_count); - masgnGetSplatFunc = cast(module->getOrInsertFunction( - "rb_vm_masgn_get_splat", - RubyObjTy, RubyObjTy, Int32Ty, Int32Ty, NULL)); - } NODE *before_splat = node->nd_head, *after_splat = NULL, *splat = NULL; assert((before_splat == NULL) || (nd_type(before_splat) == NODE_ARRAY)); // if the splat has no name (a, *, b = 1, 2, 3), its node value is -1 - if ((node->nd_next == (NODE *)-1) || (node->nd_next == NULL) || (nd_type(node->nd_next) != NODE_POSTARG)) { + if ((node->nd_next == (NODE *)-1) || (node->nd_next == NULL) + || (nd_type(node->nd_next) != NODE_POSTARG)) { splat = node->nd_next; } else { @@ -929,20 +855,18 @@ RoxorCompiler::compile_multiple_assignment(NODE *node, Value *val) ++after_splat_count; } - { - std::vector params; - params.push_back(val); - val = CallInst::Create(toAryFunc, params.begin(), + std::vector params; + params.push_back(val); + val = CallInst::Create(toAryFunc, params.begin(), params.end(), "", bb); - } NODE *l = before_splat; for (int i = 0; l != NULL; ++i) { std::vector params; params.push_back(val); params.push_back(ConstantInt::get(Int32Ty, i)); - Value *elt = CallInst::Create(masgnGetElemBeforeSplatFunc, params.begin(), - params.end(), "", bb); + Value *elt = CallInst::Create(masgnGetElemBeforeSplatFunc, + params.begin(), params.end(), "", bb); compile_multiple_assignment_element(l->nd_head, elt); @@ -967,8 +891,8 @@ RoxorCompiler::compile_multiple_assignment(NODE *node, Value *val) params.push_back(ConstantInt::get(Int32Ty, before_splat_count)); params.push_back(ConstantInt::get(Int32Ty, after_splat_count)); params.push_back(ConstantInt::get(Int32Ty, i)); - Value *elt = CallInst::Create(masgnGetElemAfterSplatFunc, params.begin(), - params.end(), "", bb); + Value *elt = CallInst::Create(masgnGetElemAfterSplatFunc, + params.begin(), params.end(), "", bb); compile_multiple_assignment_element(l->nd_head, elt); @@ -1158,13 +1082,6 @@ RoxorAOTCompiler::compile_slot_cache(ID id) Value * RoxorCompiler::compile_ivar_read(ID vid) { - if (getIvarFunc == NULL) { - // VALUE rb_vm_ivar_get(VALUE obj, ID name, struct icache *cache); - getIvarFunc = cast(module->getOrInsertFunction( - "rb_vm_ivar_get", - RubyObjTy, RubyObjTy, IntTy, PtrTy, NULL)); - } - std::vector params; params.push_back(current_self); @@ -1177,14 +1094,6 @@ RoxorCompiler::compile_ivar_read(ID vid) Value * RoxorCompiler::compile_ivar_assignment(ID vid, Value *val) { - if (setIvarFunc == NULL) { - // void rb_vm_ivar_set(VALUE obj, ID name, VALUE val, - // struct icache *cache); - setIvarFunc = - cast(module->getOrInsertFunction("rb_vm_ivar_set", - VoidTy, RubyObjTy, IntTy, RubyObjTy, PtrTy, NULL)); - } - std::vector params; params.push_back(current_self); @@ -1200,14 +1109,6 @@ RoxorCompiler::compile_ivar_assignment(ID vid, Value *val) Value * RoxorCompiler::compile_cvar_get(ID id, bool check) { - if (cvarGetFunc == NULL) { - // VALUE rb_vm_cvar_get(VALUE klass, ID id, unsigned char check, - // unsigned char dynamic_class); - cvarGetFunc = cast(module->getOrInsertFunction( - "rb_vm_cvar_get", - RubyObjTy, RubyObjTy, IntTy, Int8Ty, Int8Ty, NULL)); - } - std::vector params; params.push_back(compile_current_class()); @@ -1221,14 +1122,6 @@ RoxorCompiler::compile_cvar_get(ID id, bool check) Value * RoxorCompiler::compile_cvar_assignment(ID name, Value *val) { - if (cvarSetFunc == NULL) { - // VALUE rb_vm_cvar_set(VALUE klass, ID id, VALUE val, - // unsigned char dynamic_class); - cvarSetFunc = cast(module->getOrInsertFunction( - "rb_vm_cvar_set", - RubyObjTy, RubyObjTy, IntTy, RubyObjTy, Int8Ty, NULL)); - } - std::vector params; params.push_back(compile_current_class()); @@ -1261,15 +1154,6 @@ RoxorCompiler::compile_gvar_assignment(NODE *node, Value *val) Value * RoxorCompiler::compile_constant_declaration(NODE *node, Value *val) { - if (setConstFunc == NULL) { - // VALUE rb_vm_set_const(VALUE mod, ID id, VALUE obj, - // unsigned char dynamic_class); - setConstFunc = cast(module->getOrInsertFunction( - "rb_vm_set_const", - VoidTy, RubyObjTy, IntTy, RubyObjTy, Int8Ty, - NULL)); - } - std::vector params; int flags = 0; @@ -1303,13 +1187,13 @@ RoxorCompiler::compile_current_class(void) return new LoadInst(current_opened_class, "", bb); } -inline Value * +Value * RoxorCompiler::compile_nsobject(void) { return ConstantInt::get(RubyObjTy, rb_cObject); } -inline Value * +Value * RoxorAOTCompiler::compile_nsobject(void) { if (cObject_gvar == NULL) { @@ -1320,13 +1204,13 @@ RoxorAOTCompiler::compile_nsobject(void) return new LoadInst(cObject_gvar, "", bb); } -inline Value * +Value * RoxorCompiler::compile_standarderror(void) { return ConstantInt::get(RubyObjTy, rb_eStandardError); } -inline Value * +Value * RoxorAOTCompiler::compile_standarderror(void) { if (cStandardError_gvar == NULL) { @@ -1338,7 +1222,7 @@ RoxorAOTCompiler::compile_standarderror(void) return new LoadInst(cStandardError_gvar, "", bb); } -inline Value * +Value * RoxorCompiler::compile_id(ID id) { return ConstantInt::get(IntTy, (long)id); @@ -1371,15 +1255,6 @@ RoxorCompiler::compile_const(ID id, Value *outer) outer_given = false; } - if (getConstFunc == NULL) { - // VALUE rb_vm_get_const(VALUE mod, struct ccache *cache, ID id, - // int flags); - getConstFunc = cast(module->getOrInsertFunction( - "rb_vm_get_const", - RubyObjTy, RubyObjTy, PtrTy, IntTy, Int32Ty, - NULL)); - } - std::vector params; params.push_back(outer); @@ -2011,232 +1886,6 @@ RoxorCompiler::compile_current_exception(void) return CallInst::Create(currentExceptionFunc, "", bb); } -typedef struct rb_vm_immediate_val { - int type; - union { - long l; - double d; - } v; - rb_vm_immediate_val(void) { type = 0; } - bool is_fixnum(void) { return type == T_FIXNUM; } - bool is_float(void) { return type == T_FLOAT; } - long long_val(void) { return is_fixnum() ? v.l : (long)v.d; } - double double_val(void) { return is_float() ? v.d : (double)v.l; } -} rb_vm_immediate_val_t; - -static bool -unbox_immediate_val(VALUE rval, rb_vm_immediate_val_t *val) -{ - if (rval != Qundef) { - if (FIXNUM_P(rval)) { - val->type = T_FIXNUM; - val->v.l = FIX2LONG(rval); - return true; - } - else if (FIXFLOAT_P(rval)) { - val->type = T_FLOAT; - val->v.d = FIXFLOAT2DBL(rval); - return true; - } - } - return false; -} - -template static bool -optimized_const_immediate_op(SEL sel, T leftVal, T rightVal, - bool *is_predicate, T *res_p) -{ - T res; - if (sel == selPLUS) { - res = leftVal + rightVal; - } - else if (sel == selMINUS) { - res = leftVal - rightVal; - } - else if (sel == selDIV) { - if (rightVal == 0) { - return false; - } - res = leftVal / rightVal; - } - else if (sel == selMULT) { - res = leftVal * rightVal; - } - else { - *is_predicate = true; - if (sel == selLT) { - res = leftVal < rightVal; - } - else if (sel == selLE) { - res = leftVal <= rightVal; - } - else if (sel == selGT) { - res = leftVal > rightVal; - } - else if (sel == selGE) { - res = leftVal >= rightVal; - } - else if (sel == selEq || sel == selEqq) { - res = leftVal == rightVal; - } - else if (sel == selNeq) { - res = leftVal != rightVal; - } - else { - abort(); - } - } - *res_p = res; - return true; -} - -Value * -RoxorCompiler::optimized_immediate_op(SEL sel, Value *leftVal, Value *rightVal, - bool float_op, bool *is_predicate) -{ - Value *res; - if (sel == selPLUS) { - res = BinaryOperator::CreateAdd(leftVal, rightVal, "", bb); - } - else if (sel == selMINUS) { - res = BinaryOperator::CreateSub(leftVal, rightVal, "", bb); - } - else if (sel == selDIV) { - if (float_op) { - res = BinaryOperator::CreateFDiv(leftVal, rightVal, "", bb); - } - else { - // Fixnum division in Ruby is not a simple matter of returning the - // division result. We must round up the result in case one of the - // operands is negative. - - Value *normal_res = BinaryOperator::CreateSDiv(leftVal, rightVal, - "", bb); - - ICmpInst *left_negative = new ICmpInst(*bb, ICmpInst::ICMP_SLT, - leftVal, zeroVal); - - ICmpInst *right_negative = new ICmpInst(*bb, ICmpInst::ICMP_SLT, - rightVal, zeroVal); - - Function *f = bb->getParent(); - BasicBlock *negative_bb = BasicBlock::Create(context, "", f); - BasicBlock *check_right_bb = BasicBlock::Create(context, "", f); - BasicBlock *try_right_bb = BasicBlock::Create(context, "", f); - BasicBlock *hack_res_bb = BasicBlock::Create(context, "", f); - BasicBlock *merge_bb = BasicBlock::Create(context, "", f); - - BranchInst::Create(check_right_bb, try_right_bb, left_negative, bb); - - bb = check_right_bb; - BranchInst::Create(merge_bb, negative_bb, right_negative, bb); - - bb = try_right_bb; - BranchInst::Create(negative_bb, merge_bb, right_negative, bb); - - bb = negative_bb; - Value *rem = BinaryOperator::CreateSRem(leftVal, rightVal, - "", bb); - ICmpInst *hack_result = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - rem, zeroVal); - BranchInst::Create(merge_bb, hack_res_bb, hack_result, bb); - - bb = hack_res_bb; - Value *hacked_res = BinaryOperator::CreateSub(normal_res, - ConstantInt::get(IntTy, 1), "", bb); - BranchInst::Create(merge_bb, bb); - - bb = merge_bb; - PHINode *pn = PHINode::Create(IntTy, "", bb); - pn->addIncoming(hacked_res, hack_res_bb); - pn->addIncoming(normal_res, check_right_bb); - pn->addIncoming(normal_res, try_right_bb); - pn->addIncoming(normal_res, negative_bb); - - return pn; - } - } - else if (sel == selMULT) { - res = BinaryOperator::CreateMul(leftVal, rightVal, "", bb); - } - else { - *is_predicate = true; - - CmpInst::Predicate predicate; - - if (sel == selLT) { - predicate = float_op ? FCmpInst::FCMP_OLT : ICmpInst::ICMP_SLT; - } - else if (sel == selLE) { - predicate = float_op ? FCmpInst::FCMP_OLE : ICmpInst::ICMP_SLE; - } - else if (sel == selGT) { - predicate = float_op ? FCmpInst::FCMP_OGT : ICmpInst::ICMP_SGT; - } - else if (sel == selGE) { - predicate = float_op ? FCmpInst::FCMP_OGE : ICmpInst::ICMP_SGE; - } - else if (sel == selEq || sel == selEqq) { - predicate = float_op ? FCmpInst::FCMP_OEQ : ICmpInst::ICMP_EQ; - } - else if (sel == selNeq) { - predicate = float_op ? FCmpInst::FCMP_ONE : ICmpInst::ICMP_NE; - } - else { - abort(); - } - - if (float_op) { - res = new FCmpInst(*bb, predicate, leftVal, rightVal); - } - else { - res = new ICmpInst(*bb, predicate, leftVal, rightVal); - } - res = SelectInst::Create(res, trueVal, falseVal, "", bb); - } - return res; -} - -Value * -RoxorCompiler::compile_double_coercion(Value *val, Value *mask, - BasicBlock *fallback_bb, Function *f) -{ - Value *is_float = new ICmpInst(*bb, ICmpInst::ICMP_EQ, mask, threeVal); - - BasicBlock *is_float_bb = BasicBlock::Create(context, "is_float", f); - BasicBlock *isnt_float_bb = BasicBlock::Create(context, "isnt_float", f); - BasicBlock *merge_bb = BasicBlock::Create(context, "merge", f); - - BranchInst::Create(is_float_bb, isnt_float_bb, is_float, bb); - - bb = is_float_bb; - Value *is_float_val = BinaryOperator::CreateXor(val, threeVal, "", bb); -#if __LP64__ - is_float_val = new BitCastInst(is_float_val, DoubleTy, "", bb); -#else - is_float_val = new BitCastInst(is_float_val, FloatTy, "", bb); - is_float_val = new FPExtInst(is_float_val, DoubleTy, "", bb); -#endif - BranchInst::Create(merge_bb, bb); - - bb = isnt_float_bb; - Value *is_fixnum = new ICmpInst(*bb, ICmpInst::ICMP_EQ, mask, oneVal); - BasicBlock *is_fixnum_bb = BasicBlock::Create(context, "is_fixnum", f); - BranchInst::Create(is_fixnum_bb, fallback_bb, is_fixnum, bb); - - bb = is_fixnum_bb; - Value *is_fixnum_val = BinaryOperator::CreateAShr(val, twoVal, "", bb); - is_fixnum_val = new SIToFPInst(is_fixnum_val, DoubleTy, "", bb); - BranchInst::Create(merge_bb, bb); - - bb = merge_bb; - PHINode *pn = PHINode::Create(DoubleTy, "op_tmp", bb); - pn->addIncoming(is_float_val, is_float_bb); - pn->addIncoming(is_fixnum_val, is_fixnum_bb); - - return pn; -} - Value * RoxorCompiler::compile_optimized_dispatch_call(SEL sel, int argc, std::vector ¶ms) @@ -2279,324 +1928,53 @@ RoxorCompiler::compile_optimized_dispatch_call(SEL sel, int argc, return NULL; } - GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); - Value *leftVal = params[2]; // self Value *rightVal = params.back(); - VALUE leftRVal = Qundef, rightRVal = Qundef; - const bool leftIsConstant = unbox_ruby_constant(leftVal, &leftRVal); - const bool rightIsConst = unbox_ruby_constant(rightVal, &rightRVal); - - if (leftIsConstant && rightIsConst - && TYPE(leftRVal) == T_SYMBOL && TYPE(rightRVal) == T_SYMBOL) { - // Both operands are symbol constants. - if (sel == selEq || sel == selEqq || sel == selNeq) { - Value *is_redefined_val = new LoadInst(is_redefined, "", bb); - Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - is_redefined_val, ConstantInt::getFalse(context)); - - Function *f = bb->getParent(); - - BasicBlock *thenBB = BasicBlock::Create(context, "op_not_redefined", f); - BasicBlock *elseBB = BasicBlock::Create(context, "op_dispatch", f); - BasicBlock *mergeBB = BasicBlock::Create(context, "op_merge", f); - - BranchInst::Create(thenBB, elseBB, isOpRedefined, bb); - Value *thenVal = NULL; - if (sel == selEq || sel == selEqq) { - thenVal = leftRVal == rightRVal ? trueVal : falseVal; - } - else if (sel == selNeq) { - thenVal = leftRVal != rightRVal ? trueVal : falseVal; - } - else { - abort(); - } - BranchInst::Create(mergeBB, thenBB); - - bb = elseBB; - Value *elseVal = compile_dispatch_call(params); - elseBB = bb; - BranchInst::Create(mergeBB, elseBB); - - PHINode *pn = PHINode::Create(RubyObjTy, "op_tmp", mergeBB); - pn->addIncoming(thenVal, thenBB); - pn->addIncoming(elseVal, elseBB); - bb = mergeBB; - - return pn; - } - else { - return NULL; - } + Function *func = NULL; + if (sel == selPLUS) { + func = fastPlusFunc; } - - rb_vm_immediate_val_t leftImm, rightImm; - const bool leftIsImmediateConst = unbox_immediate_val(leftRVal, - &leftImm); - const bool rightIsImmediateConst = unbox_immediate_val(rightRVal, - &rightImm); - - if (leftIsImmediateConst && rightIsImmediateConst) { - Value *res_val = NULL; - - if (leftImm.is_fixnum() && rightImm.is_fixnum()) { - bool result_is_predicate = false; - long res; - if (optimized_const_immediate_op( - sel, - leftImm.long_val(), - rightImm.long_val(), - &result_is_predicate, - &res)) { - if (result_is_predicate) { - res_val = res == 1 ? trueVal : falseVal; - } - else if (FIXABLE(res)) { - if (sel == selDIV) { - // MRI compliant negative fixnum division. - long x = leftImm.long_val(); - long y = rightImm.long_val(); - if (((x < 0 && y >= 0) || (x >= 0 && y < 0)) - && (x % y) != 0) { - res--; - } - } - res_val = ConstantInt::get(RubyObjTy, LONG2FIX(res)); - } - } - } - else { - bool result_is_predicate = false; - double res; - if (optimized_const_immediate_op( - sel, - leftImm.double_val(), - rightImm.double_val(), - &result_is_predicate, - &res)) { - if (result_is_predicate) { - res_val = res == 1 ? trueVal : falseVal; - } - else { - res_val = ConstantInt::get(RubyObjTy, - DBL2FIXFLOAT(res)); - } - } - } - - if (res_val != NULL) { - Value *is_redefined_val = new LoadInst(is_redefined, "", bb); - Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - is_redefined_val, ConstantInt::getFalse(context)); - - Function *f = bb->getParent(); - - BasicBlock *thenBB = BasicBlock::Create(context, "op_not_redefined", f); - BasicBlock *elseBB = BasicBlock::Create(context, "op_dispatch", f); - BasicBlock *mergeBB = BasicBlock::Create(context, "op_merge", f); - - BranchInst::Create(thenBB, elseBB, isOpRedefined, bb); - Value *thenVal = res_val; - BranchInst::Create(mergeBB, thenBB); - - bb = elseBB; - Value *elseVal = compile_dispatch_call(params); - elseBB = bb; - BranchInst::Create(mergeBB, elseBB); - - PHINode *pn = PHINode::Create(RubyObjTy, "op_tmp", mergeBB); - pn->addIncoming(thenVal, thenBB); - pn->addIncoming(elseVal, elseBB); - bb = mergeBB; - - return pn; - } - // Can't optimize, call the dispatcher. - return NULL; + else if (sel == selMINUS) { + func = fastMinusFunc; } - else { - // Either one or both is not a constant immediate. - Value *is_redefined_val = new LoadInst(is_redefined, "", bb); - Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - is_redefined_val, ConstantInt::getFalse(context)); - - Function *f = bb->getParent(); - - BasicBlock *not_redefined_bb = - BasicBlock::Create(context, "op_not_redefined", f); - BasicBlock *optimize_fixnum_bb = - BasicBlock::Create(context, "op_optimize_fixnum", f); - BasicBlock *optimize_float_bb = - BasicBlock::Create(context, "op_optimize_float", f); - BasicBlock *dispatch_bb = - BasicBlock::Create( context, "op_dispatch", f); - BasicBlock *merge_bb = BasicBlock::Create(context, "op_merge", f); - - BranchInst::Create(not_redefined_bb, dispatch_bb, isOpRedefined, - bb); - - bb = not_redefined_bb; - - Value *leftAndOp = NULL; - if (!leftIsImmediateConst) { - leftAndOp = BinaryOperator::CreateAnd(leftVal, threeVal, "", - bb); - } - - Value *rightAndOp = NULL; - if (!rightIsImmediateConst) { - rightAndOp = BinaryOperator::CreateAnd(rightVal, threeVal, "", - bb); - } - - if (leftAndOp != NULL && rightAndOp != NULL) { - Value *leftIsFixnum = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - leftAndOp, oneVal); - BasicBlock *left_is_fixnum_bb = - BasicBlock::Create(context, "left_fixnum", f); - BranchInst::Create(left_is_fixnum_bb, optimize_float_bb, - leftIsFixnum, bb); - - bb = left_is_fixnum_bb; - Value *rightIsFixnum = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - rightAndOp, oneVal); - BranchInst::Create(optimize_fixnum_bb, optimize_float_bb, - rightIsFixnum, bb); - } - else if (leftAndOp != NULL) { - if (rightImm.is_fixnum()) { - Value *leftIsFixnum = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - leftAndOp, oneVal); - BranchInst::Create(optimize_fixnum_bb, optimize_float_bb, - leftIsFixnum, bb); - } - else { - BranchInst::Create(optimize_float_bb, bb); - } - } - else if (rightAndOp != NULL) { - if (leftImm.is_fixnum()) { - Value *rightIsFixnum = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - rightAndOp, oneVal); - BranchInst::Create(optimize_fixnum_bb, optimize_float_bb, - rightIsFixnum, bb); - } - else { - BranchInst::Create(optimize_float_bb, bb); - } - } - - bb = optimize_fixnum_bb; - - Value *unboxedLeft; - if (leftIsImmediateConst) { - unboxedLeft = ConstantInt::get(IntTy, leftImm.long_val()); - } - else { - unboxedLeft = BinaryOperator::CreateAShr(leftVal, twoVal, "", - bb); - } - - Value *unboxedRight; - if (rightIsImmediateConst) { - unboxedRight = ConstantInt::get(IntTy, rightImm.long_val()); - } - else { - unboxedRight = BinaryOperator::CreateAShr(rightVal, twoVal, "", - bb); - } - - bool result_is_predicate = false; - Value *fix_op_res = optimized_immediate_op(sel, unboxedLeft, - unboxedRight, false, &result_is_predicate); - - if (!result_is_predicate) { - // Box the fixnum. - Value *shift = BinaryOperator::CreateShl(fix_op_res, twoVal, "", - bb); - Value *boxed_op_res = BinaryOperator::CreateOr(shift, oneVal, - "", bb); - - // Is result fixable? - Value *fixnumMax = ConstantInt::get(IntTy, FIXNUM_MAX + 1); - Value *isFixnumMaxOk = new ICmpInst(*bb, ICmpInst::ICMP_SLT, - fix_op_res, fixnumMax); - - BasicBlock *fixable_max_bb = - BasicBlock::Create(context, "op_fixable_max", f); - - BranchInst::Create(fixable_max_bb, dispatch_bb, isFixnumMaxOk, - bb); - - bb = fixable_max_bb; - Value *fixnumMin = ConstantInt::get(IntTy, FIXNUM_MIN); - Value *isFixnumMinOk = new ICmpInst(*bb, ICmpInst::ICMP_SGE, - fix_op_res, fixnumMin); - - BranchInst::Create(merge_bb, dispatch_bb, isFixnumMinOk, bb); - fix_op_res = boxed_op_res; - optimize_fixnum_bb = fixable_max_bb; - } - else { - BranchInst::Create(merge_bb, bb); - } - - bb = optimize_float_bb; - - if (leftIsImmediateConst) { - unboxedLeft = ConstantFP::get(DoubleTy, leftImm.double_val()); - } - else { - unboxedLeft = compile_double_coercion(leftVal, leftAndOp, - dispatch_bb, f); - } - - if (rightIsImmediateConst) { - unboxedRight = ConstantFP::get(DoubleTy, rightImm.double_val()); - } - else { - unboxedRight = compile_double_coercion(rightVal, rightAndOp, - dispatch_bb, f); - } - - result_is_predicate = false; - Value *flp_op_res = optimized_immediate_op(sel, unboxedLeft, - unboxedRight, true, &result_is_predicate); - - if (!result_is_predicate) { - // Box the float. -#if !__LP64__ - flp_op_res = new FPTruncInst(flp_op_res, FloatTy, "", bb); -#endif - flp_op_res = new BitCastInst(flp_op_res, RubyObjTy, "", bb); - flp_op_res = BinaryOperator::CreateOr(flp_op_res, threeVal, - "", bb); - } - optimize_float_bb = bb; - BranchInst::Create(merge_bb, bb); - - bb = dispatch_bb; - Value *dispatch_val = compile_fast_op_call(sel, leftVal, rightVal); - if (dispatch_val == NULL) { - dispatch_val = compile_dispatch_call(params); - } - dispatch_bb = bb; - BranchInst::Create(merge_bb, bb); - - bb = merge_bb; - PHINode *pn = PHINode::Create(RubyObjTy, "op_tmp", bb); - pn->addIncoming(fix_op_res, optimize_fixnum_bb); - pn->addIncoming(flp_op_res, optimize_float_bb); - pn->addIncoming(dispatch_val, dispatch_bb); + else if (sel == selDIV) { + func = fastDivFunc; + } + else if (sel == selMULT) { + func = fastMultFunc; + } + else if (sel == selLT) { + func = fastLtFunc; + } + else if (sel == selLE) { + func = fastLeFunc; + } + else if (sel == selGT) { + func = fastGtFunc; + } + else if (sel == selGE) { + func = fastGeFunc; + } + else if (sel == selEq) { + func = fastEqFunc; + } + else if (sel == selNeq) { + func = fastNeqFunc; + } + else if (sel == selEqq) { + func = fastEqqFunc; + } + assert(func != NULL); -// if (sel == selEqq) { -// pn->addIncoming(fastEqqVal, fastEqqBB); -// } + std::vector params; + params.push_back(compile_mcache(sel, false)); + params.push_back(leftVal); + params.push_back(rightVal); + GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); + params.push_back(new LoadInst(is_redefined, "", bb)); - return pn; - } + return compile_protected_call(func, params); } // Other operators (#<< or #[] or #[]=) else if (sel == selLTLT || sel == selAREF || sel == selASET) { @@ -2612,24 +1990,17 @@ RoxorCompiler::compile_optimized_dispatch_call(SEL sel, int argc, return NULL; } - Function *opt_func = NULL; - + Function *func = NULL; if (sel == selLTLT) { - opt_func = cast(module->getOrInsertFunction("rb_vm_fast_shift", - RubyObjTy, RubyObjTy, RubyObjTy, PtrTy, Int1Ty, NULL)); + func = fastShiftFunc; } else if (sel == selAREF) { - opt_func = cast(module->getOrInsertFunction("rb_vm_fast_aref", - RubyObjTy, RubyObjTy, RubyObjTy, PtrTy, Int1Ty, NULL)); + func = fastArefFunc; } else if (sel == selASET) { - opt_func = cast(module->getOrInsertFunction("rb_vm_fast_aset", - RubyObjTy, RubyObjTy, RubyObjTy, RubyObjTy, PtrTy, - Int1Ty, NULL)); - } - else { - abort(); + func = fastAsetFunc; } + assert(func != NULL); std::vector new_params; new_params.push_back(params[2]); // self @@ -2644,7 +2015,7 @@ RoxorCompiler::compile_optimized_dispatch_call(SEL sel, int argc, 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); + return compile_protected_call(func, new_params); } // #send or #__send__ else if (sel == selSend || sel == sel__send__) { @@ -2666,7 +2037,7 @@ RoxorCompiler::compile_optimized_dispatch_call(SEL sel, int argc, Value *is_redefined_val = new LoadInst(is_redefined, "", bb); Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, - is_redefined_val, ConstantInt::getFalse(context)); + is_redefined_val, ConstantInt::get(Int8Ty, 0)); Function *f = bb->getParent(); @@ -2705,118 +2076,6 @@ RoxorCompiler::compile_optimized_dispatch_call(SEL sel, int argc, return pn; } -#if 0 - // XXX this optimization is disabled because it's buggy and not really - // interesting - // #eval - else if (sel == selEval) { - - if (current_block_func != NULL || argc != 1) { - return NULL; - } - Value *strVal = params.back(); - if (!ConstantInt::classof(strVal)) { - return NULL; - } - VALUE str = cast(strVal)->getZExtValue(); - if (TYPE(str) != T_STRING) { - return NULL; - } - // FIXME: - // - pass the real file/line arguments - // - catch potential parsing exceptions - NODE *new_node = rb_compile_string("", str, 0); - if (new_node == NULL) { - return NULL; - } - if (nd_type(new_node) != NODE_SCOPE || new_node->nd_body == NULL) { - return NULL; - } - - GlobalVariable *is_redefined = GET_VM()->redefined_op_gvar(sel, true); - - Value *is_redefined_val = new LoadInst(is_redefined, "", bb); - Value *isOpRedefined = new ICmpInst(ICmpInst::ICMP_EQ, - is_redefined_val, ConstantInt::getFalse(), "", bb); - - Function *f = bb->getParent(); - - BasicBlock *thenBB = BasicBlock::Create("op_not_redefined", f); - BasicBlock *elseBB = BasicBlock::Create("op_dispatch", f); - BasicBlock *mergeBB = BasicBlock::Create("op_merge", f); - - BranchInst::Create(thenBB, elseBB, isOpRedefined, bb); - - bb = thenBB; - Value *thenVal = compile_node(new_node->nd_body); - thenBB = bb; - BranchInst::Create(mergeBB, thenBB); - - bb = elseBB; - Value *elseVal = compile_dispatch_call(params); - BranchInst::Create(mergeBB, elseBB); - - bb = mergeBB; - PHINode *pn = PHINode::Create(RubyObjTy, "op_tmp", mergeBB); - pn->addIncoming(thenVal, thenBB); - pn->addIncoming(elseVal, elseBB); - - return pn; - - } -#endif -#if 0 - // TODO: block inlining optimization - else if (current_block_func != NULL) { - static SEL selTimes = 0; - if (selTimes == 0) { - selTimes = rb_intern("times"); - } - - if (sel == selTimes && argc == 0) { - Value *val = params[1]; // self - - long valLong; - if (unbox_fixnum_constant(val, &valLong)) { - GlobalVariable *is_redefined = redefined_op_gvar(sel, true); - - Value *is_redefined_val = new LoadInst(is_redefined, "", bb); - Value *isOpRedefined = new ICmpInst(ICmpInst::ICMP_EQ, is_redefined_val, ConstantInt::getFalse(), "", bb); - - Function *f = bb->getParent(); - - BasicBlock *thenBB = BasicBlock::Create("op_not_redefined", f); - BasicBlock *elseBB = BasicBlock::Create("op_dispatch", f); - BasicBlock *mergeBB = BasicBlock::Create("op_merge", f); - - BranchInst::Create(thenBB, elseBB, isOpRedefined, bb); - bb = thenBB; - - - -// Val *mem = new AllocaInst(RubyObjTy, "", bb); -// new StoreInst(zeroVal, mem, "", bb); -// Val *i = LoadInst(mem, "", bb); - - - - Value *thenVal = val; - BranchInst::Create(mergeBB, thenBB); - - Value *elseVal = dispatchCall; - elseBB->getInstList().push_back(dispatchCall); - BranchInst::Create(mergeBB, elseBB); - - PHINode *pn = PHINode::Create(Type::Int32Ty, "op_tmp", mergeBB); - pn->addIncoming(thenVal, thenBB); - pn->addIncoming(elseVal, elseBB); - bb = mergeBB; - - return pn; - } - } - } -#endif return NULL; } @@ -2978,14 +2237,8 @@ RoxorCompiler::compile_set_current_class(Value *klass) void RoxorCompiler::compile_set_current_scope(Value *klass, Value *scope) { - if (setScopeFunc == NULL) { - // void rb_vm_set_current_scope(VALUE mod, int scope) - setScopeFunc = cast( - module->getOrInsertFunction("rb_vm_set_current_scope", - VoidTy, RubyObjTy, Int32Ty, NULL)); - } - std::vector params; + params.push_back(klass); params.push_back(scope); @@ -3048,6 +2301,35 @@ RoxorCompiler::compile_keep_vars(BasicBlock *startBB, BasicBlock *mergeBB) BranchInst::Create(mergeBB, bb); } +bool +RoxorCompiler::should_inline_function(Function *f) +{ + return f->getName().startswith("vm_"); +} + +void +RoxorCompiler::inline_function_calls(Function *f) +{ + std::vector insns; + + for (Function::iterator fi = f->begin(); fi != f->end(); ++fi) { + for (BasicBlock::iterator bi = fi->begin(); bi != fi->end(); ++bi) { + CallInst *insn = dyn_cast(bi); + if (insn != NULL) { + Function *called = insn->getCalledFunction(); + if (called != NULL && should_inline_function(called)) { + insns.push_back(insn); + } + } + } + } + + for (std::vector::iterator i = insns.begin(); + i != insns.end(); ++i) { + InlineFunction(*i); + } +} + Function * RoxorCompiler::compile_scope(NODE *node) { @@ -4410,36 +3692,25 @@ RoxorCompiler::compile_node(NODE *node) return compile_const(node->nd_vid, NULL); case NODE_CDECL: - { - assert(node->nd_value != NULL); - return compile_constant_declaration(node, compile_node(node->nd_value)); - } - break; + assert(node->nd_value != NULL); + return compile_constant_declaration(node, + compile_node(node->nd_value)); case NODE_IASGN: case NODE_IASGN2: - { - assert(node->nd_vid > 0); - assert(node->nd_value != NULL); - return compile_ivar_assignment(node->nd_vid, - compile_node(node->nd_value)); - } - break; + assert(node->nd_vid > 0); + assert(node->nd_value != NULL); + return compile_ivar_assignment(node->nd_vid, + compile_node(node->nd_value)); case NODE_IVAR: - { - assert(node->nd_vid > 0); - return compile_ivar_read(node->nd_vid); - } - break; + assert(node->nd_vid > 0); + return compile_ivar_read(node->nd_vid); case NODE_LIT: case NODE_STR: - { - assert(node->nd_lit != 0); - return compile_literal(node->nd_lit); - } - break; + assert(node->nd_lit != 0); + return compile_literal(node->nd_lit); case NODE_ARGSCAT: case NODE_ARGSPUSH: @@ -4447,12 +3718,6 @@ RoxorCompiler::compile_node(NODE *node) assert(node->nd_head != NULL); Value *ary = compile_node(node->nd_head); - if (dupArrayFunc == NULL) { - dupArrayFunc = cast( - module->getOrInsertFunction("rb_ary_dup", - RubyObjTy, RubyObjTy, NULL)); - } - std::vector params; params.push_back(ary); @@ -4461,13 +3726,6 @@ RoxorCompiler::compile_node(NODE *node) assert(node->nd_body != NULL); Value *other = compile_node(node->nd_body); - if (catArrayFunc == NULL) { - // VALUE rb_vm_ary_cat(VALUE obj); - catArrayFunc = cast( - module->getOrInsertFunction("rb_vm_ary_cat", - RubyObjTy, RubyObjTy, RubyObjTy, NULL)); - } - params.clear(); params.push_back(ary); params.push_back(other); @@ -4482,13 +3740,6 @@ RoxorCompiler::compile_node(NODE *node) Value *val = compile_node(node->nd_head); if (nd_type(node->nd_head) != NODE_ARRAY) { - if (toAFunc == NULL) { - // VALUE rb_vm_to_a(VALUE obj); - toAFunc = cast( - module->getOrInsertFunction("rb_vm_to_a", - RubyObjTy, RubyObjTy, NULL)); - } - std::vector params; params.push_back(val); val = compile_protected_call(toAFunc, params); @@ -4502,46 +3753,47 @@ RoxorCompiler::compile_node(NODE *node) case NODE_ZARRAY: case NODE_VALUES: { - if (newArrayFunc == NULL) { - // VALUE rb_ary_new3(int argc, ...); - std::vector types; - types.push_back(Int32Ty); - FunctionType *ft = FunctionType::get(RubyObjTy, types, true); - newArrayFunc = cast(module->getOrInsertFunction( - "rb_ary_new3", ft)); - } - - std::vector params; + Value *ary; if (nd_type(node) == NODE_ZARRAY) { + std::vector params; params.push_back(ConstantInt::get(Int32Ty, 0)); + + ary = CallInst::Create(newArrayFunc, params.begin(), + params.end(), "", bb); } else { const int count = node->nd_alen; - NODE *n = node; - + std::vector params; params.push_back(ConstantInt::get(Int32Ty, count)); + ary = CallInst::Create(newArrayFunc, params.begin(), + params.end(), "", bb); + + NODE *n = node; for (int i = 0; i < count; i++) { assert(n->nd_head != NULL); - params.push_back(compile_node(n->nd_head)); + Value *elem = compile_node(n->nd_head); + + params.clear(); + params.push_back(ary); + params.push_back(ConstantInt::get(Int32Ty, i)); + params.push_back(elem); + + CallInst::Create(asetArrayFunc, params.begin(), + params.end(), "", bb); + n = n->nd_next; } } - return cast(CallInst::Create(newArrayFunc, params.begin(), params.end(), "", bb)); + return ary; } break; case NODE_HASH: { - if (newHashFunc == NULL) { - // VALUE rb_hash_new_fast(int argc, ...); - std::vector types; - types.push_back(Int32Ty); - FunctionType *ft = FunctionType::get(RubyObjTy, types, true); - newHashFunc = cast(module->getOrInsertFunction("rb_hash_new_fast", ft)); - } + Value *hash = CallInst::Create(newHashFunc, "", bb); std::vector params; @@ -4551,53 +3803,43 @@ RoxorCompiler::compile_node(NODE *node) assert(count % 2 == 0); NODE *n = node->nd_head; - params.push_back(ConstantInt::get(Int32Ty, count)); - for (int i = 0; i < count; i += 2) { Value *key = compile_node(n->nd_head); n = n->nd_next; Value *val = compile_node(n->nd_head); n = n->nd_next; + std::vector params; + + params.push_back(hash); params.push_back(key); params.push_back(val); + + CallInst::Create(storeHashFunc, params.begin(), + params.end(), "", bb); } } - else { - params.push_back(ConstantInt::get(Int32Ty, 0)); - } - return cast(CallInst::Create(newHashFunc, - params.begin(), params.end(), "", bb)); + return hash; } break; case NODE_DOT2: case NODE_DOT3: - { - assert(node->nd_beg != NULL); - assert(node->nd_end != NULL); - - return compile_range(compile_node(node->nd_beg), - compile_node(node->nd_end), - nd_type(node) == NODE_DOT3); - } - break; + assert(node->nd_beg != NULL); + assert(node->nd_end != NULL); + return compile_range(compile_node(node->nd_beg), + compile_node(node->nd_end), nd_type(node) == NODE_DOT3); case NODE_FLIP2: case NODE_FLIP3: - { - assert(node->nd_beg != NULL); - assert(node->nd_end != NULL); + assert(node->nd_beg != NULL); + assert(node->nd_end != NULL); - if (nd_type(node) == NODE_FLIP2) { - return compile_ff2(node); - } - else { - return compile_ff3(node); - } + if (nd_type(node) == NODE_FLIP2) { + return compile_ff2(node); } - break; + return compile_ff3(node); case NODE_BLOCK: { @@ -4606,7 +3848,8 @@ RoxorCompiler::compile_node(NODE *node) DEBUG_LEVEL_INC(); while (n != NULL && nd_type(n) == NODE_BLOCK) { - val = n->nd_head == NULL ? nilVal : compile_node(n->nd_head); + val = n->nd_head == NULL + ? nilVal : compile_node(n->nd_head); n = n->nd_next; } DEBUG_LEVEL_DEC(); @@ -4803,24 +4046,18 @@ RoxorCompiler::compile_node(NODE *node) case NODE_NTH_REF: case NODE_BACK_REF: { - char code = (char)node->nd_nth; - - if (getSpecialFunc == NULL) { - // VALUE rb_vm_get_special(char code); - getSpecialFunc = - cast(module->getOrInsertFunction("rb_vm_get_special", - RubyObjTy, Int8Ty, NULL)); - } - std::vector params; + char code = (char)node->nd_nth; params.push_back(ConstantInt::get(Int8Ty, code)); - return CallInst::Create(getSpecialFunc, params.begin(), params.end(), "", bb); + return CallInst::Create(getSpecialFunc, params.begin(), + params.end(), "", bb); } break; case NODE_BEGIN: - return node->nd_body == NULL ? nilVal : compile_node(node->nd_body); + return node->nd_body == NULL + ? nilVal : compile_node(node->nd_body); case NODE_RESCUE: { @@ -4850,7 +4087,8 @@ RoxorCompiler::compile_node(NODE *node) rescue_invoke_bb = old_rescue_invoke_bb; if (node->nd_else != NULL) { - BasicBlock *else_bb = BasicBlock::Create(context, "else", f); + BasicBlock *else_bb = BasicBlock::Create(context, "else", + f); BranchInst::Create(else_bb, bb); bb = else_bb; not_rescued_val = compile_node(node->nd_else); @@ -4859,10 +4097,12 @@ RoxorCompiler::compile_node(NODE *node) BasicBlock *not_rescued_bb = bb; BranchInst::Create(merge_bb, not_rescued_bb); - PHINode *pn = PHINode::Create(RubyObjTy, "rescue_result", merge_bb); + PHINode *pn = PHINode::Create(RubyObjTy, "rescue_result", + merge_bb); pn->addIncoming(not_rescued_val, not_rescued_bb); - if (new_rescue_invoke_bb->use_empty() && new_rescue_rethrow_bb->use_empty()) { + if (new_rescue_invoke_bb->use_empty() + && new_rescue_rethrow_bb->use_empty()) { new_rescue_invoke_bb->eraseFromParent(); new_rescue_rethrow_bb->eraseFromParent(); } @@ -5356,7 +4596,8 @@ RoxorCompiler::compile_node(NODE *node) if (rb_is_const_id(node->nd_mid)) { // Constant assert(node->nd_head != NULL); - return compile_const(node->nd_mid, compile_node(node->nd_head)); + return compile_const(node->nd_mid, + compile_node(node->nd_head)); } else { // Method call @@ -5562,7 +4803,7 @@ RoxorAOTCompiler::compile_main_function(NODE *node) } // Compile constant caches. - + Function *getConstCacheFunc = cast(module->getOrInsertFunction( "rb_vm_get_constant_cache", PtrTy, PtrTy, NULL)); @@ -5597,10 +4838,7 @@ RoxorAOTCompiler::compile_main_function(NODE *node) } // Compile selectors. - - Function *registerSelFunc = cast(module->getOrInsertFunction( - "sel_registerName", - PtrTy, PtrTy, NULL)); + Function *registerSelFunc = get_function("sel_registerName"); for (std::map::iterator i = sels.begin(); i != sels.end(); @@ -5624,9 +4862,12 @@ RoxorAOTCompiler::compile_main_function(NODE *node) Instruction *call = CallInst::Create(registerSelFunc, params.begin(), params.end(), ""); - Instruction *assign = new StoreInst(call, gvar, ""); + Instruction *cast = new BitCastInst(call, PtrTy, ""); + + Instruction *assign = new StoreInst(cast, gvar, ""); list.insert(list.begin(), assign); + list.insert(list.begin(), cast); list.insert(list.begin(), call); list.insert(list.begin(), load); } @@ -5878,10 +5119,6 @@ RoxorAOTCompiler::compile_main_function(NODE *node) // Compile constant class references. - Function *objcGetClassFunc = cast(module->getOrInsertFunction( - "objc_getClass", - RubyObjTy, PtrTy, NULL)); - for (std::vector::iterator i = class_gvars.begin(); i != class_gvars.end(); ++i) { @@ -5900,7 +5137,7 @@ RoxorAOTCompiler::compile_main_function(NODE *node) std::vector params; params.push_back(load); - Instruction *call = CallInst::Create(objcGetClassFunc, params.begin(), + Instruction *call = CallInst::Create(getClassFunc, params.begin(), params.end(), ""); Instruction *assign = new StoreInst(call, gvar, ""); @@ -5945,9 +5182,7 @@ RoxorCompiler::compile_read_attr(ID name) bb = BasicBlock::Create(context, "EntryBlock", f); - Value *val = compile_ivar_read(name); - - ReturnInst::Create(context, val, bb); + ReturnInst::Create(context, compile_ivar_read(name), bb); return f; } @@ -5965,21 +5200,38 @@ RoxorCompiler::compile_write_attr(ID name) bb = BasicBlock::Create(context, "EntryBlock", f); - if (setKVOIvarFunc == NULL) { - setKVOIvarFunc = - cast(module->getOrInsertFunction("rb_vm_set_kvo_ivar", - RubyObjTy, RubyObjTy, RubyObjTy, RubyObjTy, PtrTy, - NULL)); - } + Value *val; + if (rb_objc_enable_ivar_set_kvo_notifications) { + VALUE tmp = rb_id2str(name); + VALUE str = rb_str_substr(tmp, 1, rb_str_chars_len(tmp) - 1); - std::vector params; - params.push_back(current_self); - params.push_back(compile_id(name)); - params.push_back(new_val); - params.push_back(compile_slot_cache(name)); + std::vector params; + params.push_back(current_self); + params.push_back(compile_immutable_literal(str)); + + if (willChangeValueFunc == NULL) { + willChangeValueFunc = + cast(module->getOrInsertFunction( + "rb_objc_willChangeValueForKey", + VoidTy, RubyObjTy, RubyObjTy, NULL)); + } + CallInst::Create(willChangeValueFunc, params.begin(), params.end(), + "", bb); + + val = compile_ivar_assignment(name, new_val); - Value *val = CallInst::Create(setKVOIvarFunc, - params.begin(), params.end(), "", bb); + if (didChangeValueFunc == NULL) { + didChangeValueFunc = + cast(module->getOrInsertFunction( + "rb_objc_didChangeValueForKey", + VoidTy, RubyObjTy, RubyObjTy, NULL)); + } + CallInst::Create(didChangeValueFunc, params.begin(), params.end(), + "", bb); + } + else { + val = compile_ivar_assignment(name, new_val); + } ReturnInst::Create(context, val, bb); @@ -5996,207 +5248,6 @@ convert_error(const char type, VALUE val) type); } -extern "C" -void -rb_vm_rval_to_ocval(VALUE rval, id *ocval) -{ - *ocval = rval == Qnil ? NULL : RB2OC(rval); -} - -extern "C" -void -rb_vm_rval_to_bool(VALUE rval, BOOL *ocval) -{ - switch (TYPE(rval)) { - case T_FALSE: - case T_NIL: - *ocval = NO; - break; - - default: - // All other types should be converted as true, to follow the Ruby - // semantics (where for example any integer is always true, even 0). - *ocval = YES; - break; - } -} - -static inline const char * -rval_to_c_str(VALUE rval) -{ - if (NIL_P(rval)) { - return NULL; - } - else { - switch (TYPE(rval)) { - case T_SYMBOL: - return rb_sym2name(rval); - } - if (rb_obj_is_kind_of(rval, rb_cPointer)) { - return (const char *)rb_pointer_get_data(rval, "^c"); - } - return StringValueCStr(rval); - } -} - -extern "C" -void -rb_vm_rval_to_ocsel(VALUE rval, SEL *ocval) -{ - const char *cstr = rval_to_c_str(rval); - *ocval = cstr == NULL ? NULL : sel_registerName(cstr); -} - -extern "C" -void -rb_vm_rval_to_charptr(VALUE rval, const char **ocval) -{ - *ocval = rval_to_c_str(rval); -} - -static inline long -bool_to_fix(VALUE rval) -{ - if (rval == Qtrue) { - return INT2FIX(1); - } - if (rval == Qfalse) { - return INT2FIX(0); - } - return rval; -} - -static inline long -rval_to_long(VALUE rval) -{ - return NUM2LONG(rb_Integer(bool_to_fix(rval))); -} - -static inline long long -rval_to_long_long(VALUE rval) -{ - return NUM2LL(rb_Integer(bool_to_fix(rval))); -} - -static inline double -rval_to_double(VALUE rval) -{ - return RFLOAT_VALUE(rb_Float(bool_to_fix(rval))); -} - -extern "C" -void -rb_vm_rval_to_chr(VALUE rval, char *ocval) -{ - if (TYPE(rval) == T_STRING && RSTRING_LEN(rval) == 1) { - *ocval = (char)RSTRING_PTR(rval)[0]; - } - else { - *ocval = (char)rval_to_long(rval); - } -} - -extern "C" -void -rb_vm_rval_to_uchr(VALUE rval, unsigned char *ocval) -{ - if (TYPE(rval) == T_STRING && RSTRING_LEN(rval) == 1) { - *ocval = (unsigned char)RSTRING_PTR(rval)[0]; - } - else { - *ocval = (unsigned char)rval_to_long(rval); - } -} - -extern "C" -void -rb_vm_rval_to_short(VALUE rval, short *ocval) -{ - *ocval = (short)rval_to_long(rval); -} - -extern "C" -void -rb_vm_rval_to_ushort(VALUE rval, unsigned short *ocval) -{ - *ocval = (unsigned short)rval_to_long(rval); -} - -extern "C" -void -rb_vm_rval_to_int(VALUE rval, int *ocval) -{ - *ocval = (int)rval_to_long(rval); -} - -extern "C" -void -rb_vm_rval_to_uint(VALUE rval, unsigned int *ocval) -{ - *ocval = (unsigned int)rval_to_long(rval); -} - -extern "C" -void -rb_vm_rval_to_long(VALUE rval, long *ocval) -{ - *ocval = (long)rval_to_long(rval); -} - -extern "C" -void -rb_vm_rval_to_ulong(VALUE rval, unsigned long *ocval) -{ - *ocval = (unsigned long)rval_to_long(rval); -} - -extern "C" -void -rb_vm_rval_to_long_long(VALUE rval, long long *ocval) -{ - *ocval = (long long)rval_to_long_long(rval); -} - -extern "C" -void -rb_vm_rval_to_ulong_long(VALUE rval, unsigned long long *ocval) -{ - *ocval = (unsigned long long)rval_to_long_long(rval); -} - -extern "C" -void -rb_vm_rval_to_double(VALUE rval, double *ocval) -{ - *ocval = (double)rval_to_double(rval); -} - -extern "C" -void -rb_vm_rval_to_float(VALUE rval, float *ocval) -{ - *ocval = (float)rval_to_double(rval); -} - -extern "C" -void * -rb_vm_rval_to_cptr(VALUE rval, const char *type, void **cptr) -{ - if (NIL_P(rval)) { - *cptr = NULL; - } - else { - if (TYPE(rval) == T_ARRAY - || rb_boxed_is_type(CLASS_OF(rval), type + 1)) { - // A convenience helper so that the user can pass a Boxed or an - // Array object instead of a Pointer to the object. - rval = rb_pointer_new2(type + 1, rval); - } - *cptr = rb_pointer_get_data(rval, type); - } - return *cptr; -} - void RoxorCompiler::compile_get_struct_fields(Value *val, Value *buf, rb_vm_bs_boxed_t *bs_boxed) @@ -6259,13 +5310,8 @@ RoxorCompiler::compile_get_opaque_data(Value *val, rb_vm_bs_boxed_t *bs_boxed, Value * RoxorCompiler::compile_get_cptr(Value *val, const char *type, Value *slot) { - if (getPointerPtrFunc == NULL) { - getPointerPtrFunc = cast(module->getOrInsertFunction( - "rb_vm_rval_to_cptr", - PtrTy, RubyObjTy, PtrTy, PtrPtrTy, NULL)); - } - std::vector params; + params.push_back(val); params.push_back(compile_const_pointer(sel_registerName(type))); params.push_back(new BitCastInst(slot, PtrPtrTy, "", bb)); @@ -6277,78 +5323,78 @@ Value * RoxorCompiler::compile_conversion_to_c(const char *type, Value *val, Value *slot) { - const char *func_name = NULL; - type = SkipTypeModifiers(type); if (*type == _C_PTR && GET_CORE()->find_bs_cftype(type) != NULL) { type = "@"; } + Function *func = NULL; + switch (*type) { case _C_ID: case _C_CLASS: - func_name = "rb_vm_rval_to_ocval"; + func = rvalToOcvalFunc; break; case _C_BOOL: - func_name = "rb_vm_rval_to_bool"; + func = rvalToBoolFunc; break; case _C_CHR: - func_name = "rb_vm_rval_to_chr"; + func = rvalToCharFunc; break; case _C_UCHR: - func_name = "rb_vm_rval_to_uchr"; + func = rvalToUcharFunc; break; case _C_SHT: - func_name = "rb_vm_rval_to_short"; + func = rvalToShortFunc; break; case _C_USHT: - func_name = "rb_vm_rval_to_ushort"; + func = rvalToUshortFunc; break; case _C_INT: - func_name = "rb_vm_rval_to_int"; + func = rvalToIntFunc; break; case _C_UINT: - func_name = "rb_vm_rval_to_uint"; + func = rvalToUintFunc; break; case _C_LNG: - func_name = "rb_vm_rval_to_long"; + func = rvalToLongFunc; break; case _C_ULNG: - func_name = "rb_vm_rval_to_ulong"; + func = rvalToUlongFunc; break; case _C_LNG_LNG: - func_name = "rb_vm_rval_to_long_long"; + func = rvalToLongLongFunc; break; case _C_ULNG_LNG: - func_name = "rb_vm_rval_to_ulong_long"; + func = rvalToUlongLongFunc; break; case _C_FLT: - func_name = "rb_vm_rval_to_float"; + func = rvalToFloatFunc; break; case _C_DBL: - func_name = "rb_vm_rval_to_double"; + func = rvalToDoubleFunc; break; case _C_SEL: - func_name = "rb_vm_rval_to_ocsel"; + func = rvalToSelFunc; break; case _C_CHARPTR: - func_name = "rb_vm_rval_to_charptr"; + func = rvalToCharPtrFunc; break; case _C_STRUCT_B: @@ -6509,7 +5555,7 @@ RoxorCompiler::compile_conversion_to_c(const char *type, Value *val, break; } - if (func_name == NULL) { + if (func == NULL) { rb_raise(rb_eTypeError, "unrecognized compile type `%s' to C", type); } @@ -6517,77 +5563,10 @@ RoxorCompiler::compile_conversion_to_c(const char *type, Value *val, params.push_back(val); params.push_back(slot); - Function *func = cast(module->getOrInsertFunction( - func_name, VoidTy, RubyObjTy, - PointerType::getUnqual(convert_type(type)), NULL)); - CallInst::Create(func, params.begin(), params.end(), "", bb); return new LoadInst(slot, "", bb); } -extern "C" -VALUE -rb_vm_ocval_to_rval(id ocval) -{ - return OC2RB(ocval); -} - -extern "C" -VALUE -rb_vm_long_to_rval(long l) -{ - return INT2NUM(l); -} - -extern "C" -VALUE -rb_vm_ulong_to_rval(long l) -{ - return UINT2NUM(l); -} - -extern "C" -VALUE -rb_vm_long_long_to_rval(long long l) -{ - return LL2NUM(l); -} - -extern "C" -VALUE -rb_vm_ulong_long_to_rval(unsigned long long l) -{ - return ULL2NUM(l); -} - -extern "C" -VALUE -rb_vm_sel_to_rval(SEL sel) -{ - return sel == 0 ? Qnil : ID2SYM(rb_intern(sel_getName(sel))); -} - -extern "C" -VALUE -rb_vm_float_to_rval(float f) -{ - return DOUBLE2NUM(f); -} - -extern "C" -VALUE -rb_vm_double_to_rval(double d) -{ - return DOUBLE2NUM(d); -} - -extern "C" -VALUE -rb_vm_charptr_to_rval(const char *ptr) -{ - return ptr == NULL ? Qnil : rb_str_new2(ptr); -} - extern "C" VALUE rb_vm_new_struct(VALUE klass, int argc, ...) @@ -6703,93 +5682,82 @@ Value * RoxorCompiler::compile_conversion_to_ruby(const char *type, const Type *llvm_type, Value *val) { - const char *func_name = NULL; - type = SkipTypeModifiers(type); if (*type == _C_PTR && GET_CORE()->find_bs_cftype(type) != NULL) { type = "@"; } + Function *func = NULL; + switch (*type) { case _C_VOID: return nilVal; case _C_BOOL: - { - Value *is_true = new ICmpInst(*bb, ICmpInst::ICMP_EQ, val, - ConstantInt::get(Int8Ty, 1)); - return SelectInst::Create(is_true, trueVal, falseVal, "", bb); - } + val = new ICmpInst(*bb, ICmpInst::ICMP_EQ, val, + ConstantInt::get(Int8Ty, 1)); + return SelectInst::Create(val, trueVal, falseVal, "", bb); case _C_ID: case _C_CLASS: - func_name = "rb_vm_ocval_to_rval"; + func = ocvalToRvalFunc; break; case _C_CHR: - case _C_SHT: - case _C_INT: -#if !__LP64__ - if (*type != _C_INT) -#endif - val = new SExtInst(val, RubyObjTy, "", bb); - val = BinaryOperator::CreateShl(val, twoVal, "", bb); - val = BinaryOperator::CreateOr(val, oneVal, "", bb); - return val; + func = charToRvalFunc; + break; case _C_UCHR: + func = ucharToRvalFunc; + break; + + case _C_SHT: + func = shortToRvalFunc; + break; + case _C_USHT: + func = ushortToRvalFunc; + break; + + case _C_INT: + func = intToRvalFunc; + break; + case _C_UINT: -#if !__LP64__ - if (*type != _C_UINT) -#endif - val = new ZExtInst(val, RubyObjTy, "", bb); - val = BinaryOperator::CreateShl(val, twoVal, "", bb); - val = BinaryOperator::CreateOr(val, oneVal, "", bb); - return val; + func = uintToRvalFunc; + break; case _C_LNG: - func_name = "rb_vm_long_to_rval"; + func = longToRvalFunc; break; case _C_ULNG: - func_name = "rb_vm_ulong_to_rval"; + func = ulongToRvalFunc; break; case _C_LNG_LNG: - func_name = "rb_vm_long_long_to_rval"; + func = longLongToRvalFunc; break; case _C_ULNG_LNG: - func_name = "rb_vm_ulong_long_to_rval"; + func = ulongLongToRvalFunc; break; -#if __LP64__ - case _C_FLT: - val = new FPExtInst(val, DoubleTy, "", bb); - // fall through - case _C_DBL: - val = new BitCastInst(val, RubyObjTy, "", bb); - val = BinaryOperator::CreateOr(val, threeVal, "", bb); - return val; -#else - // TODO inline the right code for the 32-bit fixfloat optimization case _C_FLT: - func_name = "rb_vm_float_to_rval"; + func = floatToRvalFunc; break; case _C_DBL: - func_name = "rb_vm_double_to_rval"; + func = doubleToRvalFunc; break; -#endif case _C_SEL: - func_name = "rb_vm_sel_to_rval"; + func = selToRvalFunc; break; case _C_CHARPTR: - func_name = "rb_vm_charptr_to_rval"; + func = charPtrToRvalFunc; break; case _C_STRUCT_B: @@ -6839,18 +5807,17 @@ RoxorCompiler::compile_conversion_to_ruby(const char *type, break; } - if (func_name == NULL) { + if (func == NULL) { rb_raise(rb_eTypeError, "unrecognized compile type `%s' to Ruby", type); abort(); } std::vector params; - params.push_back(val); - Function *func = cast(module->getOrInsertFunction( - func_name, RubyObjTy, llvm_type, NULL)); + params.push_back(val); - return CallInst::Create(func, params.begin(), params.end(), "", bb); + return CallInst::Create(func, params.begin(), + params.end(), "", bb); } const Type * diff --git a/compiler.h b/compiler.h index f1170e5b4..1465d2c77 100644 --- a/compiler.h +++ b/compiler.h @@ -9,8 +9,6 @@ #ifndef __COMPILER_H_ #define __COMPILER_H_ -#if defined(__cplusplus) - // For the dispatcher. #define DISPATCH_VCALL 1 // no receiver, no argument #define DISPATCH_FCALL 2 // no receiver, one or more arguments @@ -31,6 +29,8 @@ #define DEFINED_SUPER 6 #define DEFINED_METHOD 7 +#if defined(__cplusplus) + class RoxorCompiler { public: static llvm::Module *module; @@ -60,6 +60,8 @@ class RoxorCompiler { Function *compile_block_caller(rb_vm_block_t *block); Function *compile_mri_stub(void *imp, const int arity); + void inline_function_calls(Function *f); + const Type *convert_type(const char *type); bool is_inside_eval(void) { return inside_eval; } @@ -138,6 +140,9 @@ class RoxorCompiler { Function *fastEqFunc; Function *fastNeqFunc; Function *fastEqqFunc; + Function *fastArefFunc; + Function *fastAsetFunc; + Function *fastShiftFunc; Function *whenSplatFunc; Function *prepareBlockFunc; Function *pushBindingFunc; @@ -151,17 +156,20 @@ class RoxorCompiler { Function *prepareIvarSlotFunc; Function *getIvarFunc; Function *setIvarFunc; - Function *setKVOIvarFunc; + Function *willChangeValueFunc; + Function *didChangeValueFunc; Function *definedFunc; Function *undefFunc; Function *aliasFunc; Function *valiasFunc; Function *newHashFunc; + Function *storeHashFunc; Function *toAFunc; Function *toAryFunc; Function *catArrayFunc; Function *dupArrayFunc; Function *newArrayFunc; + Function *asetArrayFunc; Function *newStructFunc; Function *newOpaqueFunc; Function *newPointerFunc; @@ -196,8 +204,6 @@ class RoxorCompiler { Function *returnedFromBlockFunc; Function *checkReturnFromBlockFunc; Function *setHasEnsureFunc; - Function *longjmpFunc; - Function *setjmpFunc; Function *setScopeFunc; Function *setCurrentClassFunc; Function *getCacheFunc; @@ -205,6 +211,37 @@ class RoxorCompiler { Function *getFFStateFunc; Function *setFFStateFunc; Function *takeOwnershipFunc; + Function *ocvalToRvalFunc; + Function *charToRvalFunc; + Function *ucharToRvalFunc; + Function *shortToRvalFunc; + Function *ushortToRvalFunc; + Function *intToRvalFunc; + Function *uintToRvalFunc; + Function *longToRvalFunc; + Function *ulongToRvalFunc; + Function *longLongToRvalFunc; + Function *ulongLongToRvalFunc; + Function *floatToRvalFunc; + Function *doubleToRvalFunc; + Function *selToRvalFunc; + Function *charPtrToRvalFunc; + Function *rvalToOcvalFunc; + Function *rvalToBoolFunc; + Function *rvalToCharFunc; + Function *rvalToUcharFunc; + Function *rvalToShortFunc; + Function *rvalToUshortFunc; + Function *rvalToIntFunc; + Function *rvalToUintFunc; + Function *rvalToLongFunc; + Function *rvalToUlongFunc; + Function *rvalToLongLongFunc; + Function *rvalToUlongLongFunc; + Function *rvalToFloatFunc; + Function *rvalToDoubleFunc; + Function *rvalToSelFunc; + Function *rvalToCharPtrFunc; Constant *zeroVal; Constant *oneVal; @@ -239,6 +276,16 @@ class RoxorCompiler { void compile_node_error(const char *msg, NODE *node); + Function * + get_function(const char *name) { + Function *f = module->getFunction(name); + if (f == NULL) { + printf("function %s cannot be found!\n", name); + abort(); + } + return f; + } + virtual Constant * compile_const_pointer(void *ptr, const PointerType *type=NULL) { if (type == NULL) { @@ -258,6 +305,8 @@ class RoxorCompiler { return compile_const_pointer(ptr, PtrPtrTy); } + bool should_inline_function(Function *f); + Function *compile_scope(NODE *node); Instruction *compile_protected_call(Value *imp, std::vector ¶ms); @@ -276,7 +325,6 @@ class RoxorCompiler { NODE *body); Value *compile_dispatch_call(std::vector ¶ms); Value *compile_when_splat(Value *comparedToVal, Value *splatVal); - Value *compile_fast_op_call(SEL sel, Value *selfVal, Value *comparedToVal); Value *compile_attribute_assign(NODE *node, Value *extra_val); virtual Value *compile_prepare_block_args(Function *func, int *flags); Value *compile_block_create(void); @@ -353,26 +401,19 @@ class RoxorCompiler { Value *compile_xmalloc(size_t len); Value *compile_conversion_to_c(const char *type, Value *val, - Value *slot); + Value *slot); Value *compile_conversion_to_ruby(const char *type, - const Type *llvm_type, Value *val); + const Type *llvm_type, Value *val); void compile_debug_trap(void); virtual Value *compile_slot_cache(ID id); - ICmpInst *is_value_a_fixnum(Value *val); - void compile_ivar_slots(Value *klass, BasicBlock::InstListType &list, - BasicBlock::InstListType::iterator iter); - bool unbox_ruby_constant(Value *val, VALUE *rval); - Value *optimized_immediate_op(SEL sel, Value *leftVal, Value *rightVal, - bool float_op, bool *is_predicate); - Value *compile_double_coercion(Value *val, Value *mask, - BasicBlock *fallback_bb, Function *f); void compile_keep_vars(BasicBlock *startBB, BasicBlock *mergeBB); SEL mid_to_sel(ID mid, int arity); Value *compile_get_ffstate(GlobalVariable *ffstate); - Value *compile_set_ffstate(Value *val, Value *expected, GlobalVariable *ffstate, BasicBlock *mergeBB, Function *f); + Value *compile_set_ffstate(Value *val, Value *expected, + GlobalVariable *ffstate, BasicBlock *mergeBB, Function *f); Value *compile_ff2(NODE *current_node); Value *compile_ff3(NODE *current_node); diff --git a/dispatcher.cpp b/dispatcher.cpp index 78dc6cc18..f6eb994e3 100644 --- a/dispatcher.cpp +++ b/dispatcher.cpp @@ -1100,305 +1100,6 @@ rb_vm_call_with_cache2(void *cache, rb_vm_block_t *block, VALUE self, (Class)klass, sel, block, DISPATCH_FCALL, argc, argv); } -// The rb_vm_fast_* functions don't check if the selector has been redefined or -// not, because this is already handled by the compiler. -// Also, fixnums and floats are already handled. - -extern "C" { - VALUE rb_fix_plus(VALUE x, VALUE y); - VALUE rb_fix_minus(VALUE x, VALUE y); - VALUE rb_fix_div(VALUE x, VALUE y); - VALUE rb_fix_mul(VALUE x, VALUE y); - VALUE rb_flo_plus(VALUE x, VALUE y); - VALUE rb_flo_minus(VALUE x, VALUE y); - VALUE rb_flo_div(VALUE x, VALUE y); - VALUE rb_flo_mul(VALUE x, VALUE y); - VALUE rb_nu_plus(VALUE x, VALUE y); - VALUE rb_nu_minus(VALUE x, VALUE y); - VALUE rb_nu_div(VALUE x, VALUE y); - VALUE rb_nu_mul(VALUE x, VALUE y); -} - -extern "C" -VALUE -rb_vm_fast_plus(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - // TODO: Array, String - case T_BIGNUM: - return rb_big_plus(self, other); - case T_FIXNUM: - return rb_fix_plus(self, other); - case T_FLOAT: - return rb_flo_plus(self, other); - case T_COMPLEX: - return rb_nu_plus(self, other); - } - return rb_vm_dispatch(cache, 0, self, selPLUS, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_minus(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - // TODO: Array, String - case T_BIGNUM: - return rb_big_minus(self, other); - case T_FIXNUM: - return rb_fix_minus(self, other); - case T_FLOAT: - return rb_flo_minus(self, other); - case T_COMPLEX: - return rb_nu_minus(self, other); - } - return rb_vm_dispatch(cache, 0, self, selMINUS, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_div(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - case T_BIGNUM: - return rb_big_div(self, other); - case T_FIXNUM: - return rb_fix_div(self, other); - case T_FLOAT: - return rb_flo_div(self, other); - case T_COMPLEX: - return rb_nu_div(self, other); - } - return rb_vm_dispatch(cache, 0, self, selDIV, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_mult(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - // TODO: Array, String - case T_BIGNUM: - return rb_big_mul(self, other); - case T_FIXNUM: - return rb_fix_mul(self, other); - case T_FLOAT: - return rb_flo_mul(self, other); - case T_COMPLEX: - return rb_nu_mul(self, other); - } - return rb_vm_dispatch(cache, 0, self, selMULT, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_lt(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - case T_BIGNUM: - return FIX2INT(rb_big_cmp(self, other)) < 0 ? Qtrue : Qfalse; - } - return rb_vm_dispatch(cache, 0, self, selLT, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_le(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - case T_BIGNUM: - return FIX2INT(rb_big_cmp(self, other)) <= 0 ? Qtrue : Qfalse; - } - return rb_vm_dispatch(cache, 0, self, selLE, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_gt(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - case T_BIGNUM: - return FIX2INT(rb_big_cmp(self, other)) > 0 ? Qtrue : Qfalse; - } - return rb_vm_dispatch(cache, 0, self, selGT, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_ge(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - case T_BIGNUM: - return FIX2INT(rb_big_cmp(self, other)) >= 0 ? Qtrue : Qfalse; - } - return rb_vm_dispatch(cache, 0, self, selGE, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_eq(struct mcache *cache, VALUE self, VALUE other) -{ - const int self_type = TYPE(self); - switch (self_type) { - case T_SYMBOL: - return self == other ? Qtrue : Qfalse; - - case T_STRING: - if (self == other) { - return Qtrue; - } - if (TYPE(other) != self_type) { - return Qfalse; - } - return rb_str_equal(self, other); - - case T_ARRAY: - if (self == other) { - return Qtrue; - } - if (TYPE(other) != self_type) { - return Qfalse; - } - return rb_ary_equal(self, other); - - case T_HASH: - if (self == other) { - return Qtrue; - } - if (TYPE(other) != self_type) { - return Qfalse; - } - return rb_hash_equal(self, other); - - case T_BIGNUM: - return rb_big_eq(self, other); - } - return rb_vm_dispatch(cache, 0, self, selEq, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_neq(struct mcache *cache, VALUE self, VALUE other) -{ - // TODO - return rb_vm_dispatch(cache, 0, self, selNeq, NULL, 0, 1, other); -} - -extern "C" -VALUE -rb_vm_fast_eqq(struct mcache *cache, VALUE self, VALUE other) -{ - switch (TYPE(self)) { - // TODO: Range - case T_STRING: - if (self == other) { - return Qtrue; - } - return rb_str_equal(self, other); - - case T_REGEXP: - return regexp_eqq(self, selEqq, other); - - case T_SYMBOL: - return (self == other ? Qtrue : Qfalse); - - case T_MODULE: - case T_CLASS: - return rb_obj_is_kind_of(other, self); - - default: - return rb_vm_dispatch(cache, 0, self, selEqq, NULL, 0, 1, other); - } -} - -extern "C" -VALUE -rb_vm_when_splat(struct mcache *cache, unsigned char overriden, - VALUE comparedTo, VALUE splat) -{ - VALUE ary = rb_check_convert_type(splat, T_ARRAY, "Array", "to_a"); - if (NIL_P(ary)) { - ary = rb_ary_new3(1, splat); - } - int count = RARRAY_LEN(ary); - if (overriden == 0) { - for (int i = 0; i < count; ++i) { - VALUE o = RARRAY_AT(ary, i); - if (RTEST(rb_vm_fast_eqq(cache, o, comparedTo))) { - return Qtrue; - } - } - } - else { - for (int i = 0; i < count; ++i) { - VALUE o = RARRAY_AT(ary, i); - if (RTEST(rb_vm_dispatch(cache, 0, o, selEqq, NULL, 0, 1, - comparedTo))) { - return Qtrue; - } - } - } - return Qfalse; -} - -extern "C" -VALUE -rb_vm_fast_shift(VALUE obj, VALUE other, struct mcache *cache, - unsigned char overriden) -{ - if (overriden == 0) { - VALUE klass = CLASS_OF(obj); - if (klass == rb_cRubyArray) { - return rary_push_m(obj, 0, other); - } - else if (klass == rb_cRubyString) { - return rstr_concat(obj, 0, other); - } - } - return __rb_vm_dispatch(GET_VM(), cache, 0, obj, NULL, selLTLT, NULL, 0, 1, - &other); -} - -extern "C" -VALUE -rb_vm_fast_aref(VALUE obj, VALUE other, struct mcache *cache, - unsigned char overriden) -{ - if (overriden == 0) { - VALUE klass = CLASS_OF(obj); - if (klass == rb_cRubyArray) { - return rary_aref(obj, 0, 1, &other); - } - else if (klass == rb_cRubyHash) { - return rhash_aref(obj, 0, other); - } - } - return __rb_vm_dispatch(GET_VM(), cache, 0, obj, NULL, selAREF, NULL, 0, 1, - &other); -} - -extern "C" -VALUE -rb_vm_fast_aset(VALUE obj, VALUE other1, VALUE other2, struct mcache *cache, - unsigned char overriden) -{ - if (overriden == 0) { - VALUE klass = CLASS_OF(obj); - if (klass == rb_cRubyArray) { - if (TYPE(other1) == T_FIXNUM) { - rary_store(obj, FIX2LONG(other1), other2); - return other2; - } - } - else if (klass == rb_cRubyHash) { - return rhash_aset(obj, 0, other1, other2); - } - } - VALUE args[2] = { other1, other2 }; - return __rb_vm_dispatch(GET_VM(), cache, 0, obj, NULL, selASET, NULL, 0, 2, - args); -} - static rb_vm_block_t * dup_block(rb_vm_block_t *src_b) { diff --git a/exported_symbols_list b/exported_symbols_list index 43557ca38..f754e8554 100644 --- a/exported_symbols_list +++ b/exported_symbols_list @@ -4,3 +4,24 @@ _macruby_main .objc_class_name_MacRuby _LLVMLinkInJIT ___auto_zone +_st_* + +# used by the kernel: +_rary_reserve +_rhash_call_default +_rhash_aset +_rstr_concat +_selPLUS +_selMINUS +_selDIV +_selMULT +_selLT +_selLE +_selGT +_selGE +_selEq +_selNeq +_selEqq +_selLTLT +_selAREF +_selASET diff --git a/hash.c b/hash.c index 7277f548f..08a8923e3 100644 --- a/hash.c +++ b/hash.c @@ -428,6 +428,11 @@ rhash_rehash(VALUE hash, SEL sel) * */ +VALUE +rhash_call_default(VALUE hash, VALUE key) +{ + return rb_vm_call_with_cache(defaultCache, hash, selDefault, 1, &key); +} VALUE rhash_aref(VALUE hash, SEL sel, VALUE key) @@ -438,8 +443,7 @@ rhash_aref(VALUE hash, SEL sel, VALUE key) && RHASH(hash)->ifnone == Qnil) { return Qnil; } - return rb_vm_call_with_cache(defaultCache, hash, selDefault, - 1, &key); + return rhash_call_default(hash, key); } return val; } @@ -902,8 +906,8 @@ rhash_aset(VALUE hash, SEL sel, VALUE key, VALUE val) { rhash_modify(hash); if (TYPE(key) == T_STRING) { - key = rb_str_dup(key); - OBJ_FREEZE(key); + key = rb_str_dup(key); + OBJ_FREEZE(key); } st_insert(RHASH(hash)->tbl, key, val); return val; diff --git a/hash.h b/hash.h index 260cdbaaa..f4f8d1061 100644 --- a/hash.h +++ b/hash.h @@ -72,6 +72,18 @@ rhash_lookup(VALUE hash, VALUE key) return Qundef; } +static inline VALUE +rhash_store(VALUE hash, VALUE key, VALUE val) +{ + rhash_modify(hash); + if (TYPE(key) == T_STRING) { + key = rb_str_dup(key); + OBJ_FREEZE(key); + } + st_insert(RHASH(hash)->tbl, key, val); + return val; +} + static inline VALUE rhash_delete_key(VALUE hash, VALUE key) { @@ -87,6 +99,7 @@ VALUE rhash_aref(VALUE hash, SEL sel, VALUE key); VALUE rhash_aset(VALUE hash, SEL sel, VALUE key, VALUE val); VALUE rhash_keys(VALUE hash, SEL sel); VALUE rhash_has_key(VALUE hash, SEL sel, VALUE key); +VALUE rhash_call_default(VALUE hash, VALUE key); VALUE rhash_set_default(VALUE hash, SEL sel, VALUE ifnone); #if defined(__cplusplus) diff --git a/include/ruby/intern.h b/include/ruby/intern.h index 57616a267..171c89701 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -85,10 +85,6 @@ VALUE rb_big_clone(VALUE); void rb_big_2comp(VALUE); VALUE rb_big_norm(VALUE); void rb_big_resize(VALUE big, long len); -VALUE rb_uint2big(VALUE); -VALUE rb_int2big(SIGNED_VALUE); -VALUE rb_uint2inum(VALUE); -VALUE rb_int2inum(SIGNED_VALUE); VALUE rb_cstr_to_inum(const char*, int, int); VALUE rb_str_to_inum(VALUE, int, int); VALUE rb_cstr2inum(const char*, int); @@ -102,8 +98,6 @@ SIGNED_VALUE rb_big2long(VALUE); VALUE rb_big2ulong(VALUE); #define rb_big2uint(x) rb_big2ulong(x) #if HAVE_LONG_LONG -VALUE rb_ll2inum(LONG_LONG); -VALUE rb_ull2inum(unsigned LONG_LONG); LONG_LONG rb_big2ll(VALUE); unsigned LONG_LONG rb_big2ull(VALUE); #endif /* HAVE_LONG_LONG */ diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 058aab57c..b08a00804 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -176,11 +176,9 @@ typedef unsigned LONG_LONG ID; #define INT2FIX(i) ((VALUE)(((SIGNED_VALUE)(i))<<2 | FIXNUM_FLAG)) #define LONG2FIX(i) INT2FIX(i) #define rb_fix_new(v) INT2FIX(v) -VALUE rb_int2inum(SIGNED_VALUE); #define INT2NUM(v) rb_int2inum(v) #define LONG2NUM(v) INT2NUM(v) #define rb_int_new(v) rb_int2inum(v) -VALUE rb_uint2inum(VALUE); #define UINT2NUM(v) rb_uint2inum(v) #define ULONG2NUM(v) UINT2NUM(v) #define rb_uint_new(v) rb_uint2inum(v) @@ -188,9 +186,7 @@ VALUE rb_uint2inum(VALUE); #define TIMET2NUM(t) LONG2NUM(t) #ifdef HAVE_LONG_LONG -VALUE rb_ll2inum(LONG_LONG); #define LL2NUM(v) rb_ll2inum(v) -VALUE rb_ull2inum(unsigned LONG_LONG); #define ULL2NUM(v) rb_ull2inum(v) #endif @@ -238,7 +234,6 @@ VALUE rb_ull2inum(unsigned LONG_LONG); #define IMMEDIATE_P(x) ((VALUE)(x) & IMMEDIATE_MASK) - #if __LP64__ #define VOODOO_DOUBLE(d) (*(VALUE*)(&d)) #define DBL2FIXFLOAT(d) (VOODOO_DOUBLE(d) | FIXFLOAT_FLAG) @@ -274,7 +269,8 @@ enum ruby_special_consts { // We can't directly cast a void* to a double, so we cast it to a union // and then extract its double member. Hacky, but effective. -static inline double coerce_ptr_to_double(VALUE v) +static inline double +coerce_ptr_to_double(VALUE v) { union { VALUE val; @@ -301,6 +297,47 @@ static inline double coerce_ptr_to_double(VALUE v) #define CLASS_OF(v) rb_class_of((VALUE)(v)) +VALUE rb_uint2big(VALUE); +VALUE rb_int2big(SIGNED_VALUE); +VALUE rb_ull2big(unsigned long long n); +VALUE rb_ll2big(long long n); + +static inline VALUE +rb_uint2inum(VALUE n) +{ + if (POSFIXABLE(n)) { + return LONG2FIX(n); + } + return rb_uint2big(n); +} + +static inline VALUE +rb_int2inum(SIGNED_VALUE n) +{ + if (FIXABLE(n)) { + return LONG2FIX(n); + } + return rb_int2big(n); +} + +static inline VALUE +rb_ull2inum(unsigned long long n) +{ + if (POSFIXABLE(n)) { + return LONG2FIX(n); + } + return rb_ull2big(n); +} + +static inline VALUE +rb_ll2inum(long long n) +{ + if (FIXABLE(n)) { + return LONG2FIX(n); + } + return rb_ll2big(n); +} + enum ruby_value_type { RUBY_T_NONE = 0x00, @@ -426,7 +463,12 @@ unsigned long rb_fix2uint(VALUE); #ifdef HAVE_LONG_LONG LONG_LONG rb_num2ll(VALUE); unsigned LONG_LONG rb_num2ull(VALUE); -# define NUM2LL(x) (FIXNUM_P(x)?FIX2LONG(x):rb_num2ll((VALUE)x)) +static inline long long +__num2ll(VALUE obj) +{ + return FIXNUM_P(obj) ? FIX2LONG(obj) : rb_num2ll(obj); +} +# define NUM2LL(x) __num2ll((VALUE)x) # define NUM2ULL(x) rb_num2ull((VALUE)x) #endif diff --git a/kernel.c b/kernel.c new file mode 100644 index 000000000..bd2beb4ba --- /dev/null +++ b/kernel.c @@ -0,0 +1,853 @@ +/* + * MacRuby kernel. This file is compiled into LLVM bitcode and injected into + * the global module. Some of the functions defined here are inlined in the + * code MacRuby generates. + * + * This file is covered by the Ruby license. See COPYING for more details. + * + * Copyright (C) 2010, Apple Inc. All rights reserved. + */ + +#include "ruby/ruby.h" +#include "ruby/node.h" +#include "vm.h" +#include "compiler.h" +#include "bridgesupport.h" +#include "id.h" +#include "array.h" +#include "hash.h" +#include "encoding.h" +#include "class.h" +#include "objc.h" + +inline VALUE +vm_ivar_get(VALUE obj, ID name, void *cache_p) +{ + struct icache *cache = (struct icache *)cache_p; + VALUE klass = 0; + + if (!SPECIAL_CONST_P(obj)) { + klass = *(VALUE *)obj; + if (klass == cache->klass) { + if ((unsigned int)cache->slot < ROBJECT(obj)->num_slots) { + rb_object_ivar_slot_t *slot; +use_slot: + slot = &ROBJECT(obj)->slots[cache->slot]; + if (slot->name == name) { + VALUE val = slot->value; + if (val == Qundef) { + val = Qnil; + } + return val; + } + } + } + } + + if (cache->slot == SLOT_CACHE_VIRGIN) { + const int slot = rb_vm_get_ivar_slot(obj, name, true); + if (slot >= 0) { + cache->klass = *(VALUE *)obj; + cache->slot = slot; + goto use_slot; + } + cache->klass = 0; + cache->slot = SLOT_CACHE_CANNOT; + } + + return rb_ivar_get(obj, name); +} + +inline void +vm_ivar_set(VALUE obj, ID name, VALUE val, void *cache_p) +{ + struct icache *cache = (struct icache *)cache_p; + VALUE klass = 0; + + if (!SPECIAL_CONST_P(obj)) { + klass = *(VALUE *)obj; + if (klass == cache->klass) { + if ((unsigned int)cache->slot < ROBJECT(obj)->num_slots) { + rb_object_ivar_slot_t *slot; +use_slot: + slot = &ROBJECT(obj)->slots[cache->slot]; + if (slot->name == name) { + if ((ROBJECT(obj)->basic.flags & FL_FREEZE) == FL_FREEZE) { + rb_error_frozen("object"); + } + GC_WB(&slot->value, val); + return; + } + } + } + } + + if (cache->slot == SLOT_CACHE_VIRGIN) { + const int slot = rb_vm_get_ivar_slot(obj, name, true); + if (slot >= 0) { + cache->klass = *(VALUE *)obj; + cache->slot = slot; + goto use_slot; + } + cache->slot = SLOT_CACHE_CANNOT; + } + + rb_ivar_set(obj, name, val); +} + +inline VALUE +vm_cvar_get(VALUE klass, ID id, unsigned char check, + unsigned char dynamic_class) +{ + if (dynamic_class) { + Class k = rb_vm_get_current_class(); + if (k != NULL) { + klass = (VALUE)k; + } + } + return rb_cvar_get2(klass, id, check); +} + +inline VALUE +vm_cvar_set(VALUE klass, ID id, VALUE val, unsigned char dynamic_class) +{ + if (dynamic_class) { + Class k = rb_vm_get_current_class(); + if (k != NULL) { + klass = (VALUE)k; + } + } + rb_cvar_set(klass, id, val); + return val; +} + +inline VALUE +vm_get_const(VALUE outer, void *cache_p, ID path, int flags) +{ + struct ccache *cache = (struct ccache *) cache_p; + const bool lexical_lookup = (flags & CONST_LOOKUP_LEXICAL); + const bool dynamic_class = (flags & CONST_LOOKUP_DYNAMIC_CLASS); + + if (dynamic_class) { + Class k = rb_vm_get_current_class(); + if (lexical_lookup && k != NULL) { + outer = (VALUE)k; + } + } + + VALUE val; + if (cache->outer == outer && cache->val != Qundef) { + val = cache->val; + } + else { + val = rb_vm_const_lookup(outer, path, lexical_lookup, false); + cache->outer = outer; + cache->val = val; + } + + return val; +} + +inline void +vm_set_const(VALUE outer, ID id, VALUE obj, unsigned char dynamic_class) +{ + if (dynamic_class) { + Class k = rb_vm_get_current_class(); + if (k != NULL) { + outer = (VALUE)k; + } + } + rb_const_set(outer, id, obj); +} + +VALUE rb_vm_dispatch(void *cache, VALUE top, VALUE self, void *sel, + void *block, unsigned char opt, int argc, ...); + +// Only numeric immediates have their lsb at 1. +#define NUMERIC_IMM_P(x) ((x & 0x1) == 0x1) + +#define IMM2DBL(x) (FIXFLOAT_P(x) ? FIXFLOAT2DBL(x) : FIX2LONG(x)) + +inline VALUE +vm_fast_plus(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + const long res = FIX2LONG(left) + FIX2LONG(right); + if (FIXABLE(res)) { + return LONG2FIX(res); + } + } + else { + const double res = IMM2DBL(left) + IMM2DBL(right); + return DBL2FIXFLOAT(res); + } + } + return rb_vm_dispatch(cache, 0, left, selPLUS, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_minus(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + const long res = FIX2LONG(left) - FIX2LONG(right); + if (FIXABLE(res)) { + return LONG2FIX(res); + } + } + else { + const double res = IMM2DBL(left) - IMM2DBL(right); + return DBL2FIXFLOAT(res); + } + } + return rb_vm_dispatch(cache, 0, left, selMINUS, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_mult(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + const long res = FIX2LONG(left) * FIX2LONG(right); + if (FIXABLE(res)) { + return LONG2FIX(res); + } + } + else { + const double res = IMM2DBL(left) * IMM2DBL(right); + return DBL2FIXFLOAT(res); + } + } + return rb_vm_dispatch(cache, 0, left, selMULT, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_div(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + const long x = FIX2LONG(left); + const long y = FIX2LONG(right); + if (y != 0) { + long res = x / y; + if (((x < 0 && y >= 0) || (x >= 0 && y < 0)) + && (x % y) != 0) { + res--; + } + if (FIXABLE(res)) { + return LONG2FIX(res); + } + } + } + else { + const double res = IMM2DBL(left) / IMM2DBL(right); + return DBL2FIXFLOAT(res); + } + } + return rb_vm_dispatch(cache, 0, left, selDIV, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_lt(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + return FIX2LONG(left) < FIX2LONG(right) ? Qtrue : Qfalse; + } + else { + return IMM2DBL(left) < IMM2DBL(right) ? Qtrue : Qfalse; + } + } + return rb_vm_dispatch(cache, 0, left, selLT, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_le(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + return FIX2LONG(left) <= FIX2LONG(right) ? Qtrue : Qfalse; + } + else { + return IMM2DBL(left) <= IMM2DBL(right) ? Qtrue : Qfalse; + } + } + return rb_vm_dispatch(cache, 0, left, selLE, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_gt(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + return FIX2LONG(left) > FIX2LONG(right) ? Qtrue : Qfalse; + } + else { + return IMM2DBL(left) > IMM2DBL(right) ? Qtrue : Qfalse; + } + } + return rb_vm_dispatch(cache, 0, left, selGT, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_ge(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0 && NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + return FIX2LONG(left) >= FIX2LONG(right) ? Qtrue : Qfalse; + } + else { + return IMM2DBL(left) >= IMM2DBL(right) ? Qtrue : Qfalse; + } + } + return rb_vm_dispatch(cache, 0, left, selGE, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_eq(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0) { + if (NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + return FIX2LONG(left) == FIX2LONG(right) ? Qtrue : Qfalse; + } + else { + return IMM2DBL(left) == IMM2DBL(right) ? Qtrue : Qfalse; + } + } + if (left == Qtrue || left == Qfalse) { + return left == right ? Qtrue : Qfalse; + } + // TODO: opt for non-immediate types + } + return rb_vm_dispatch(cache, 0, left, selEq, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_eqq(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0) { + if (NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + return FIX2LONG(left) == FIX2LONG(right) ? Qtrue : Qfalse; + } + else { + return IMM2DBL(left) == IMM2DBL(right) ? Qtrue : Qfalse; + } + } + if (left == Qtrue || left == Qfalse) { + return left == right ? Qtrue : Qfalse; + } + // TODO: opt for non-immediate types + } + return rb_vm_dispatch(cache, 0, left, selEqq, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_neq(void *cache, VALUE left, VALUE right, unsigned char overriden) +{ + if (overriden == 0) { + if (NUMERIC_IMM_P(left) && NUMERIC_IMM_P(right)) { + if (FIXNUM_P(left) && FIXNUM_P(right)) { + return FIX2LONG(left) != FIX2LONG(right) ? Qtrue : Qfalse; + } + else { + return IMM2DBL(left) != IMM2DBL(right) ? Qtrue : Qfalse; + } + } + if (left == Qtrue || left == Qfalse) { + return left != right ? Qtrue : Qfalse; + } + // TODO: opt for non-immediate types + } + return rb_vm_dispatch(cache, 0, left, selNeq, NULL, 0, 1, right); +} + +inline VALUE +vm_fast_aref(VALUE obj, VALUE other, void *cache, unsigned char overriden) +{ + if (overriden == 0 && !SPECIAL_CONST_P(obj)) { + VALUE klass = *(VALUE *)obj; + if (klass == rb_cRubyArray) { + if (FIXNUM_P(other)) { + return rary_entry(obj, FIX2LONG(other)); + } + } + else if (klass == rb_cRubyHash) { + VALUE val = rhash_lookup(obj, other); + if (val == Qundef) { + if (RHASH(obj)->ifnone == Qnil) { + return Qnil; + } + return rhash_call_default(obj, other); + } + return val; + } + } + return rb_vm_dispatch(cache, 0, obj, selAREF, NULL, 0, 1, other); +} + +inline VALUE +vm_fast_aset(VALUE obj, VALUE other1, VALUE other2, void *cache, + unsigned char overriden) +{ + if (overriden == 0 && !SPECIAL_CONST_P(obj)) { + VALUE klass = *(VALUE *)obj; + if (klass == rb_cRubyArray) { + if (FIXNUM_P(other1)) { + rary_store(obj, FIX2LONG(other1), other2); + return other2; + } + } + else if (klass == rb_cRubyHash) { + return rhash_aset(obj, 0, other1, other2); + } + } + return rb_vm_dispatch(cache, 0, obj, selASET, NULL, 0, 2, other1, other2); +} + +inline VALUE +vm_fast_shift(VALUE obj, VALUE other, void *cache, unsigned char overriden) +{ + if (overriden == 0 && !SPECIAL_CONST_P(obj)) { + VALUE klass = *(VALUE *)obj; + if (klass == rb_cRubyArray) { + rary_modify(obj); + rary_push(obj, other); + return obj; + } + else if (klass == rb_cRubyString) { + return rstr_concat(obj, 0, other); + } + } + return rb_vm_dispatch(cache, 0, obj, selLTLT, NULL, 0, 1, other); +} + +inline VALUE +vm_when_splat(void *cache, unsigned char overriden, + VALUE comparedTo, VALUE splat) +{ + VALUE ary = rb_check_convert_type(splat, T_ARRAY, "Array", "to_a"); + if (NIL_P(ary)) { + ary = rb_ary_new3(1, splat); + } + long i, count = RARRAY_LEN(ary); + for (i = 0; i < count; i++) { + VALUE o = RARRAY_AT(ary, i); + if (RTEST(vm_fast_eqq(cache, o, comparedTo, overriden))) { + return Qtrue; + } + } + return Qfalse; +} + +inline void +vm_set_current_scope(VALUE mod, int scope) +{ + rb_vm_set_current_scope(mod, scope); +} + +inline VALUE +vm_ocval_to_rval(id ocval) +{ + return OC2RB(ocval); +} + +inline VALUE +vm_char_to_rval(char c) +{ + return INT2FIX(c); +} + +inline VALUE +vm_uchar_to_rval(unsigned char c) +{ + return INT2FIX(c); +} + +inline VALUE +vm_short_to_rval(short c) +{ + return INT2FIX(c); +} + +inline VALUE +vm_ushort_to_rval(unsigned short c) +{ + return INT2FIX(c); +} + +inline VALUE +vm_int_to_rval(int c) +{ + return INT2FIX(c); +} + +inline VALUE +vm_uint_to_rval(unsigned int c) +{ + return INT2FIX(c); +} + +inline VALUE +vm_long_to_rval(long l) +{ + return LONG2NUM(l); +} + +inline VALUE +vm_ulong_to_rval(unsigned long l) +{ + return ULONG2NUM(l); +} + +inline VALUE +vm_long_long_to_rval(long long l) +{ + return LL2NUM(l); +} + +inline VALUE +vm_ulong_long_to_rval(unsigned long long l) +{ + return ULL2NUM(l); +} + +inline VALUE +vm_float_to_rval(float f) +{ + return DOUBLE2NUM(f); +} + +inline VALUE +vm_double_to_rval(double d) +{ + return DOUBLE2NUM(d); +} + +inline VALUE +vm_sel_to_rval(SEL sel) +{ + return sel == 0 ? Qnil : ID2SYM(rb_intern(sel_getName(sel))); +} + +inline VALUE +vm_charptr_to_rval(const char *ptr) +{ + return ptr == NULL ? Qnil : rb_str_new2(ptr); +} + +inline void +vm_rval_to_ocval(VALUE rval, id *ocval) +{ + *ocval = rval == Qnil ? NULL : RB2OC(rval); +} + +inline void +vm_rval_to_bool(VALUE rval, BOOL *ocval) +{ + if (rval == Qfalse || rval == Qnil) { + *ocval = NO; + } + else { + // All other types should be converted as true, to follow the Ruby + // semantics (where for example any integer is always true, even 0). + *ocval = YES; + } +} + +static inline const char * +rval_to_c_str(VALUE rval) +{ + if (NIL_P(rval)) { + return NULL; + } + else { + if (CLASS_OF(rval) == rb_cSymbol) { + return rb_sym2name(rval); + } + if (rb_obj_is_kind_of(rval, rb_cPointer)) { + return (const char *)rb_pointer_get_data(rval, "^c"); + } + return StringValueCStr(rval); + } +} + +inline void +vm_rval_to_sel(VALUE rval, SEL *ocval) +{ + const char *cstr = rval_to_c_str(rval); + *ocval = cstr == NULL ? NULL : sel_registerName(cstr); +} + +inline void +vm_rval_to_charptr(VALUE rval, const char **ocval) +{ + *ocval = rval_to_c_str(rval); +} + +static inline long +bool_to_fix(VALUE rval) +{ + if (rval == Qtrue) { + return INT2FIX(1); + } + if (rval == Qfalse) { + return INT2FIX(0); + } + return rval; +} + +static inline long +rval_to_long(VALUE rval) +{ + return NUM2LONG(rb_Integer(bool_to_fix(rval))); +} + +static inline long long +rval_to_long_long(VALUE rval) +{ + return NUM2LL(rb_Integer(bool_to_fix(rval))); +} + +static inline double +rval_to_double(VALUE rval) +{ + return RFLOAT_VALUE(rb_Float(bool_to_fix(rval))); +} + +inline void +vm_rval_to_char(VALUE rval, char *ocval) +{ + if (TYPE(rval) == T_STRING && RSTRING_LEN(rval) == 1) { + *ocval = (char)RSTRING_PTR(rval)[0]; + } + else { + *ocval = (char)rval_to_long(rval); + } +} + +inline void +vm_rval_to_uchar(VALUE rval, unsigned char *ocval) +{ + if (TYPE(rval) == T_STRING && RSTRING_LEN(rval) == 1) { + *ocval = (unsigned char)RSTRING_PTR(rval)[0]; + } + else { + *ocval = (unsigned char)rval_to_long(rval); + } +} + +inline void +vm_rval_to_short(VALUE rval, short *ocval) +{ + *ocval = (short)rval_to_long(rval); +} + +inline void +vm_rval_to_ushort(VALUE rval, unsigned short *ocval) +{ + *ocval = (unsigned short)rval_to_long(rval); +} + +inline void +vm_rval_to_int(VALUE rval, int *ocval) +{ + *ocval = (int)rval_to_long(rval); +} + +inline void +vm_rval_to_uint(VALUE rval, unsigned int *ocval) +{ + *ocval = (unsigned int)rval_to_long(rval); +} + +inline void +vm_rval_to_long(VALUE rval, long *ocval) +{ + *ocval = (long)rval_to_long(rval); +} + +inline void +vm_rval_to_ulong(VALUE rval, unsigned long *ocval) +{ + *ocval = (unsigned long)rval_to_long(rval); +} + +inline void +vm_rval_to_long_long(VALUE rval, long long *ocval) +{ + *ocval = (long long)rval_to_long_long(rval); +} + +inline void +vm_rval_to_ulong_long(VALUE rval, unsigned long long *ocval) +{ + *ocval = (unsigned long long)rval_to_long_long(rval); +} + +inline void +vm_rval_to_double(VALUE rval, double *ocval) +{ + *ocval = (double)rval_to_double(rval); +} + +inline void +vm_rval_to_float(VALUE rval, float *ocval) +{ + *ocval = (float)rval_to_double(rval); +} + +inline void * +vm_rval_to_cptr(VALUE rval, const char *type, void **cptr) +{ + if (NIL_P(rval)) { + *cptr = NULL; + } + else { + if (TYPE(rval) == T_ARRAY + || rb_boxed_is_type(CLASS_OF(rval), type + 1)) { + // A convenience helper so that the user can pass a Boxed or an + // Array object instead of a Pointer to the object. + rval = rb_pointer_new2(type + 1, rval); + } + *cptr = rb_pointer_get_data(rval, type); + } + return *cptr; +} + +inline VALUE +vm_to_a(VALUE obj) +{ + VALUE ary = rb_check_convert_type(obj, T_ARRAY, "Array", "to_a"); + if (NIL_P(ary)) { + ary = rb_ary_new3(1, obj); + } + return ary; +} + +inline VALUE +vm_to_ary(VALUE obj) +{ + VALUE ary = rb_check_convert_type(obj, T_ARRAY, "Array", "to_ary"); + if (NIL_P(ary)) { + ary = rb_ary_new3(1, obj); + } + return ary; +} + +inline VALUE +vm_ary_cat(VALUE ary, VALUE obj) +{ + VALUE ary2 = rb_check_convert_type(obj, T_ARRAY, "Array", "to_a"); + if (!NIL_P(ary2)) { + rb_ary_concat(ary, ary2); + } + else { + rb_ary_push(ary, obj); + } + return ary; +} + +inline VALUE +vm_ary_dup(VALUE ary) +{ + return rb_ary_dup(ary); +} + +inline VALUE +vm_rary_new(int len) +{ + VALUE ary = rb_ary_new2(len); + RARY(ary)->len = len; + return ary; +} + +inline void +vm_rary_aset(VALUE ary, int i, VALUE obj) +{ + rary_elt_set(ary, i, obj); +} + +inline VALUE +vm_rhash_new(void) +{ + return rb_hash_new(); +} + +inline void +vm_rhash_store(VALUE hash, VALUE key, VALUE obj) +{ + rhash_store(hash, key, obj); +} + +inline VALUE +vm_masgn_get_elem_before_splat(VALUE ary, int offset) +{ + if (offset < RARRAY_LEN(ary)) { + return RARRAY_AT(ary, offset); + } + return Qnil; +} + +inline VALUE +vm_masgn_get_elem_after_splat(VALUE ary, int before_splat_count, + int after_splat_count, int offset) +{ + const int len = RARRAY_LEN(ary); + if (len < before_splat_count + after_splat_count) { + offset += before_splat_count; + if (offset < len) { + return RARRAY_AT(ary, offset); + } + } + else { + offset += len - after_splat_count; + return RARRAY_AT(ary, offset); + } + return Qnil; +} + +inline VALUE +vm_masgn_get_splat(VALUE ary, int before_splat_count, int after_splat_count) +{ + const int len = RARRAY_LEN(ary); + if (len > before_splat_count + after_splat_count) { + return rb_ary_subseq(ary, before_splat_count, + len - before_splat_count - after_splat_count); + } + else { + return rb_ary_new(); + } +} + +inline VALUE +vm_get_special(char code) +{ + VALUE backref = rb_backref_get(); + if (backref == Qnil) { + return Qnil; + } + + VALUE val; + switch (code) { + case '&': + val = rb_reg_last_match(backref); + break; + case '`': + val = rb_reg_match_pre(backref); + break; + case '\'': + val = rb_reg_match_post(backref); + break; + case '+': + val = rb_reg_match_last(backref); + break; + default: + // Boundaries check is done in rb_reg_nth_match(). + val = rb_reg_nth_match((int)code, backref); + break; + } + return val; +} diff --git a/objc.h b/objc.h index 722e193c4..5726a84d7 100644 --- a/objc.h +++ b/objc.h @@ -173,6 +173,9 @@ TypeArity(const char *type) return arity; } +id rb_objc_numeric2nsnumber(VALUE obj); +VALUE rb_objc_nsnumber2numeric(id obj); + static inline id rb_rval_to_ocid(VALUE obj) { @@ -186,39 +189,13 @@ rb_rval_to_ocid(VALUE obj) if (obj == Qnil) { return (id)kCFNull; } - if (FIXNUM_P(obj)) { - // TODO: this could be optimized in case we can fit the fixnum - // into an immediate NSNumber directly. - long val = FIX2LONG(obj); - CFNumberRef number = CFNumberCreate(NULL, kCFNumberLongType, &val); - CFMakeCollectable(number); - return (id)number; - } - if (FIXFLOAT_P(obj)) { - double val = NUM2DBL(obj); - CFNumberRef number = CFNumberCreate(NULL, kCFNumberDoubleType, - &val); - CFMakeCollectable(number); - return (id)number; + if (IMMEDIATE_P(obj)) { + return rb_objc_numeric2nsnumber(obj); } } return (id)obj; } -static inline bool -rb_objc_obj_is_nsnumber(id obj) -{ - Class k = object_getClass(obj); // might be an immediate - do { - if (k == (Class)rb_cNSNumber) { - return true; - } - k = class_getSuperclass(k); - } - while (k != NULL); - return false; -} - static inline VALUE rb_ocid_to_rval(id obj) { @@ -231,26 +208,7 @@ rb_ocid_to_rval(id obj) if (obj == (id)kCFNull || obj == nil) { return Qnil; } - - if (rb_objc_obj_is_nsnumber(obj)) { - // TODO: this could be optimized in case the object is an immediate. - if (CFNumberIsFloatType((CFNumberRef)obj)) { - double v = 0; - assert(CFNumberGetValue((CFNumberRef)obj, kCFNumberDoubleType, &v)); - return DOUBLE2NUM(v); - } - else { - long v = 0; - assert(CFNumberGetValue((CFNumberRef)obj, kCFNumberLongType, &v)); - return LONG2FIX(v); - } - } - - if (((unsigned long)obj & 0x1) == 0x1) { - rb_bug("unknown Objective-C immediate: %p\n", obj); - } - - return (VALUE)obj; + return rb_objc_nsnumber2numeric(obj); } #define RB2OC(obj) (rb_rval_to_ocid((VALUE)obj)) @@ -263,6 +221,8 @@ bool rb_objc_isEqual(VALUE x, VALUE y); void rb_objc_force_class_initialize(Class klass); void rb_objc_fix_relocatable_load_path(void); +extern bool rb_objc_enable_ivar_set_kvo_notifications; + #if defined(__cplusplus) } #endif diff --git a/objc.m b/objc.m index ee04719ea..d26c772e0 100644 --- a/objc.m +++ b/objc.m @@ -367,7 +367,7 @@ - (VMURange)addressRange; #endif } -static bool enable_kvo_notifications = false; +bool rb_objc_enable_ivar_set_kvo_notifications = false; VALUE rb_require_framework(VALUE recv, SEL sel, int argc, VALUE *argv) @@ -481,7 +481,7 @@ - (VMURange)addressRange; reload_class_constants(); reload_protocols(); - enable_kvo_notifications = true; + rb_objc_enable_ivar_set_kvo_notifications = true; return loaded ? Qfalse : Qtrue; } @@ -537,23 +537,6 @@ - (VMURange)addressRange; } } -VALUE -rb_vm_set_kvo_ivar(VALUE obj, ID name, VALUE val, void *cache) -{ - if (enable_kvo_notifications) { - NSString *key = [(NSString *)rb_id2str(name) substringFromIndex:1]; // skip '@' prefix - [(id)obj willChangeValueForKey:key]; - - rb_vm_ivar_set(obj, name, val, cache); - - [(id)obj didChangeValueForKey:key]; - } - else { - rb_vm_ivar_set(obj, name, val, cache); - } - return val; -} - VALUE rb_mod_objc_ib_outlet(VALUE recv, SEL sel, int argc, VALUE *argv) { @@ -666,6 +649,65 @@ - (VMURange)addressRange; reason:[NSString stringWithUTF8String:message] userInfo:nil] raise]; } +id +rb_objc_numeric2nsnumber(VALUE obj) +{ + if (FIXNUM_P(obj)) { + // TODO: this could be optimized in case we can fit the fixnum + // into an immediate NSNumber directly. + long val = FIX2LONG(obj); + CFNumberRef number = CFNumberCreate(NULL, kCFNumberLongType, &val); + CFMakeCollectable(number); + return (id)number; + } + if (FIXFLOAT_P(obj)) { + double val = NUM2DBL(obj); + CFNumberRef number = CFNumberCreate(NULL, kCFNumberDoubleType, + &val); + CFMakeCollectable(number); + return (id)number; + } + abort(); +} + +static inline bool +rb_objc_obj_is_nsnumber(id obj) +{ + Class k = object_getClass(obj); // might be an immediate + do { + if (k == (Class)rb_cNSNumber) { + return true; + } + k = class_getSuperclass(k); + } + while (k != NULL); + return false; +} + +VALUE +rb_objc_nsnumber2numeric(id obj) +{ + if (rb_objc_obj_is_nsnumber(obj)) { + // TODO: this could be optimized in case the object is an immediate. + if (CFNumberIsFloatType((CFNumberRef)obj)) { + double v = 0; + assert(CFNumberGetValue((CFNumberRef)obj, kCFNumberDoubleType, &v)); + return DOUBLE2NUM(v); + } + else { + long v = 0; + assert(CFNumberGetValue((CFNumberRef)obj, kCFNumberLongType, &v)); + return LONG2FIX(v); + } + } + + if (((unsigned long)obj & 0x1) == 0x1) { + rb_bug("unknown Objective-C immediate: %p\n", obj); + } + + return (VALUE)obj; +} + bool rb_objc_ignore_sel(SEL sel) { @@ -747,6 +789,18 @@ - (VMURange)addressRange; } } +void +rb_objc_willChangeValueForKey(id obj, NSString *key) +{ + [obj willChangeValueForKey:key]; +} + +void +rb_objc_didChangeValueForKey(id obj, NSString *key) +{ + [obj didChangeValueForKey:key]; +} + void Init_ObjC(void) { diff --git a/rakelib/builder.rake b/rakelib/builder.rake index 833085035..88de966a7 100644 --- a/rakelib/builder.rake +++ b/rakelib/builder.rake @@ -25,6 +25,28 @@ task :objects => [:config_h, :dtrace_h, :revision_h, :mark_gc] do if !File.exist?('node_name.inc') or File.mtime('include/ruby/node.h') > File.mtime('node_name.inc') sh("/usr/bin/ruby -n tool/node_name.rb include/ruby/node.h > node_name.inc") end + if !File.exist?('kernel_data.c') or File.mtime('kernel.c') > File.mtime('kernel_data.c') + # Locate llvm-gcc... + path = ENV['PATH'].split(':') + path.unshift('/Developer/usr/bin') + llvm_gcc = path.map { |x| File.join(x, 'llvm-gcc') }.find { |x| File.exist?(x) } + unless llvm_gcc + $stderr.puts "Cannot locate llvm-gcc in given path: #{path}" + exit 1 + end + opt = File.join(LLVM_PATH, 'bin/opt') + unless File.exist?(opt) + $stderr.puts "Cannot locate opt in given LLVM path: #{LLVM_PATH}" + end + sh "echo '' > kernel_data.c" + ARCHS.each do |x| + output = "kernel-#{x}.bc" + sh "#{llvm_gcc} -arch #{x} -fexceptions -I. -I./include --emit-llvm -c kernel.c -o #{output}" + sh "#{opt} -O3 #{output} -o=#{output}" + sh "/usr/bin/xxd -i #{output} >> kernel_data.c" + sh "/bin/rm #{output}" + end + end t = File.exist?('dispatcher.o') ? File.mtime('dispatcher.o') : nil $builder.build if t == nil or File.mtime('dispatcher.o') > t @@ -69,14 +91,6 @@ namespace :macruby do task :build => :dylib do $builder.link_executable(RUBY_INSTALL_NAME, ['main', 'gc-stub'], "-L. -l#{RUBY_SO_NAME} -lobjc") end - - # Generates a list of weak symbols in libmacruby.dylib. You must not pass a unexported symbols list to - # rake when calling this command. - task :weak_symbols => :dylib do - sh("nm -m -P -arch i386 libmacruby.1.9.0.dylib | grep 'weak external' | grep -v 'undefined' | egrep -v '__ZT[IS]' | awk '{print$5}' > /tmp/syms-i386") - sh("nm -m -P -arch x86_64 libmacruby.1.9.0.dylib | grep 'weak external' | grep -v 'undefined' | egrep -v '__ZT[IS]' | awk '{print$5}' > /tmp/syms-x86_64") - sh("cat /tmp/syms-i386 /tmp/syms-x86_64 | uniq > unexported_symbols.list") - end end DESTDIR = (ENV['DESTDIR'] or "") @@ -163,7 +177,7 @@ namespace :clean do desc "Clean local build files" task :local do $builder.clean - list = ['parse.c', 'lex.c', INSTALLED_LIST, 'Makefile', RUBY_INSTALL_NAME, 'miniruby'] + list = ['parse.c', 'lex.c', INSTALLED_LIST, 'Makefile', RUBY_INSTALL_NAME, 'miniruby', 'kernel_data.c'] list.concat(Dir['*.inc']) list.concat(Dir['lib*.{dylib,a}']) list.each { |x| rm_f(x) } diff --git a/rakelib/builder/options.rb b/rakelib/builder/options.rb index d844c2ab2..8851735aa 100644 --- a/rakelib/builder/options.rb +++ b/rakelib/builder/options.rb @@ -105,7 +105,7 @@ def self.option(name, default) INSTALL_NAME = File.join(FRAMEWORK_USR_LIB, 'lib' + RUBY_SO_NAME + '.dylib') ARCHFLAGS = ARCHS.map { |a| '-arch ' + a }.join(' ') -LLVM_MODULES = "core jit nativecodegen bitwriter" +LLVM_MODULES = "core jit nativecodegen bitwriter bitreader" EXPORTED_SYMBOLS_LIST = "./exported_symbols_list" CC = '/usr/bin/gcc-4.2' diff --git a/string.c b/string.c index 03ee75f2d..fb260ba97 100644 --- a/string.c +++ b/string.c @@ -428,7 +428,7 @@ str_length(rb_str_t *self, bool ucs2_mode) if (self->length_in_bytes == 0) { return 0; } - if (str_is_stored_in_uchars(self)) { + if (str_try_making_data_uchars(self)) { long length; if (ucs2_mode) { length = BYTES_TO_UCHARS(self->length_in_bytes); @@ -4401,6 +4401,9 @@ rstr_justify(int argc, VALUE *argv, VALUE str, char mode) const long len = str_length(RSTR(str), true); long width = NUM2LONG(w); str = rb_str_new3(str); + if (str_is_stored_in_uchars(RSTR(padstr))) { + str_try_making_data_uchars(RSTR(str)); + } if (width < 0 || width <= len) { return str; } diff --git a/vm.cpp b/vm.cpp index 6569b8a78..d579a8d14 100644 --- a/vm.cpp +++ b/vm.cpp @@ -30,7 +30,8 @@ #include #include #include -#include // Including PST to disable it +#include +#include #include #include #include @@ -302,6 +303,7 @@ RoxorCore::RoxorCore(void) { running = false; abort_on_exception = false; + inlining_enabled = getenv("VM_DISABLE_INLINING") == NULL; pthread_assert(pthread_mutex_init(&gl, 0)); @@ -326,8 +328,30 @@ RoxorCore::RoxorCore(void) InitializeNativeTarget(); + CodeGenOpt::Level opt = CodeGenOpt::Default; + const char *env_str = getenv("VM_OPT_LEVEL"); + if (env_str != NULL) { + const int tmp = atoi(env_str); + if (tmp >= 0 && tmp <= 3) { + switch (tmp) { + case 0: + opt = CodeGenOpt::None; + break; + case 1: + opt = CodeGenOpt::Less; + break; + case 2: + opt = CodeGenOpt::Default; + break; + case 3: + opt = CodeGenOpt::Aggressive; + break; + } + } + } + std::string err; - ee = ExecutionEngine::createJIT(emp, &err, jmm, CodeGenOpt::None, false); + ee = ExecutionEngine::createJIT(emp, &err, jmm, opt, false); if (ee == NULL) { fprintf(stderr, "error while creating JIT: %s\n", err.c_str()); abort(); @@ -497,8 +521,17 @@ RoxorVM::debug_exceptions(void) return s; } +void +RoxorCore::optimize(Function *func) +{ + if (inlining_enabled) { + RoxorCompiler::shared->inline_function_calls(func); + } + fpm->run(*func); +} + IMP -RoxorCore::compile(Function *func) +RoxorCore::compile(Function *func, bool run_optimize) { std::map::iterator iter = JITcache.find(func); if (iter != JITcache.end()) { @@ -511,15 +544,19 @@ RoxorCore::compile(Function *func) if (!ruby_aot_compile) { if (verifyModule(*RoxorCompiler::module, PrintMessageAction)) { printf("Error during module verification\n"); - exit(1); + abort(); } } uint64_t start = mach_absolute_time(); #endif - // Optimize & compile. - optimize(func); + // Optimize if needed. + if (run_optimize) { + optimize(func); + } + + // Compile & cache. IMP imp = (IMP)ee->getPointerToFunction(func); JITcache[func] = imp; @@ -807,7 +844,7 @@ RoxorCore::is_large_struct_type(const Type *type) && ee->getTargetData()->getTypeSizeInBits(type) > LARGE_STRUCT_SIZE; } -inline GlobalVariable * +GlobalVariable * RoxorCore::redefined_op_gvar(SEL sel, bool create) { std::map ::iterator iter = @@ -816,10 +853,11 @@ RoxorCore::redefined_op_gvar(SEL sel, bool create) if (iter == redefined_ops_gvars.end()) { if (create) { gvar = new GlobalVariable(*RoxorCompiler::module, - Type::getInt1Ty(context), + Type::getInt8Ty(context), ruby_aot_compile ? true : false, GlobalValue::InternalLinkage, - ConstantInt::getFalse(context), ""); + ConstantInt::get(Type::getInt8Ty(context), 0), + ""); assert(gvar != NULL); redefined_ops_gvars[sel] = gvar; } @@ -830,7 +868,7 @@ RoxorCore::redefined_op_gvar(SEL sel, bool create) return gvar; } -inline bool +bool RoxorCore::should_invalidate_inline_op(SEL sel, Class klass) { if (sel == selEq || sel == selEqq || sel == selNeq) { @@ -899,7 +937,6 @@ RoxorCore::method_added(Class klass, SEL sel) } } } - } void @@ -1144,26 +1181,6 @@ rb_vm_set_abort_on_exception(bool flag) GET_CORE()->set_abort_on_exception(flag); } -extern "C" -void -rb_vm_set_const(VALUE outer, ID id, VALUE obj, unsigned char dynamic_class) -{ - if (dynamic_class) { - Class k = GET_VM()->get_current_class(); - if (k != NULL) { - outer = (VALUE)k; - } - } -#if ROXOR_VM_DEBUG - printf("define const %s::%s to %p\n", - class_getName((Class)outer), - rb_id2name(id), - (void *)obj); -#endif - rb_const_set(outer, id, obj); - GET_CORE()->const_defined(id); -} - static inline VALUE rb_const_get_direct(VALUE klass, ID id) { @@ -1202,9 +1219,11 @@ rb_const_get_direct(VALUE klass, ID id) return Qundef; } -static VALUE +extern "C" +VALUE rb_vm_const_lookup(VALUE outer, ID path, bool lexical, bool defined) { + rb_vm_check_if_module(outer); 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. @@ -1225,51 +1244,6 @@ rb_vm_const_lookup(VALUE outer, ID path, bool lexical, bool defined) return defined ? rb_const_defined(outer, path) : rb_const_get(outer, path); } -static inline void -check_if_module(VALUE mod) -{ - switch (TYPE(mod)) { - case T_CLASS: - case T_MODULE: - break; - - default: - rb_raise(rb_eTypeError, "%s is not a class/module", - RSTRING_PTR(rb_inspect(mod))); - } -} - -extern "C" -VALUE -rb_vm_get_const(VALUE outer, struct ccache *cache, ID path, int flags) -{ - const bool lexical_lookup = (flags & CONST_LOOKUP_LEXICAL); - const bool dynamic_class = (flags & CONST_LOOKUP_DYNAMIC_CLASS); - - if (dynamic_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) { - val = cache->val; - } - else { - check_if_module(outer); - val = rb_vm_const_lookup(outer, path, lexical_lookup, false); - assert(val != Qundef); - cache->outer = outer; - cache->val = val; - } - - return val; -} - extern "C" void rb_vm_const_is_defined(ID path) @@ -1343,7 +1317,7 @@ rb_vm_define_class(ID path, VALUE outer, VALUE super, int flags, unsigned char dynamic_class) { assert(path > 0); - check_if_module(outer); + rb_vm_check_if_module(outer); if (dynamic_class) { Class k = GET_VM()->get_current_class(); @@ -1355,7 +1329,7 @@ rb_vm_define_class(ID path, VALUE outer, VALUE super, int flags, VALUE klass = get_klass_const(outer, path, dynamic_class); if (klass != Qundef) { // Constant is already defined. - check_if_module(klass); + rb_vm_check_if_module(klass); if (!(flags & DEFINE_MODULE) && super != 0) { if (rb_class_real(RCLASS_SUPER(klass), true) != super) { rb_raise(rb_eTypeError, "superclass mismatch for class %s", @@ -1424,9 +1398,6 @@ rb_vm_define_class(ID path, VALUE outer, VALUE super, int flags, return klass; } -#define LIKELY(x) (__builtin_expect((x), 1)) -#define UNLIKELY(x) (__builtin_expect((x), 0)) - extern "C" int rb_vm_get_ivar_slot(VALUE obj, ID name, bool create) @@ -1454,169 +1425,6 @@ rb_vm_get_ivar_slot(VALUE obj, ID name, bool create) return -1; } -extern "C" -VALUE -rb_vm_ivar_get(VALUE obj, ID name, struct icache *cache) -{ - VALUE klass = CLASS_OF(obj); - if (LIKELY(klass == cache->klass)) { -use_slot: - if ((unsigned int)cache->slot < ROBJECT(obj)->num_slots) { - rb_object_ivar_slot_t *slot = &ROBJECT(obj)->slots[cache->slot]; - if (slot->name == name) { -#if ROXOR_VM_DEBUG - printf("get ivar <%s %p> %s slot %d -> %p\n", - class_getName((Class)CLASS_OF(obj)), (void *)obj, - rb_id2name(name), cache->slot, (void *)slot->value); -#endif - VALUE val = slot->value; - return val == Qundef ? Qnil : val; - } - } - goto recache; - } - else { - goto recache; - } - - if (cache->slot == SLOT_CACHE_VIRGIN) { -recache: - const int slot = rb_vm_get_ivar_slot(obj, name, true); - if (slot >= 0) { - cache->klass = klass; - cache->slot = slot; - goto use_slot; - } - cache->klass = 0; - cache->slot = SLOT_CACHE_CANNOT; - } - - assert(cache->slot == SLOT_CACHE_CANNOT); -#if ROXOR_VM_DEBUG - printf("get ivar <%s %p> %s without slot\n", - class_getName((Class)CLASS_OF(obj)), (void *)obj, rb_id2name(name)); -#endif - return rb_ivar_get(obj, name); -} - -extern "C" -void -rb_vm_ivar_set(VALUE obj, ID name, VALUE val, void *cache_ptr) -{ - struct icache *cache = (struct icache *)cache_ptr; - VALUE klass = CLASS_OF(obj); - if (LIKELY(klass == cache->klass)) { -use_slot: - if ((unsigned int)cache->slot < ROBJECT(obj)->num_slots) { - rb_object_ivar_slot_t *slot = &ROBJECT(obj)->slots[cache->slot]; - if (slot->name == name) { - if ((ROBJECT(obj)->basic.flags & FL_FREEZE) == FL_FREEZE) { - rb_error_frozen("object"); - } -#if ROXOR_VM_DEBUG - printf("set ivar <%s %p> %s to %p slot %d\n", - class_getName((Class)CLASS_OF(obj)), (void *)obj, - rb_id2name(name), (void *)val, cache->slot); -#endif - GC_WB(&slot->value, val); - return; - } - } - goto recache; - } - else { - goto recache; - } - - if (cache->slot == SLOT_CACHE_VIRGIN) { -recache: - const int slot = rb_vm_get_ivar_slot(obj, name, true); - if (slot >= 0) { - cache->klass = klass; - cache->slot = slot; - goto use_slot; - } - cache->slot = SLOT_CACHE_CANNOT; - } - - assert(cache->slot == SLOT_CACHE_CANNOT); -#if ROXOR_VM_DEBUG - printf("set ivar <%s %p> %s to %p without slot\n", - class_getName((Class)CLASS_OF(obj)), (void *)obj, - rb_id2name(name), (void *)val); -#endif - rb_ivar_set(obj, name, val); -} - -extern "C" -VALUE -rb_vm_cvar_get(VALUE klass, ID id, unsigned char check, - unsigned char dynamic_class) -{ - if (dynamic_class) { - Class k = GET_VM()->get_current_class(); - if (k != NULL) { - klass = (VALUE)k; - } - } - return rb_cvar_get2(klass, id, check); -} - -extern "C" -VALUE -rb_vm_cvar_set(VALUE klass, ID id, VALUE val, unsigned char dynamic_class) -{ - if (dynamic_class) { - Class k = GET_VM()->get_current_class(); - if (k != NULL) { - klass = (VALUE)k; - } - } - rb_cvar_set(klass, id, val); - return val; -} - -extern "C" -VALUE -rb_vm_ary_cat(VALUE ary, VALUE obj) -{ - if (TYPE(obj) == T_ARRAY) { - rb_ary_concat(ary, obj); - } - else { - VALUE ary2 = rb_check_convert_type(obj, T_ARRAY, "Array", "to_a"); - if (!NIL_P(ary2)) { - rb_ary_concat(ary, ary2); - } - else { - rb_ary_push(ary, obj); - } - } - return ary; -} - -extern "C" -VALUE -rb_vm_to_a(VALUE obj) -{ - VALUE ary = rb_check_convert_type(obj, T_ARRAY, "Array", "to_a"); - if (NIL_P(ary)) { - ary = rb_ary_new3(1, obj); - } - return ary; -} - -extern "C" -VALUE -rb_vm_to_ary(VALUE obj) -{ - VALUE ary = rb_check_convert_type(obj, T_ARRAY, "Array", "to_ary"); - if (NIL_P(ary)) { - ary = rb_ary_new3(1, obj); - } - return ary; -} - extern "C" void rb_print_undef(VALUE, ID, int); static void @@ -1974,7 +1782,7 @@ RoxorCore::resolve_method(Class klass, SEL sel, Function *func, if (iter == objc_to_ruby_stubs.end()) { Function *objc_func = RoxorCompiler::shared->compile_objc_stub(func, imp, arity, types); - objc_imp = compile(objc_func); + objc_imp = compile(objc_func, false); objc_to_ruby_stubs[imp] = objc_imp; } else { @@ -2690,7 +2498,7 @@ rb_vm_generate_mri_stub(void *imp, const int arity) // Not needed! return imp; } - return (void *)GET_CORE()->compile(func); + return (void *)GET_CORE()->compile(func, false); } void @@ -2809,46 +2617,6 @@ rb_vm_remove_method(Class klass, ID name) GET_CORE()->remove_method(klass, node->sel); } -extern "C" -VALUE -rb_vm_masgn_get_elem_before_splat(VALUE ary, int offset) -{ - if (offset < RARRAY_LEN(ary)) { - return RARRAY_AT(ary, offset); - } - return Qnil; -} - -extern "C" -VALUE -rb_vm_masgn_get_elem_after_splat(VALUE ary, int before_splat_count, int after_splat_count, int offset) -{ - int len = RARRAY_LEN(ary); - if (len < before_splat_count + after_splat_count) { - offset += before_splat_count; - if (offset < len) { - return RARRAY_AT(ary, offset); - } - } - else { - offset += len - after_splat_count; - return RARRAY_AT(ary, offset); - } - return Qnil; -} - -extern "C" -VALUE -rb_vm_masgn_get_splat(VALUE ary, int before_splat_count, int after_splat_count) { - int len = RARRAY_LEN(ary); - if (len > before_splat_count + after_splat_count) { - return rb_ary_subseq(ary, before_splat_count, len - before_splat_count - after_splat_count); - } - else { - return rb_ary_new(); - } -} - extern "C" VALUE rb_vm_method_missing(VALUE obj, int argc, const VALUE *argv) @@ -2921,7 +2689,7 @@ RoxorCore::gen_large_arity_stub(int argc, bool is_block) if (iter == stubs.end()) { Function *f = RoxorCompiler::shared->compile_long_arity_stub(argc, is_block); - stub = (void *)compile(f); + stub = (void *)compile(f, false); stubs.insert(std::make_pair(argc, stub)); } else { @@ -3429,41 +3197,6 @@ class_respond_to(Class klass, SEL sel) } #endif -extern "C" -VALUE -rb_vm_get_special(char code) -{ - VALUE backref = rb_backref_get(); - if (backref == Qnil) { - return Qnil; - } - - VALUE val; - switch (code) { - case '&': - val = rb_reg_last_match(backref); - break; - case '`': - val = rb_reg_match_pre(backref); - break; - case '\'': - val = rb_reg_match_post(backref); - break; - case '+': - val = rb_reg_match_last(backref); - break; - default: - { - const int index = (int)code; - // Boundaries check is done in rb_reg_nth_match(). - val = rb_reg_nth_match(index, backref); - } - break; - } - - return val; -} - static inline void __vm_raise(void) { @@ -3950,7 +3683,7 @@ extern "C" void rb_node_release(NODE *node); extern "C" VALUE rb_vm_run(const char *fname, NODE *node, rb_vm_binding_t *binding, - bool inside_eval) + bool inside_eval) { RoxorVM *vm = GET_VM(); RoxorCompiler *compiler = RoxorCompiler::shared; @@ -3969,7 +3702,7 @@ rb_vm_run(const char *fname, NODE *node, rb_vm_binding_t *binding, vm->pop_current_binding(false); } - // JIT compile the function. + // Optimize & compile the function. IMP imp = GET_CORE()->compile(function); // Register it for symbolication. @@ -4054,7 +3787,7 @@ rb_vm_aot_compile(NODE *node) // Force a module verification. if (verifyModule(*RoxorCompiler::module, PrintMessageAction)) { printf("Error during module verification\n"); - exit(1); + abort(); } // Optimize the IR. @@ -4813,66 +4546,6 @@ rb_vm_get_current_class(void) return GET_VM()->get_current_class(); } -extern "C" -void -rb_vm_set_current_scope(VALUE mod, rb_vm_scope_t scope) -{ - if (scope == SCOPE_DEFAULT) { - scope = mod == rb_cObject ? SCOPE_PRIVATE : SCOPE_PUBLIC; - } - long v = RCLASS_VERSION(mod); -#if ROXOR_VM_DEBUG - const char *scope_name = NULL; -#endif - switch (scope) { - case SCOPE_PUBLIC: -#if ROXOR_VM_DEBUG - scope_name = "public"; -#endif - v &= ~RCLASS_SCOPE_PRIVATE; - v &= ~RCLASS_SCOPE_PROTECTED; - v &= ~RCLASS_SCOPE_MOD_FUNC; - break; - - case SCOPE_PRIVATE: -#if ROXOR_VM_DEBUG - scope_name = "private"; -#endif - v |= RCLASS_SCOPE_PRIVATE; - v &= ~RCLASS_SCOPE_PROTECTED; - v &= ~RCLASS_SCOPE_MOD_FUNC; - break; - - case SCOPE_PROTECTED: -#if ROXOR_VM_DEBUG - scope_name = "protected"; -#endif - v &= ~RCLASS_SCOPE_PRIVATE; - v |= RCLASS_SCOPE_PROTECTED; - v &= ~RCLASS_SCOPE_MOD_FUNC; - break; - - case SCOPE_MODULE_FUNC: -#if ROXOR_VM_DEBUG - scope_name = "module_func"; -#endif - v &= ~RCLASS_SCOPE_PRIVATE; - v &= ~RCLASS_SCOPE_PROTECTED; - v |= RCLASS_SCOPE_MOD_FUNC; - break; - - case SCOPE_DEFAULT: - abort(); // handled earlier - } - -#if ROXOR_VM_DEBUG - printf("changing scope of %s (%p) to %s\n", - class_getName((Class)mod), (void *)mod, scope_name); -#endif - - RCLASS_SET_VERSION(mod, v); -} - static VALUE builtin_ostub1(IMP imp, id self, SEL sel, int argc, VALUE *argv) { @@ -4933,6 +4606,8 @@ class_has_custom_resolver(Class klass) # define TARGET_TRIPLE "i386-apple-darwin" #endif +#include "kernel_data.c" + extern "C" void Init_PreVM(void) @@ -4946,7 +4621,22 @@ Init_PreVM(void) // To not corrupt stack pointer (essential for backtracing). llvm::NoFramePointerElim = true; - RoxorCompiler::module = new llvm::Module("Roxor", getGlobalContext()); + const char *kernel_beg; + const char *kernel_end; +#if __LP64__ + kernel_beg = (const char *)kernel_x86_64_bc; + kernel_end = kernel_beg + kernel_x86_64_bc_len; +#else + kernel_beg = (const char *)kernel_i386_bc; + kernel_end = kernel_beg + kernel_i386_bc_len; +#endif + + MemoryBuffer *mbuf = MemoryBuffer::getMemBuffer(kernel_beg, kernel_end); + assert(mbuf != NULL); + RoxorCompiler::module = ParseBitcodeFile(mbuf, getGlobalContext()); + delete mbuf; + assert(RoxorCompiler::module != NULL); + RoxorCompiler::module->setTargetTriple(TARGET_TRIPLE); RoxorCore::shared = new RoxorCore(); RoxorVM::main = new RoxorVM(); @@ -5205,12 +4895,11 @@ rb_vm_finalize(void) if (getenv("VM_VERIFY_IR") != NULL) { - printf("Verifying IR...\n"); if (verifyModule(*RoxorCompiler::module, PrintMessageAction)) { printf("Error during module verification\n"); - exit(1); + abort(); } - printf("Good!\n"); + printf("IR verified!\n"); } // XXX: deleting the core is not safe at this point because there might be diff --git a/vm.h b/vm.h index 62db92e19..e1aa54bbd 100644 --- a/vm.h +++ b/vm.h @@ -294,6 +294,7 @@ int rb_vm_thread_safe_level(rb_vm_thread_t *thread); VALUE rb_vm_top_self(void); void rb_vm_const_is_defined(ID path); VALUE rb_vm_resolve_const_value(VALUE val, VALUE klass, ID name); +VALUE rb_vm_const_lookup(VALUE outer, ID path, bool lexical, bool defined); bool rb_vm_lookup_method(Class klass, SEL sel, IMP *pimp, rb_vm_method_node_t **pnode); bool rb_vm_lookup_method2(Class klass, ID mid, SEL *psel, IMP *pimp, @@ -329,7 +330,6 @@ bool rb_vm_respond_to2(VALUE obj, VALUE klass, SEL sel, bool priv, bool check_ov VALUE rb_vm_method_missing(VALUE obj, int argc, const VALUE *argv); void rb_vm_push_methods(VALUE ary, VALUE mod, bool include_objc_methods, int (*filter) (VALUE, ID, VALUE)); -void rb_vm_ivar_set(VALUE obj, ID name, VALUE val, void *cache); void rb_vm_set_outer(VALUE klass, VALUE under); VALUE rb_vm_get_outer(VALUE klass); VALUE rb_vm_catch(VALUE tag); @@ -502,16 +502,6 @@ void rb_vm_finalize(void); void rb_vm_load_bridge_support(const char *path, const char *framework_path, int options); -typedef enum { - SCOPE_DEFAULT = 0, // public for everything but Object - SCOPE_PUBLIC, - SCOPE_PRIVATE, - SCOPE_PROTECTED, - SCOPE_MODULE_FUNC, -} rb_vm_scope_t; - -void rb_vm_set_current_scope(VALUE mod, rb_vm_scope_t scope); - typedef struct { VALUE klass; VALUE objid; @@ -522,16 +512,21 @@ void rb_vm_register_finalizer(rb_vm_finalizer_t *finalizer); void rb_vm_unregister_finalizer(rb_vm_finalizer_t *finalizer); void rb_vm_call_finalizer(rb_vm_finalizer_t *finalizer); -#if defined(__cplusplus) -} - -#include "bridgesupport.h" - struct icache { VALUE klass; int slot; }; +struct ccache { + VALUE outer; + VALUE val; +}; + +#if defined(__cplusplus) +} + +#include "bridgesupport.h" + typedef struct { Function *func; rb_vm_arity_t arity; @@ -572,11 +567,6 @@ struct mcache { #define fcache cache->as.fcall }; -struct ccache { - VALUE outer; - VALUE val; -}; - // For rb_vm_define_class() #define DEFINE_MODULE 0x1 #define DEFINE_OUTER 0x2 @@ -620,16 +610,16 @@ class RoxorCore { pthread_mutex_t gl; // State. + bool inlining_enabled; bool running; bool abort_on_exception; VALUE loaded_features; VALUE load_path; VALUE default_random; - // Signals + // Signals. std::map trap_cmd; - // Safety level at the time trap is set - std::map trap_level; + std::map trap_level; // Cache to avoid compiling the same Function twice. std::map JITcache; @@ -711,10 +701,8 @@ class RoxorCore { void register_thread(VALUE thread); void unregister_thread(VALUE thread); - void optimize(Function *func) { - fpm->run(*func); - } - IMP compile(Function *func); + void optimize(Function *func); + IMP compile(Function *func, bool optimize=true); void delenda(Function *func); void load_bridge_support(const char *path, const char *framework_path,