[macruby-changes] [3169] MacRuby/branches/ticket159

source_changes at macosforge.org source_changes at macosforge.org
Wed Dec 23 15:59:59 PST 2009


Revision: 3169
          http://trac.macosforge.org/projects/ruby/changeset/3169
Author:   emoy at 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 *> &params,
+	std::string *name)
+#else /* !DEBUG_IR */
 RoxorCompiler::compile_protected_call(Value *imp, std::vector<Value *> &params)
+#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 *> &params, std::string *name=NULL);
+#else /* !DEBUG_IR */
+	Instruction *compile_protected_call(Value *imp,
 		std::vector<Value *> &params);
+#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();
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20091223/68f677f3/attachment-0001.html>


More information about the macruby-changes mailing list