Revision: 3169 http://trac.macosforge.org/projects/ruby/changeset/3169 Author: emoy@apple.com Date: 2009-12-23 15:59:56 -0800 (Wed, 23 Dec 2009) Log Message: ----------- Branch ticket159: fixed crasher, improved optimization, uniqued literals, added IR debugging mode that gives descriptive names to variables, added strict mode, fixed rubyc temp file clashes Modified Paths: -------------- MacRuby/branches/ticket159/array.c MacRuby/branches/ticket159/bignum.c MacRuby/branches/ticket159/bin/rubyc MacRuby/branches/ticket159/compiler.cpp MacRuby/branches/ticket159/compiler.h MacRuby/branches/ticket159/include/ruby/intern.h MacRuby/branches/ticket159/include/ruby/ruby.h MacRuby/branches/ticket159/io.c MacRuby/branches/ticket159/marshal.c MacRuby/branches/ticket159/numeric.c MacRuby/branches/ticket159/object.c MacRuby/branches/ticket159/ruby.c MacRuby/branches/ticket159/sprintf.c MacRuby/branches/ticket159/vm.cpp Modified: MacRuby/branches/ticket159/array.c =================================================================== --- MacRuby/branches/ticket159/array.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/array.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -1923,7 +1923,7 @@ } return 0; } - else if (TYPE(a) == T_FLOAT && TYPE(b) == T_FLOAT) { + else if (FLOAT_P(a) && FLOAT_P(b)) { const double fa = RFLOAT_VALUE(a); const double fb = RFLOAT_VALUE(b); if (fa > fb) { @@ -2935,8 +2935,8 @@ VALUE item1 = rary_elt(RARY(ary1), i); VALUE item2 = rary_elt(RARY(ary2), i); - if ((TYPE(item1) == T_FLOAT && isnan(RFLOAT_VALUE(item1))) - || TYPE(item2) == T_FLOAT && isnan(RFLOAT_VALUE(item2))) { + if ((FLOAT_P(item1) && isnan(RFLOAT_VALUE(item1))) + || FLOAT_P(item2) && isnan(RFLOAT_VALUE(item2))) { return Qfalse; } Modified: MacRuby/branches/ticket159/bignum.c =================================================================== --- MacRuby/branches/ticket159/bignum.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/bignum.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -2235,8 +2235,9 @@ static VALUE bit_coerce(VALUE x) { - while (!FIXNUM_P(x) && TYPE(x) != T_BIGNUM) { - if (TYPE(x) == T_FLOAT) { + int t; + while (!FIXNUM_P(x) && (t = TYPE(x)) != T_BIGNUM) { + if (t == T_FLOAT) { rb_raise(rb_eTypeError, "can't convert Float into Integer"); } x = rb_to_int(x); Modified: MacRuby/branches/ticket159/bin/rubyc =================================================================== --- MacRuby/branches/ticket159/bin/rubyc 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/bin/rubyc 2009-12-23 23:59:56 UTC (rev 3169) @@ -267,7 +267,7 @@ end def gen_tmpfile(base, ext) - file = File.join(@tmpdir, "#{base}.#{ext}") + file = File.join(@tmpdir, "#{base}-#{$$}.#{ext}") @tmpfiles << file file end Modified: MacRuby/branches/ticket159/compiler.cpp =================================================================== --- MacRuby/branches/ticket159/compiler.cpp 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/compiler.cpp 2009-12-23 23:59:56 UTC (rev 3169) @@ -133,6 +133,10 @@ setScopeFunc = NULL; setCurrentClassFunc = NULL; getCacheFunc = NULL; + floatValueFunc = NULL; + newFloatFunc = NULL; + effectiveImmediateBitsFunc = NULL; + zeroDivFunc = NULL; VoidTy = Type::getVoidTy(context); Int1Ty = Type::getInt1Ty(context); @@ -199,11 +203,62 @@ inline bool RoxorCompiler::unbox_ruby_constant(Value *val, VALUE *rval) { - if (ConstantInt::classof(val)) { - long tmp = cast<ConstantInt>(val)->getZExtValue(); - *rval = tmp; - return true; + { + /* Unbox a direct ConstantInt value */ + ConstantInt *const_int = dyn_cast<ConstantInt>(val); + if (const_int != NULL) { + long tmp = const_int->getZExtValue(); + *rval = tmp; + return true; + } } + + if (newFloatFunc == NULL) { + // VALUE rb_float_new_retained(double) + newFloatFunc = cast<Function> + (module->getOrInsertFunction("rb_float_new_retained", + RubyObjTy, DoubleTy, NULL)); + } + + { + /* + * Unbox a CallInst to newFloatFunc (a literal floating point value) + * by getting the ConstantFP argument to the call. + */ + CallInst *callinst = dyn_cast<CallInst>(val); + ConstantFP *const_fp; + if (callinst != NULL + && callinst->getCalledFunction() == newFloatFunc + && (const_fp = dyn_cast<ConstantFP>(callinst->getOperand(1))) + != NULL) + { + *rval = rb_float_new(const_fp->getValueAPF().convertToDouble()); + return true; + } + } + + { + /* + * Unbox a LoadInst of a GlobalVariable. We have previously stored + * the value of the GlobalVariable in the std::map named global_values + * (since the AOT compiler doesn't initialize the GlobalVariable with + * the true value until later). + */ + GlobalVariable *gvar; + LoadInst *load_inst = dyn_cast<LoadInst>(val); + if (load_inst != NULL + && (gvar = dyn_cast<GlobalVariable>(load_inst->getOperand(0))) + != NULL) + { + std::map<GlobalVariable *, VALUE>::iterator iter + = global_values.find(gvar); + if (iter != global_values.end()) { + *rval = iter->second; + return true; + } + } + } + return false; } @@ -215,13 +270,22 @@ } Instruction * +#ifdef DEBUG_IR +RoxorCompiler::compile_protected_call(Value *imp, std::vector<Value *> ¶ms, + std::string *name) +#else /* !DEBUG_IR */ RoxorCompiler::compile_protected_call(Value *imp, std::vector<Value *> ¶ms) +#endif /* DEBUG_IR */ { if (rescue_invoke_bb == NULL) { CallInst *dispatch = CallInst::Create(imp, params.begin(), params.end(), +#ifdef DEBUG_IR + name ? *name : "", +#else /* !DEBUG_IR */ "", +#endif /* DEBUG_IR */ bb); return dispatch; } @@ -234,7 +298,11 @@ rescue_invoke_bb, params.begin(), params.end(), +#ifdef DEBUG_IR + name ? *name : "", +#else /* !DEBUG_IR */ "", +#endif /* DEBUG_IR */ bb); bb = normal_bb; @@ -480,7 +548,11 @@ std::vector<Value *> params; params.push_back(compile_mcache(selEqq, false)); GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(selEqq, true); +#ifdef DEBUG_IR + params.push_back(new LoadInst(is_redefined, "[is_redefined:===:]", bb)); +#else /* !DEBUG_IR */ params.push_back(new LoadInst(is_redefined, "", bb)); +#endif /* DEBUG_IR */ params.push_back(comparedToVal); params.push_back(splatVal); @@ -566,7 +638,8 @@ params.push_back(sel); params.push_back(ConstantInt::get(Int8Ty, super ? 1 : 0)); - return CallInst::Create(getCacheFunc, params.begin(), params.end(), "", bb); + return CallInst::Create(getCacheFunc, params.begin(), params.end(), + "", bb); } Value * @@ -626,7 +699,36 @@ return new LoadInst(gvar, "", bb); } +#ifdef DEBUG_IR Value * +RoxorCompiler::compile_sel(SEL sel, bool add_to_bb) +{ + std::string name("[sel:"); + name.append(sel_getName(sel)); + name.push_back(']'); + + std::map<std::string, GlobalVariable *>::iterator iter = globals.find(name); + + GlobalVariable *gvar; + if (iter == globals.end()) { + gvar = new GlobalVariable(*RoxorCompiler::module, PtrTy, false, + GlobalValue::InternalLinkage, + compile_const_pointer((void *)sel), ""); + assert(gvar != NULL); + + globals[name] = gvar; + } + else { + gvar = iter->second; + } + + return add_to_bb + ? new LoadInst(gvar, name, bb) + : new LoadInst(gvar, name); +} +#endif /* DEBUG_IR */ + +Value * RoxorAOTCompiler::compile_sel(SEL sel, bool add_to_bb) { std::map<SEL, GlobalVariable *>::iterator iter = sels.find(sel); @@ -641,9 +743,18 @@ else { gvar = iter->second; } +#ifdef DEBUG_IR + std::string name("[sel:"); + name.append(sel_getName(sel)); + name.push_back(']'); return add_to_bb + ? new LoadInst(gvar, name, bb) + : new LoadInst(gvar, name); +#else /* !DEBUG_IR */ + return add_to_bb ? new LoadInst(gvar, "", bb) : new LoadInst(gvar, ""); +#endif /* DEBUG_IR */ } inline Value * @@ -1145,7 +1256,15 @@ params.push_back(compile_id(vid)); params.push_back(compile_slot_cache(vid)); +#ifdef DEBUG_IR + std::string name("[ivar:"); + name.append(rb_id2name(vid)); + name.push_back(']'); + + return CallInst::Create(getIvarFunc, params.begin(), params.end(), name, bb); +#else /* !DEBUG_IR */ return CallInst::Create(getIvarFunc, params.begin(), params.end(), "", bb); +#endif /* DEBUG_IR */ } Value * @@ -1189,7 +1308,15 @@ params.push_back(ConstantInt::get(Int8Ty, check ? 1 : 0)); params.push_back(ConstantInt::get(Int8Ty, dynamic_class ? 1 : 0)); +#ifdef DEBUG_IR + std::string name("[cvar:"); + name.append(rb_id2name(id)); + name.push_back(']'); + + return compile_protected_call(cvarGetFunc, params, &name); +#else /* !DEBUG_IR */ return compile_protected_call(cvarGetFunc, params); +#endif /* DEBUG_IR */ } Value * @@ -1210,8 +1337,17 @@ params.push_back(val); params.push_back(ConstantInt::get(Int8Ty, dynamic_class ? 1 : 0)); +#ifdef DEBUG_IR + std::string vname("[cvar:"); + vname.append(rb_id2name(name)); + vname.push_back(']'); + return CallInst::Create(cvarSetFunc, params.begin(), + params.end(), vname, bb); +#else /* !DEBUG_IR */ + return CallInst::Create(cvarSetFunc, params.begin(), params.end(), "", bb); +#endif /* DEBUG_IR */ } Value * @@ -1229,7 +1365,15 @@ params.push_back(compile_global_entry(node)); params.push_back(val); +#ifdef DEBUG_IR + std::string name("[gvar:"); + name.append(rb_id2name(node->nd_vid)); + name.push_back(']'); + + return compile_protected_call(gvarSetFunc, params, &name); +#else /* !DEBUG_IR */ return compile_protected_call(gvarSetFunc, params); +#endif /* DEBUG_IR */ } Value * @@ -1315,7 +1459,30 @@ inline Value * RoxorCompiler::compile_id(ID id) { +#ifdef DEBUG_IR + std::string name("[id:"); + name.append(rb_id2name(id)); + name.push_back(']'); + + std::map<std::string, GlobalVariable *>::iterator iter = globals.find(name); + + GlobalVariable *gvar; + if (iter == globals.end()) { + gvar = new GlobalVariable(*RoxorCompiler::module, + IntTy, false, GlobalValue::InternalLinkage, + ConstantInt::get(IntTy, (long)id), ""); + + globals[name] = gvar; + } + else { + gvar = iter->second; + } + return bb + ? new LoadInst(gvar, name, bb) + : new LoadInst(gvar, name); +#else /* !DEBUG_IR */ return ConstantInt::get(IntTy, (long)id); +#endif /* DEBUG_IR */ } Value * @@ -1333,7 +1500,14 @@ gvar = iter->second; } +#ifdef DEBUG_IR + std::string name("[id:"); + name.append(rb_id2name(id)); + name.push_back(']'); + return new LoadInst(gvar, name, bb); +#else /* !DEBUG_IR */ return new LoadInst(gvar, "", bb); +#endif /* DEBUG_IR */ } Value * @@ -1362,7 +1536,15 @@ params.push_back(compile_id(id)); params.push_back(ConstantInt::get(Int8Ty, dynamic_class ? 1 : 0)); +#ifdef DEBUG_IR + std::string name("[const:"); + name.append(rb_id2name(id)); + name.push_back(']'); + + return compile_protected_call(getConstFunc, params, &name); +#else /* !DEBUG_IR */ return compile_protected_call(getConstFunc, params); +#endif /* DEBUG_IR */ } Value * @@ -1618,7 +1800,15 @@ Value *index = ConstantInt::get(Int32Ty, idx); Value *slot = GetElementPtrInst::Create(dvars_ary, index, rb_id2name(name), bb); +#ifdef DEBUG_IR + std::string vname("[dvar:"); + vname.append(rb_id2name(name)); + vname.push_back(']'); + + return new LoadInst(slot, vname, bb); +#else /* !DEBUG_IR */ return new LoadInst(slot, "", bb); +#endif /* DEBUG_IR */ } void @@ -2017,7 +2207,7 @@ val->v.l = FIX2LONG(rval); return true; } - else if (TYPE(rval) == T_FLOAT) { + else if (FLOAT_P(rval)) { val->type = T_FLOAT; val->v.d = RFLOAT_VALUE(rval); return true; @@ -2136,6 +2326,13 @@ RoxorCompiler::compile_double_coercion(Value *val, Value *mask, BasicBlock *fallback_bb, Function *f) { + if (floatValueFunc == NULL) { + // double rb_vm_float_value(VALUE) + floatValueFunc = cast<Function> + (module->getOrInsertFunction("rb_vm_float_value", + DoubleTy, RubyObjTy, NULL)); + } + Value *is_float = new ICmpInst(*bb, ICmpInst::ICMP_EQ, mask, threeVal); BasicBlock *is_float_bb = BasicBlock::Create(context, "is_float", f); @@ -2145,13 +2342,10 @@ 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 + std::vector<Value *> params; + params.push_back(val); + Value *is_float_val = CallInst::Create(floatValueFunc, params.begin(), + params.end(), "", bb); BranchInst::Create(merge_bb, bb); bb = isnt_float_bb; @@ -2215,6 +2409,11 @@ } GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); +#ifdef DEBUG_IR + std::string name("[is_redefined:"); + name.append(sel_getName(sel)); + name.push_back(']'); +#endif /* DEBUG_IR */ Value *leftVal = params[2]; // self Value *rightVal = params.back(); @@ -2227,7 +2426,11 @@ && TYPE(leftRVal) == T_SYMBOL && TYPE(rightRVal) == T_SYMBOL) { // Both operands are symbol constants. if (sel == selEq || sel == selEqq || sel == selNeq) { +#ifdef DEBUG_IR + Value *is_redefined_val = new LoadInst(is_redefined, name, bb); +#else /* !DEBUG_IR */ Value *is_redefined_val = new LoadInst(is_redefined, "", bb); +#endif /* DEBUG_IR */ Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, is_redefined_val, ConstantInt::getFalse(context)); @@ -2267,6 +2470,13 @@ } } + if (newFloatFunc == NULL) { + // VALUE rb_float_new_retained(double) + newFloatFunc = cast<Function> + (module->getOrInsertFunction("rb_float_new_retained", + RubyObjTy, DoubleTy, NULL)); + } + rb_vm_immediate_val_t leftImm, rightImm; const bool leftIsImmediateConst = unbox_immediate_val(leftRVal, &leftImm); @@ -2289,7 +2499,7 @@ res_val = res == 1 ? trueVal : falseVal; } else if (FIXABLE(res)) { - res_val = ConstantInt::get(RubyObjTy, LONG2FIX(res)); + res_val = compile_immutable_literal(LONG2FIX(res)); } } } @@ -2306,14 +2516,17 @@ res_val = res == 1 ? trueVal : falseVal; } else { - res_val = ConstantInt::get(RubyObjTy, - DOUBLE2NUM(res)); + res_val = compile_immutable_literal(DOUBLE2NUM(res)); } } } if (res_val != NULL) { +#ifdef DEBUG_IR + Value *is_redefined_val = new LoadInst(is_redefined, name, bb); +#else /* !DEBUG_IR */ Value *is_redefined_val = new LoadInst(is_redefined, "", bb); +#endif /* DEBUG_IR */ Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, is_redefined_val, ConstantInt::getFalse(context)); @@ -2343,11 +2556,21 @@ return NULL; } else { + if (effectiveImmediateBitsFunc == NULL) { + // int rb_vm_effective_immediate_bits(VALUE) + effectiveImmediateBitsFunc = cast<Function> + (module->getOrInsertFunction("rb_vm_effective_immediate_bits", + IntTy, RubyObjTy, NULL)); + } + // Either one or both is not a constant immediate. +#ifdef DEBUG_IR + Value *is_redefined_val = new LoadInst(is_redefined, name, bb); +#else /* !DEBUG_IR */ Value *is_redefined_val = new LoadInst(is_redefined, "", bb); +#endif /* DEBUG_IR */ Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, is_redefined_val, ConstantInt::getFalse(context)); - Function *f = bb->getParent(); BasicBlock *not_redefined_bb = @@ -2363,18 +2586,57 @@ BranchInst::Create(not_redefined_bb, dispatch_bb, isOpRedefined, bb); + BasicBlock *div_by_zero_bb; + BasicBlock *optimize_fixnum_cont_bb; + BasicBlock *optimize_fixnum_ret_bb; + /* + * In the case of integer division, we need to test for the + * divisor being zero, and throw an exception if so. Because + * the BasicBlock from which the final fixnum varies depending + * on whether we are dealing with division, whether the result + * is a predicate or other cases, we use the optimize_fixnum_ret_bb + * variable to record that final BasicBlock, which we use to setup + * the PHINode (this make the code easier to decipher). + */ + bool doDIV = (sel == selDIV); + if (doDIV) { + div_by_zero_bb = + BasicBlock::Create(context, "div_by_zero", f); + optimize_fixnum_ret_bb = optimize_fixnum_cont_bb = + BasicBlock::Create(context, "op_optimize_fixnum_cont", f); + + if (zeroDivFunc == NULL) { + // void rb_num_zerodiv(void) + zeroDivFunc = cast<Function> + (module->getOrInsertFunction("rb_num_zerodiv", + VoidTy, NULL)); + zeroDivFunc->setDoesNotReturn(true); + } + + bb = div_by_zero_bb; + CallInst::Create(zeroDivFunc, "", bb); + new UnreachableInst(context, bb); + } + else { + optimize_fixnum_ret_bb = optimize_fixnum_bb; + } + bb = not_redefined_bb; Value *leftAndOp = NULL; if (!leftIsImmediateConst) { - leftAndOp = BinaryOperator::CreateAnd(leftVal, threeVal, "", - bb); + std::vector<Value *> params; + params.push_back(leftVal); + leftAndOp = CallInst::Create(effectiveImmediateBitsFunc, + params.begin(), params.end(), "", bb); } Value *rightAndOp = NULL; if (!rightIsImmediateConst) { - rightAndOp = BinaryOperator::CreateAnd(rightVal, threeVal, "", - bb); + std::vector<Value *> params; + params.push_back(rightVal); + rightAndOp = CallInst::Create(effectiveImmediateBitsFunc, + params.begin(), params.end(), "", bb); } if (leftAndOp != NULL && rightAndOp != NULL) { @@ -2416,21 +2678,29 @@ bb = optimize_fixnum_bb; - Value *unboxedLeft; - if (leftIsImmediateConst) { - unboxedLeft = ConstantInt::get(IntTy, leftImm.long_val()); + Value *unboxedRight; + if (rightIsImmediateConst) { + unboxedRight = ConstantInt::get(IntTy, rightImm.long_val()); } else { - unboxedLeft = BinaryOperator::CreateAShr(leftVal, twoVal, "", + unboxedRight = BinaryOperator::CreateAShr(rightVal, twoVal, "", bb); } - Value *unboxedRight; - if (rightIsImmediateConst) { - unboxedRight = ConstantInt::get(IntTy, rightImm.long_val()); + if (doDIV) { + Value *rightFixnumIsZero = new ICmpInst(*bb, ICmpInst::ICMP_EQ, + unboxedRight, zeroVal); + BranchInst::Create(div_by_zero_bb, optimize_fixnum_cont_bb, + rightFixnumIsZero, bb); + bb = optimize_fixnum_cont_bb; } + + Value *unboxedLeft; + if (leftIsImmediateConst) { + unboxedLeft = ConstantInt::get(IntTy, leftImm.long_val()); + } else { - unboxedRight = BinaryOperator::CreateAShr(rightVal, twoVal, "", + unboxedLeft = BinaryOperator::CreateAShr(leftVal, twoVal, "", bb); } @@ -2463,7 +2733,7 @@ BranchInst::Create(merge_bb, dispatch_bb, isFixnumMinOk, bb); fix_op_res = boxed_op_res; - optimize_fixnum_bb = fixable_max_bb; + optimize_fixnum_ret_bb = fixable_max_bb; } else { BranchInst::Create(merge_bb, bb); @@ -2493,12 +2763,10 @@ 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); + std::vector<Value *> params; + params.push_back(flp_op_res); + flp_op_res = CallInst::Create(newFloatFunc, params.begin(), + params.end(), "", bb); } optimize_float_bb = bb; BranchInst::Create(merge_bb, bb); @@ -2513,7 +2781,7 @@ bb = merge_bb; PHINode *pn = PHINode::Create(RubyObjTy, "op_tmp", bb); - pn->addIncoming(fix_op_res, optimize_fixnum_bb); + pn->addIncoming(fix_op_res, optimize_fixnum_ret_bb); pn->addIncoming(flp_op_res, optimize_float_bb); pn->addIncoming(dispatch_val, dispatch_bb); @@ -2568,7 +2836,14 @@ new_params.push_back(params[0]); // cache GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); +#ifdef DEBUG_IR + std::string name("[is_redefined:"); + name.append(sel_getName(sel)); + name.push_back(']'); + new_params.push_back(new LoadInst(is_redefined, name, bb)); +#else /* !DEBUG_IR */ new_params.push_back(new LoadInst(is_redefined, "", bb)); +#endif /* DEBUG_IR */ return compile_protected_call(opt_func, new_params); } @@ -2590,7 +2865,14 @@ GlobalVariable *is_redefined = GET_CORE()->redefined_op_gvar(sel, true); +#ifdef DEBUG_IR + std::string name("[is_redefined:"); + name.append(sel_getName(sel)); + name.push_back(']'); + Value *is_redefined_val = new LoadInst(is_redefined, name, bb); +#else /* !DEBUG_IR */ Value *is_redefined_val = new LoadInst(is_redefined, "", bb); +#endif /* DEBUG_IR */ Value *isOpRedefined = new ICmpInst(*bb, ICmpInst::ICMP_EQ, is_redefined_val, ConstantInt::getFalse(context)); @@ -2787,7 +3069,11 @@ module->getOrInsertFunction( "rb_str_new_empty", RubyObjTy, NULL)); } +#ifdef DEBUG_IR + return CallInst::Create(newString3Func, "[str:]", bb); +#else /* !DEBUG_IR */ return CallInst::Create(newString3Func, "", bb); +#endif /* DEBUG_IR */ } else { UniChar *buf = (UniChar *)CFStringGetCharactersPtr( @@ -2820,8 +3106,16 @@ params.push_back(load); params.push_back(ConstantInt::get(Int32Ty, str_len)); +#ifdef DEBUG_IR + std::string name("[str:"); + name.append(RSTRING_PTR(val)); + name.push_back(']'); return CallInst::Create(newString2Func, params.begin(), + params.end(), name, bb); +#else /* DEBUG_IR */ + return CallInst::Create(newString2Func, params.begin(), params.end(), "", bb); +#endif /* DEBUG_IR */ } } @@ -2831,7 +3125,162 @@ Value * RoxorCompiler::compile_immutable_literal(VALUE val) { +#ifdef DEBUG_IR + char buf[32]; // big enough for a double converted with %a and a FIXNUM + + std::string name("["); + + const int type = TYPE(val); + switch (type) { + case T_FALSE: + name.append("false"); + break; + + case T_NIL: + name.append("nil"); + break; + + case T_TRUE: + name.append("true"); + break; + + case T_UNDEF: + name.append("undef"); + break; + + case T_CLASS: + // This strange literal seems to be only emitted for + // `for' loops. + name.append("class:"); + name.append(class_getName((Class)val)); + break; + + case T_MODULE: + name.append("module:"); + name.append(class_getName((Class)val)); + break; + + case T_REGEXP: + name.append("re:"); + name.append(RSTRING_PTR(rb_inspect(val))); + break; + + case T_SYMBOL: + name.append("symbol:"); + name.append(rb_id2name(SYM2ID(val))); + break; + + case T_FIXNUM: + name.append("fixnum:"); + snprintf(buf, sizeof(buf), "%ld", FIX2LONG(val)); + name.append(buf); + break; + + case T_BIGNUM: + name.append("bignum:"); + name.append(RSTRING_PTR(rb_big2str(val, 10))); + break; + + case T_FLOAT: + name.append("float:"); + snprintf(buf, sizeof(buf), "%a", RFLOAT_VALUE(val)); + name.append(buf); + break; + + default: + if (rb_obj_is_kind_of(val, rb_cRange)) { + VALUE beg = 0, end = 0; + bool exclude_end = false; + rb_range_extract(val, &beg, &end, &exclude_end); + + // For literal ranges, beg and end are FIXNUMs + assert(FIXNUM_P(beg) && FIXNUM_P(end)); + name.append("range:"); + snprintf(buf, sizeof(buf), "%ld", FIX2LONG(beg)); + name.append(buf); + name.append(exclude_end ? "..." : ".."); + snprintf(buf, sizeof(buf), "%ld", FIX2LONG(end)); + name.append(buf); + } + else if (rb_obj_is_kind_of(val, rb_cEncoding)) { + name.append("enc:"); + name.append(RSTRING_PTR(rb_enc_name2( + (CFStringEncoding *)DATA_PTR(val)))); + } + else { + printf("unrecognized literal `%s' (class `%s' type %d)\n", + RSTRING_PTR(rb_inspect(val)), + rb_obj_classname(val), + TYPE(val)); + abort(); + } + break; + } + name.push_back(']'); + + std::map<std::string, GlobalVariable *>::iterator iter = globals.find(name); + + GlobalVariable *gvar; + if (iter == globals.end()) { + GC_RETAIN(val); // make sure this doesn't get garbage collected + gvar = new GlobalVariable(*RoxorCompiler::module, + RubyObjTy, false, GlobalValue::InternalLinkage, + ConstantInt::get(RubyObjTy, (long)val), ""); + + globals[name] = gvar; + switch (type) { + case T_FIXNUM: + case T_FLOAT: + global_values[gvar] = val; + break; + } + } + else { + gvar = iter->second; + } + return new LoadInst(gvar, name, bb); +#else /* !DEBUG_IR */ +#ifdef NOT_DEFINED_DUE_TO_LLVM_BUG_5026 + /* + * The following code causes an abort with llvm-82747: + * + * Don't know how to fold this instruction! + * UNREACHABLE executed at .../lib/Target/X86/X86InstrInfo.cpp:2313! + * Stack dump: + * 0. Running pass 'Linear Scan Register Allocator' on function '@__ruby_scope1179' + * + * when running the core/float spec tests: + * + * ./mspec/bin/mspec ci -B ./spec/macruby.mspec ./spec/frozen/core/float + * + * This is bug #5026, and is fixed in llvm-83656. Using TOT llvm does + * not have this problem, but that has other problems. + */ + if (FLOAT_P(val) && !FIXFLOAT_P(val)) { + if (newFloatFunc == NULL) { + // VALUE rb_float_new_retained(double) + newFloatFunc = cast<Function> + (module->getOrInsertFunction("rb_float_new_retained", + RubyObjTy, DoubleTy, NULL)); + } + + Value *d = ConstantFP::get(DoubleTy, RFLOAT_VALUE(val)); + + std::vector<Value *> params; + params.push_back(d); + + return CallInst::Create(newFloatFunc, + params.begin(), params.end(), "", bb); + } +#else /* !NOT_DEFINED_DUE_TO_LLVM_BUG_5026 */ + /* + * So to avoid crashes due to GC releasing a float literal, we now + * retain the float before converting to a ConstantInt. + */ + if (FLOAT_P(val)) GC_RETAIN(val); +#endif /* NOT_DEFINED_DUE_TO_LLVM_BUG_5026 */ return ConstantInt::get(RubyObjTy, (long)val); +#endif /* DEBUG_IR */ } Value * @@ -2846,19 +3295,104 @@ return nilVal; } - std::map<VALUE, GlobalVariable *>::iterator iter = literals.find(val); + /* + * We convert val to a string representation, so we can unique them, and + * avoid multiple GlobalVariable's of the same value. The string value + * is prefixed with a type, so that each type has its own effective + * namespace. When DEBUG_IR is defined, we also enclose the string in + * square brackets, and use it to name LLVM IR variables. + */ + char buf[32]; // big enough for a double converted with %a and a FIXNUM +#ifdef DEBUG_IR + std::string key("["); +#else /* !DEBUG_IR */ + std::string key; +#endif /* DEBUG_IR */ + + const int type = TYPE(val); + switch (type) { + case T_CLASS: + // This strange literal seems to be only emitted for + // `for' loops. + key.append("class:"); + key.append(class_getName((Class)val)); + break; + + case T_REGEXP: + { + struct RRegexp *re = (struct RRegexp *)val; + + key.append("re:"); + if (re->len > 0) key.append(RSTRING_PTR(rb_inspect(val))); + } + break; + + case T_SYMBOL: + key.append("symbol:"); + key.append(rb_id2name(SYM2ID(val))); + break; + + case T_BIGNUM: + key.append("bignum:"); + key.append(RSTRING_PTR(rb_big2str(val, 10))); + break; + + case T_FLOAT: + key.append("float:"); + snprintf(buf, sizeof(buf), "%a", RFLOAT_VALUE(val)); + key.append(buf); + break; + + default: + if (rb_obj_is_kind_of(val, rb_cRange)) { + VALUE beg = 0, end = 0; + bool exclude_end = false; + rb_range_extract(val, &beg, &end, &exclude_end); + + // For literal ranges, beg and end are FIXNUMs + assert(FIXNUM_P(beg) && FIXNUM_P(end)); + key.append("range:"); + snprintf(buf, sizeof(buf), "%ld", FIX2LONG(beg)); + key.append(buf); + key.append(exclude_end ? "..." : ".."); + snprintf(buf, sizeof(buf), "%ld", FIX2LONG(end)); + key.append(buf); + } + else { + printf("unrecognized literal `%s' (class `%s' type %d)\n", + RSTRING_PTR(rb_inspect(val)), + rb_obj_classname(val), + TYPE(val)); + abort(); + } + break; + } +#ifdef DEBUG_IR + key.push_back(']'); +#endif /* DEBUG_IR */ + + std::map<std::string, std::pair<VALUE, GlobalVariable *> >::iterator iter + = literals.find(key); GlobalVariable *gvar = NULL; if (iter == literals.end()) { gvar = new GlobalVariable(*RoxorCompiler::module, RubyObjTy, false, GlobalValue::InternalLinkage, nilVal, ""); - literals[val] = gvar; + literals[key] = std::make_pair(val, gvar); + + if (type == T_FLOAT) { + global_values[gvar] = val; + } } else { - gvar = iter->second; + gvar = iter->second.second; } +#ifdef DEBUG_IR + return new LoadInst(gvar, key, bb); +#else /* !DEBUG_IR */ return new LoadInst(gvar, "", bb); +#endif /* DEBUG_IR */ } Value * @@ -2950,7 +3484,7 @@ Value *id_val = compile_id(ivar_name); if (Instruction::classof(id_val)) { Instruction *insn = cast<Instruction>(id_val); - insn->removeFromParent(); + if (bb) insn->removeFromParent(); list.insert(list_iter, insn); } params.push_back(id_val); @@ -3167,7 +3701,14 @@ val = CallInst::Create(currentBlockObjectFunc, "", bb); current_block_arg = val; } +#ifdef DEBUG_IR + std::string name("[arg:"); + name.append(rb_id2name(id)); + name.push_back(']'); + Value *slot = new AllocaInst(RubyObjTy, name, bb); +#else /* !DEBUG_IR */ Value *slot = new AllocaInst(RubyObjTy, "", bb); +#endif /* DEBUG_IR */ new StoreInst(val, slot, bb); lvars[id] = slot; has_vars_to_save = true; @@ -3371,7 +3912,15 @@ { assert(node->nd_vid > 0); +#ifdef DEBUG_IR + std::string name("[lval:"); + name.append(rb_id2name(node->nd_vid)); + name.push_back(']'); + + return new LoadInst(compile_lvar_slot(node->nd_vid), name, bb); +#else /* !DEBUG_IR */ return new LoadInst(compile_lvar_slot(node->nd_vid), "", bb); +#endif /* DEBUG_IR */ } break; @@ -3389,7 +3938,15 @@ params.push_back(compile_global_entry(node)); +#ifdef DEBUG_IR + std::string name("[gval:"); + name.append(rb_id2name(node->nd_vid)); + name.push_back(']'); + + return CallInst::Create(gvarGetFunc, params.begin(), params.end(), name, bb); +#else /* !DEBUG_IR */ return CallInst::Create(gvarGetFunc, params.begin(), params.end(), "", bb); +#endif /* DEBUG_IR */ } break; @@ -3950,7 +4507,14 @@ params.push_back(ConstantInt::get(Int8Ty, outer && dynamic_class ? 1 : 0)); +#ifdef DEBUG_IR + std::string name("[class:"); + name.append(rb_id2name(path)); + name.push_back(']'); + classVal = compile_protected_call(defineClassFunc, params, &name); +#else /* !DEBUG_IR */ classVal = compile_protected_call(defineClassFunc, params); +#endif /* DEBUG_IR */ } NODE *body = node->nd_body; @@ -4203,7 +4767,14 @@ // because if may have a default value. ID argid = rb_intern(iter->getName().data()); Value *argslot = compile_lvar_slot(argid); +#ifdef DEBUG_IR + std::string name("[lvar:"); + name.append(iter->getName().data()); + name.push_back(']'); + params.push_back(new LoadInst(argslot, name, bb)); +#else /* !DEBUG_IR */ params.push_back(new LoadInst(argslot, "", bb)); +#endif /* DEBUG_IR */ ++i; ++iter; @@ -5446,20 +6017,24 @@ cast<Function>(module->getOrInsertFunction("rb_bignum_new_retained", RubyObjTy, PtrTy, NULL)); - Function *newFloatFunc = - cast<Function>(module->getOrInsertFunction("rb_float_from_astr_retained", - RubyObjTy, PtrTy, NULL)); + if (newFloatFunc == NULL) { + // VALUE rb_float_new_retained(double) + newFloatFunc = cast<Function> + (module->getOrInsertFunction("rb_float_new_retained", + RubyObjTy, DoubleTy, NULL)); + } Function *getClassFunc = cast<Function>(module->getOrInsertFunction("objc_getClass", RubyObjTy, PtrTy, NULL)); - for (std::map<VALUE, GlobalVariable *>::iterator i = literals.begin(); + for (std::map<std::string, std::pair<VALUE, GlobalVariable *> >::iterator i + = literals.begin(); i != literals.end(); ++i) { - VALUE val = i->first; - GlobalVariable *gvar = i->second; + VALUE val = i->second.first; + GlobalVariable *gvar = i->second.second; switch (TYPE(val)) { case T_CLASS: @@ -5584,19 +6159,10 @@ case T_FLOAT: { - const char *astr = RSTRING_PTR(rb_float_to_astr(val)); + Value *v = ConstantFP::get(DoubleTy, RFLOAT_VALUE(val)); - GlobalVariable *floatstr_gvar = - compile_const_global_string(astr); - - std::vector<Value *> idxs; - idxs.push_back(ConstantInt::get(Int32Ty, 0)); - idxs.push_back(ConstantInt::get(Int32Ty, 0)); - Instruction *load = GetElementPtrInst::Create(floatstr_gvar, - idxs.begin(), idxs.end(), ""); - std::vector<Value *> params; - params.push_back(load); + params.push_back(v); Instruction *call = CallInst::Create(newFloatFunc, params.begin(), params.end(), ""); @@ -5605,7 +6171,6 @@ list.insert(list.begin(), assign); list.insert(list.begin(), call); - list.insert(list.begin(), load); } break; @@ -6056,6 +6621,25 @@ return *cptr; } +extern "C" +double +rb_vm_float_value(VALUE obj) +{ + return RFLOAT_VALUE(obj); +} + +/* + * Besides returning the actual immediate bits, if the object is a + * struct RFloat, we return FIXFLOAT_FLAG (even though it isn't fixed), + * so we know the object is a float value. + */ +extern "C" +int +rb_vm_effective_immediate_bits(VALUE obj) +{ + return rb_effective_immediate_bits(obj); +} + static inline long rebuild_new_struct_ary(const StructType *type, VALUE orig, VALUE new_ary) { @@ -6883,7 +7467,14 @@ #if ROXOR_COMPILER_DEBUG printf("lvar %s\n", rb_id2name(id)); #endif +#ifdef DEBUG_IR + std::string name("[lvar:"); + name.append(rb_id2name(id)); + name.push_back(']'); + Value *store = new AllocaInst(RubyObjTy, name, bb); +#else /* !DEBUG_IR */ Value *store = new AllocaInst(RubyObjTy, "", bb); +#endif /* DEBUG_IR */ new StoreInst(nilVal, store, bb); lvars[id] = store; has_real_lvars = true; Modified: MacRuby/branches/ticket159/compiler.h =================================================================== --- MacRuby/branches/ticket159/compiler.h 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/compiler.h 2009-12-23 23:59:56 UTC (rev 3169) @@ -100,6 +100,10 @@ std::map<std::string, GlobalVariable *> static_strings; std::map<CFHashCode, GlobalVariable *> static_ustrings; std::map<Function *, RoxorScope *> scopes; + std::map<GlobalVariable *, VALUE> global_values; +#ifdef DEBUG_IR + std::map<std::string, GlobalVariable *> globals; +#endif /* DEBUG_IR */ #if ROXOR_COMPILER_DEBUG int level; @@ -218,6 +222,10 @@ Function *setScopeFunc; Function *setCurrentClassFunc; Function *getCacheFunc; + Function *floatValueFunc; + Function *newFloatFunc; + Function *effectiveImmediateBitsFunc; + Function *zeroDivFunc; Constant *zeroVal; Constant *oneVal; @@ -268,8 +276,13 @@ return compile_const_pointer(ptr, PtrPtrTy); } +#ifdef DEBUG_IR Instruction *compile_protected_call(Value *imp, + std::vector<Value *> ¶ms, std::string *name=NULL); +#else /* !DEBUG_IR */ + Instruction *compile_protected_call(Value *imp, std::vector<Value *> ¶ms); +#endif /* DEBUG_IR */ void compile_dispatch_arguments(NODE *args, std::vector<Value *> &arguments, int *pargc); Function::ArgumentListType::iterator compile_optional_arguments( @@ -319,9 +332,13 @@ virtual Value *compile_mcache(SEL sel, bool super); Value *compile_get_mcache(Value *sel, bool super); virtual Value *compile_ccache(ID id); +#ifdef DEBUG_IR + virtual Value *compile_sel(SEL sel, bool add_to_bb=true); +#else /* !DEBUG_IR */ virtual Value *compile_sel(SEL sel, bool add_to_bb=true) { return compile_const_pointer(sel, PtrTy); } +#endif /* DEBUG_IR */ virtual Value *compile_id(ID id); GlobalVariable *compile_const_global_string(const char *str, const size_t str_len); @@ -395,7 +412,7 @@ std::map<ID, GlobalVariable *> ids; std::map<ID, GlobalVariable *> global_entries; std::vector<GlobalVariable *> ivar_slots; - std::map<VALUE, GlobalVariable *> literals; + std::map<std::string, std::pair<VALUE, GlobalVariable *> > literals; GlobalVariable *cObject_gvar; GlobalVariable *cStandardError_gvar; Modified: MacRuby/branches/ticket159/include/ruby/intern.h =================================================================== --- MacRuby/branches/ticket159/include/ruby/intern.h 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/include/ruby/intern.h 2009-12-23 23:59:56 UTC (rev 3169) @@ -418,7 +418,6 @@ VALUE rb_objc_num_coerce_cmp(VALUE, VALUE, SEL sel); VALUE rb_num_coerce_relop(VALUE, VALUE, SEL); VALUE rb_float_new(double); -VALUE rb_float_to_astr(VALUE); VALUE rb_num2fix(VALUE); VALUE rb_fix2str(VALUE, int); VALUE rb_fix_minus(VALUE x, VALUE y); @@ -457,6 +456,7 @@ VALUE rb_Array(VALUE); double rb_cstr_to_dbl(const char*, int); double rb_str_to_dbl(VALUE, int); +bool rb_strict(void); /* parse.y */ RUBY_EXTERN int ruby_sourceline; RUBY_EXTERN char *ruby_sourcefile; Modified: MacRuby/branches/ticket159/include/ruby/ruby.h =================================================================== --- MacRuby/branches/ticket159/include/ruby/ruby.h 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/include/ruby/ruby.h 2009-12-23 23:59:56 UTC (rev 3169) @@ -254,12 +254,14 @@ #else // voodoo_float must be a function // because the parameter must be converted to float -static inline VALUE voodoo_float(float f) +static inline VALUE +voodoo_float(float f) { return *(VALUE *)(&f); } #define DBL2FIXFLOAT(d) (voodoo_float(d) | FIXFLOAT_FLAG) #endif +#define FLOAT_P(v) (rb_effective_immediate_bits((VALUE)(v)) == FIXFLOAT_FLAG) #define FIXFLOAT_P(v) (((VALUE)v & IMMEDIATE_MASK) == FIXFLOAT_FLAG) #define FIXFLOAT2DBL(v) coerce_ptr_to_double((VALUE)v) @@ -291,7 +293,8 @@ // 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; @@ -1410,6 +1413,23 @@ return BUILTIN_TYPE(obj); } +/* + * Besides returning the actual immediate bits, if the object is a + * struct RFloat, we return FIXFLOAT_FLAG (even though it isn't fixed), + * so we know the object is a float value. This is less work than calling + * TYPE() to see if it equals T_FLOAT. This is also used in the compiler; + * code is generated to call rb_effective_immediate_bits instead of simply + * ORing the last two bits. + */ +static inline int +rb_effective_immediate_bits(VALUE obj) +{ + VALUE bits = (obj & IMMEDIATE_MASK); + if (bits || !RTEST(obj)) return bits; + if (*(VALUE *)obj == rb_cFloat) return FIXFLOAT_FLAG; + return 0; +} + static inline void __ignore_stupid_gcc_warnings(void) { Modified: MacRuby/branches/ticket159/io.c =================================================================== --- MacRuby/branches/ticket159/io.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/io.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -3394,7 +3394,7 @@ } tv->tv_sec = FIX2LONG(num); } - else if (TYPE(num) == T_FLOAT) { + else if (FLOAT_P(num)) { double quantity = RFLOAT_VALUE(num); if (quantity < 0.0) { rb_raise(rb_eArgError, "select() does not accept negative timeouts."); Modified: MacRuby/branches/ticket159/marshal.c =================================================================== --- MacRuby/branches/ticket159/marshal.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/marshal.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -648,7 +648,7 @@ } #endif } - else if (FIXFLOAT_P(obj)) { + else if (FLOAT_P(obj)) { w_byte(TYPE_FLOAT, arg); w_float(RFLOAT_VALUE(obj), arg); } @@ -759,11 +759,6 @@ } break; - case T_FLOAT: - w_byte(TYPE_FLOAT, arg); - w_float(RFLOAT_VALUE(obj), arg); - break; - case T_STRING: #if WITH_OBJC w_uclass(obj, rb_objc_str_is_pure(obj), arg); Modified: MacRuby/branches/ticket159/numeric.c =================================================================== --- MacRuby/branches/ticket159/numeric.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/numeric.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -591,13 +591,17 @@ VALUE v; double d; } x = {.d = d}; - if (IMMEDIATE_P(x.v) == 0) return DBL2FIXFLOAT(d); + if (IMMEDIATE_P(x.v) == 0) return (x.v | FIXFLOAT_FLAG); #endif - NEWOBJ(flt, struct RFloat); - OBJSETUP(flt, rb_cFloat, T_FLOAT); + return rb_box_fixfloat0(d); +} - flt->float_value = d; - return (VALUE)flt; +VALUE +rb_float_new_retained(double d) +{ + VALUE val = rb_float_new(d); + GC_RETAIN(val); + return val; } /* @@ -1213,7 +1217,7 @@ static VALUE flo_eql(VALUE x, SEL sel, VALUE y) { - if (TYPE(y) == T_FLOAT) { + if (FLOAT_P(y)) { double a = RFLOAT_VALUE(x); double b = RFLOAT_VALUE(y); @@ -1462,31 +1466,6 @@ return LONG2FIX(val); } -// used to serialize/deserialize literal floats - -VALUE -rb_float_to_astr(VALUE num) -{ - char buf[32]; // should be big enough for any %a string - if (TYPE(num) != T_FLOAT) { - rb_raise(rb_eArgError, - "rb_float_to_astr called with argument that is not a float"); - } - double d = RFLOAT_VALUE(num); - snprintf(buf, sizeof(buf), "%a", d); - return rb_str_new2(buf); -} - -VALUE -rb_float_from_astr_retained(const char *s) -{ - double d = 0.0; - sscanf(s, "%la", &d); - VALUE v = DOUBLE2NUM(d); - GC_RETAIN(v); - return v; -} - /* * call-seq: * num.floor => integer @@ -1631,7 +1610,7 @@ } } } - else if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) { + else if (FLOAT_P(from) || FLOAT_P(to) || FLOAT_P(step)) { const double epsilon = DBL_EPSILON; double beg = NUM2DBL(from); double end = NUM2DBL(to); @@ -2874,8 +2853,9 @@ static VALUE bit_coerce(VALUE x) { - while (!FIXNUM_P(x) && TYPE(x) != T_BIGNUM) { - if (TYPE(x) == T_FLOAT) { + int t; + while (!FIXNUM_P(x) && (t = TYPE(x)) != T_BIGNUM) { + if (t == T_FLOAT) { rb_raise(rb_eTypeError, "can't convert Float into Integer"); } x = rb_to_int(x); Modified: MacRuby/branches/ticket159/object.c =================================================================== --- MacRuby/branches/ticket159/object.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/object.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -19,6 +19,8 @@ #include <ctype.h> #include <math.h> #include <float.h> +#include "onig/onigposix.h" // oniguruma version of regcomp/regexec +#include <libkern/OSAtomic.h> #include "objc.h" #include "vm.h" @@ -2571,6 +2573,37 @@ if (!p) return 0.0; q = p; while (ISSPACE(*p)) p++; + if (rb_strict()) { + /* + * In strict mode, before we call strtod, we need to check if the + * string begins with "0x" (a possible floating hex string) or + * "inf[inity]" or "nan" (ignoring case, and with optional preceding + * sign), and return an appropriate zero or error. + */ + static regex_t re_hex; + static regex_t re_str; + static bool inited = false; + static OSSpinLock lock = OS_SPINLOCK_INIT; + + if (!inited) { + OSSpinLockLock(&lock); + if (!inited) { + assert(regcomp(&re_hex, "^[-+]?0x", + REG_EXTENDED | REG_ICASE) == 0); + assert(regcomp(&re_str, "^[-+]?(inf|nan)", + REG_EXTENDED | REG_ICASE) == 0); + inited = true; + } + OSSpinLockUnlock(&lock); + } + if (regexec(&re_hex, p, 0, NULL, 0) == 0) { + return *p == '-' ? -0.0 : 0.0; + } + if (regexec(&re_str, p, 0, NULL, 0) == 0) { + if (badcheck) goto bad; + return 0.0; + } + } d = strtod(p, &end); if (errno == ERANGE) { OutOfRange(); Modified: MacRuby/branches/ticket159/ruby.c =================================================================== --- MacRuby/branches/ticket159/ruby.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/ruby.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -456,6 +456,36 @@ rb_warn("don't know how to dump `%.*s', (insns)", len, str); } +/* + * MacRuby strict mode. This is to disable macruby extensions, such as + * %a in sprintf or interpretation of floating point hex, "inf[inity]" and + * "nan" by to_f. + */ +static bool macruby_strict = false; +static bool macruby_strict_inited = false; + +static inline void +rb_set_strict(void) +{ + macruby_strict = true; + macruby_strict_inited = true; +} + +bool +rb_strict(void) +{ + if (!macruby_strict_inited) { + char *val = getenv("MACRUBY_STRICT"); + if (val && strcasecmp(val, "no") != 0 + && strcasecmp(val, "false") != 0 + && strcmp(val, "0") != 0) { + macruby_strict = true; + } + macruby_strict_inited = true; + } + return macruby_strict; +} + static int proc_options(int argc, char **argv, struct cmdline_options *opt) { @@ -736,6 +766,9 @@ encoding: opt->ext.enc.name = rb_str_new2(s); } + else if (strcmp("strict", s) == 0) { + rb_set_strict(); + } else if (strcmp("version", s) == 0) { opt->version = 1; } Modified: MacRuby/branches/ticket159/sprintf.c =================================================================== --- MacRuby/branches/ticket159/sprintf.c 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/sprintf.c 2009-12-23 23:59:56 UTC (rev 3169) @@ -950,6 +950,10 @@ case 'a': case 'A': + if (rb_strict()) { + rb_raise(rb_eArgError, "malformed format string - %%%c", *p); + } + /* drop through */ case 'f': case 'g': case 'G': Modified: MacRuby/branches/ticket159/vm.cpp =================================================================== --- MacRuby/branches/ticket159/vm.cpp 2009-12-23 22:08:20 UTC (rev 3168) +++ MacRuby/branches/ticket159/vm.cpp 2009-12-23 23:59:56 UTC (rev 3169) @@ -149,6 +149,7 @@ return mm->getGOTBase(); } +#if !LLVM_TOT void SetDlsymTable(void *ptr) { mm->SetDlsymTable(ptr); } @@ -156,6 +157,7 @@ void *getDlsymTable() const { return mm->getDlsymTable(); } +#endif uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize) { @@ -242,7 +244,12 @@ InitializeNativeTarget(); std::string err; +#if LLVM_TOT + ee = ExecutionEngine::createJIT(emp, &err, jmm, CodeGenOpt::None, false, + CodeModel::Default); +#else ee = ExecutionEngine::createJIT(emp, &err, jmm, CodeGenOpt::None, false); +#endif if (ee == NULL) { fprintf(stderr, "error while creating JIT: %s\n", err.c_str()); abort();
participants (1)
-
source_changes@macosforge.org