Created
August 7, 2012 18:25
-
-
Save evilpie/3288066 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/js/src/assembler/assembler/X86Assembler.h b/js/src/assembler/assembler/X86Assembler.h | |
--- a/js/src/assembler/assembler/X86Assembler.h | |
+++ b/js/src/assembler/assembler/X86Assembler.h | |
@@ -279,16 +279,17 @@ private: | |
OP2_ADDSD_VsdWsd = 0x58, | |
OP2_MULSD_VsdWsd = 0x59, | |
OP2_CVTSS2SD_VsdEd = 0x5A, | |
OP2_CVTSD2SS_VsdEd = 0x5A, | |
OP2_SUBSD_VsdWsd = 0x5C, | |
OP2_DIVSD_VsdWsd = 0x5E, | |
OP2_SQRTSD_VsdWsd = 0x51, | |
OP2_ANDPD_VpdWpd = 0x54, | |
+ OP2_ORPD_VpdWpd = 0x56, | |
OP2_XORPD_VpdWpd = 0x57, | |
OP2_MOVD_VdEd = 0x6E, | |
OP2_PSRLDQ_Vd = 0x73, | |
OP2_PCMPEQW = 0x75, | |
OP2_MOVD_EdVd = 0x7E, | |
OP2_JCC_rel32 = 0x80, | |
OP_SETCC = 0x90, | |
OP2_IMUL_GvEv = 0xAF, | |
@@ -2444,16 +2445,25 @@ public: | |
{ | |
js::JaegerSpew(js::JSpew_Insns, | |
IPFX "xorpd %s, %s\n", MAYBE_PAD, | |
nameFPReg(src), nameFPReg(dst)); | |
m_formatter.prefix(PRE_SSE_66); | |
m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); | |
} | |
+ void orpd_rr(XMMRegisterID src, XMMRegisterID dst) | |
+ { | |
+ js::JaegerSpew(js::JSpew_Insns, | |
+ IPFX "orpd %s, %s\n", MAYBE_PAD, | |
+ nameFPReg(src), nameFPReg(dst)); | |
+ m_formatter.prefix(PRE_SSE_66); | |
+ m_formatter.twoByteOp(OP2_ORPD_VpdWpd, (RegisterID)dst, (RegisterID)src); | |
+ } | |
+ | |
void andpd_rr(XMMRegisterID src, XMMRegisterID dst) | |
{ | |
js::JaegerSpew(js::JSpew_Insns, | |
IPFX "andpd %s, %s\n", MAYBE_PAD, | |
nameFPReg(src), nameFPReg(dst)); | |
m_formatter.prefix(PRE_SSE_66); | |
m_formatter.twoByteOp(OP2_ANDPD_VpdWpd, (RegisterID)dst, (RegisterID)src); | |
} | |
diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp | |
--- a/js/src/ion/CodeGenerator.cpp | |
+++ b/js/src/ion/CodeGenerator.cpp | |
@@ -1505,16 +1505,45 @@ CodeGenerator::visitStringLength(LString | |
Register output = ToRegister(lir->output()); | |
masm.loadPtr(lengthAndFlags, output); | |
masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), output); | |
return true; | |
} | |
bool | |
+CodeGenerator::visitMinMaxI(LMinMaxI *ins) | |
+{ | |
+ Register first = ToRegister(ins->first()); | |
+ Register output = ToRegister(ins->output()); | |
+ | |
+ JS_ASSERT(first == output); | |
+ | |
+ if (ins->second()->isConstant()) | |
+ masm.cmp32(first, Imm32(ToInt32(ins->second()))); | |
+ else | |
+ masm.cmp32(first, ToRegister(ins->second())); | |
+ | |
+ Label done; | |
+ if (ins->mir()->isMax()) | |
+ masm.j(Assembler::GreaterThan, &done); | |
+ else | |
+ masm.j(Assembler::LessThan, &done); | |
+ | |
+ if (ins->second()->isConstant()) | |
+ masm.move32(Imm32(ToInt32(ins->second())), output); | |
+ else | |
+ masm.mov(ToRegister(ins->second()), output); | |
+ | |
+ | |
+ masm.bind(&done); | |
+ return true; | |
+} | |
+ | |
+bool | |
CodeGenerator::visitAbsI(LAbsI *ins) | |
{ | |
Register input = ToRegister(ins->input()); | |
Label positive; | |
JS_ASSERT(input == ToRegister(ins->output())); | |
masm.test32(input, input); | |
masm.j(Assembler::GreaterThanOrEqual, &positive); | |
diff --git a/js/src/ion/CodeGenerator.h b/js/src/ion/CodeGenerator.h | |
--- a/js/src/ion/CodeGenerator.h | |
+++ b/js/src/ion/CodeGenerator.h | |
@@ -104,16 +104,17 @@ class CodeGenerator : public CodeGenerat | |
bool visitLoadFixedSlotT(LLoadFixedSlotT *ins); | |
bool visitStoreFixedSlotV(LStoreFixedSlotV *ins); | |
bool visitStoreFixedSlotT(LStoreFixedSlotT *ins); | |
bool visitAbsI(LAbsI *lir); | |
bool visitPowI(LPowI *lir); | |
bool visitPowD(LPowD *lir); | |
bool visitMathFunctionD(LMathFunctionD *ins); | |
bool visitModD(LModD *ins); | |
+ bool visitMinMaxI(LMinMaxI *lir); | |
bool visitBinaryV(LBinaryV *lir); | |
bool visitCompareS(LCompareS *lir); | |
bool visitCompareV(LCompareV *lir); | |
bool visitIsNullOrUndefined(LIsNullOrUndefined *lir); | |
bool visitIsNullOrUndefinedAndBranch(LIsNullOrUndefinedAndBranch *lir); | |
bool visitConcat(LConcat *lir); | |
bool visitCharCodeAt(LCharCodeAt *lir); | |
bool visitFromCharCode(LFromCharCode *lir); | |
diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h | |
--- a/js/src/ion/IonBuilder.h | |
+++ b/js/src/ion/IonBuilder.h | |
@@ -376,16 +376,17 @@ class IonBuilder : public MIRGenerator | |
InliningStatus inlineArrayPopShift(MArrayPopShift::Mode mode, uint32 argc, bool constructing); | |
InliningStatus inlineArrayPush(uint32 argc, bool constructing); | |
// Math natives. | |
InliningStatus inlineMathAbs(uint32 argc, bool constructing); | |
InliningStatus inlineMathFloor(uint32 argc, bool constructing); | |
InliningStatus inlineMathRound(uint32 argc, bool constructing); | |
InliningStatus inlineMathSqrt(uint32 argc, bool constructing); | |
+ InliningStatus inlineMathMinMax(bool max, uint32 argc, bool constructing); | |
InliningStatus inlineMathPow(uint32 argc, bool constructing); | |
InliningStatus inlineMathFunction(MMathFunction::Function function, uint32 argc, | |
bool constructing); | |
// String natives. | |
InliningStatus inlineStrCharCodeAt(uint32 argc, bool constructing); | |
InliningStatus inlineStrFromCharCode(uint32 argc, bool constructing); | |
InliningStatus inlineStrCharAt(uint32 argc, bool constructing); | |
diff --git a/js/src/ion/IonCaches.cpp b/js/src/ion/IonCaches.cpp | |
--- a/js/src/ion/IonCaches.cpp | |
+++ b/js/src/ion/IonCaches.cpp | |
@@ -783,16 +783,19 @@ js::ion::GetElementCache(JSContext *cx, | |
// Override the return value if we are invalidated (bug 728188). | |
AutoDetectInvalidation adi(cx, res.address(), ion); | |
RootedId id(cx); | |
if (!FetchElementId(cx, obj, idval, id.address(), res)) | |
return false; | |
+ obj->dump(); | |
+ js_DumpValue(idval); | |
+ | |
if (cache.stubCount() < MAX_STUBS) { | |
if (obj->isNative() && cache.monitoredResult()) { | |
cache.incrementStubCount(); | |
uint32_t dummy; | |
if (idval.isString() && JSID_IS_ATOM(id) && !JSID_TO_ATOM(id)->isIndex(&dummy)) { | |
if (!cache.attachGetProp(cx, obj, idval, JSID_TO_ATOM(id)->asPropertyName())) | |
return false; | |
diff --git a/js/src/ion/LIR-Common.h b/js/src/ion/LIR-Common.h | |
--- a/js/src/ion/LIR-Common.h | |
+++ b/js/src/ion/LIR-Common.h | |
@@ -1117,16 +1117,64 @@ class LBinaryMath : public LInstructionH | |
const LAllocation *lhs() { | |
return this->getOperand(0); | |
} | |
const LAllocation *rhs() { | |
return this->getOperand(1); | |
} | |
}; | |
+class LMinMaxI : public LInstructionHelper<1, 2, 0> | |
+{ | |
+ public: | |
+ LIR_HEADER(MinMaxI); | |
+ LMinMaxI(const LAllocation &first, const LAllocation &second) | |
+ { | |
+ setOperand(0, first); | |
+ setOperand(1, second); | |
+ } | |
+ | |
+ const LAllocation *first() { | |
+ return this->getOperand(0); | |
+ } | |
+ const LAllocation *second() { | |
+ return this->getOperand(1); | |
+ } | |
+ const LDefinition *output() { | |
+ return this->getDef(0); | |
+ } | |
+ MMinMax *mir() const { | |
+ return mir_->toMinMax(); | |
+ } | |
+}; | |
+ | |
+class LMinMaxD : public LInstructionHelper<1, 2, 0> | |
+{ | |
+ public: | |
+ LIR_HEADER(MinMaxD); | |
+ LMinMaxD(const LAllocation &first, const LAllocation &second) | |
+ { | |
+ setOperand(0, first); | |
+ setOperand(1, second); | |
+ } | |
+ | |
+ const LAllocation *first() { | |
+ return this->getOperand(0); | |
+ } | |
+ const LAllocation *second() { | |
+ return this->getOperand(1); | |
+ } | |
+ const LDefinition *output() { | |
+ return this->getDef(0); | |
+ } | |
+ MMinMax *mir() const { | |
+ return mir_->toMinMax(); | |
+ } | |
+}; | |
+ | |
// Absolute value of an integer. | |
class LAbsI : public LInstructionHelper<1, 1, 0> | |
{ | |
public: | |
LIR_HEADER(AbsI); | |
LAbsI(const LAllocation &num) { | |
setOperand(0, num); | |
} | |
diff --git a/js/src/ion/LOpcodes.h b/js/src/ion/LOpcodes.h | |
--- a/js/src/ion/LOpcodes.h | |
+++ b/js/src/ion/LOpcodes.h | |
@@ -54,16 +54,18 @@ | |
_(CompareS) \ | |
_(CompareV) \ | |
_(CompareAndBranch) \ | |
_(CompareDAndBranch) \ | |
_(CompareB) \ | |
_(CompareBAndBranch) \ | |
_(IsNullOrUndefined) \ | |
_(IsNullOrUndefinedAndBranch) \ | |
+ _(MinMaxI) \ | |
+ _(MinMaxD) \ | |
_(AbsI) \ | |
_(AbsD) \ | |
_(SqrtD) \ | |
_(PowI) \ | |
_(PowD) \ | |
_(MathFunctionD) \ | |
_(NotI) \ | |
_(NotD) \ | |
diff --git a/js/src/ion/Lowering.cpp b/js/src/ion/Lowering.cpp | |
--- a/js/src/ion/Lowering.cpp | |
+++ b/js/src/ion/Lowering.cpp | |
@@ -647,16 +647,32 @@ LIRGenerator::visitRound(MRound *ins) | |
JS_ASSERT(ins->num()->type() == MIRType_Double); | |
LRound *lir = new LRound(useRegister(ins->num()), tempFloat()); | |
if (!assignSnapshot(lir)) | |
return false; | |
return define(lir, ins); | |
} | |
bool | |
+LIRGenerator::visitMinMax(MMinMax *ins) | |
+{ | |
+ MDefinition *first = ins->getOperand(0); | |
+ MDefinition *second = ins->getOperand(1); | |
+ | |
+ if (ins->specialization() == MIRType_Int32) { | |
+ ReorderCommutative(&first, &second); | |
+ LMinMaxI *lir = new LMinMaxI(useRegisterAtStart(first), useRegisterOrConstant(second)); | |
+ return defineReuseInput(lir, ins, 0); | |
+ } else { | |
+ LMinMaxD *lir = new LMinMaxD(useRegisterAtStart(first), useRegister(second)); | |
+ return defineReuseInput(lir, ins, 0); | |
+ } | |
+} | |
+ | |
+bool | |
LIRGenerator::visitAbs(MAbs *ins) | |
{ | |
MDefinition *num = ins->num(); | |
if (num->type() == MIRType_Int32) { | |
LAbsI *lir = new LAbsI(useRegisterAtStart(num)); | |
// needed to handle abs(INT32_MIN) | |
if (!ins->range()->isFinite() && !assignSnapshot(lir)) | |
diff --git a/js/src/ion/Lowering.h b/js/src/ion/Lowering.h | |
--- a/js/src/ion/Lowering.h | |
+++ b/js/src/ion/Lowering.h | |
@@ -99,16 +99,17 @@ class LIRGenerator : public LIRGenerator | |
bool visitBitAnd(MBitAnd *ins); | |
bool visitBitOr(MBitOr *ins); | |
bool visitBitXor(MBitXor *ins); | |
bool visitLsh(MLsh *ins); | |
bool visitRsh(MRsh *ins); | |
bool visitUrsh(MUrsh *ins); | |
bool visitFloor(MFloor *ins); | |
bool visitRound(MRound *ins); | |
+ bool visitMinMax(MMinMax *ins); | |
bool visitAbs(MAbs *ins); | |
bool visitSqrt(MSqrt *ins); | |
bool visitPow(MPow *ins); | |
bool visitMathFunction(MMathFunction *ins); | |
bool visitAdd(MAdd *ins); | |
bool visitSub(MSub *ins); | |
bool visitMul(MMul *ins); | |
bool visitDiv(MDiv *ins); | |
diff --git a/js/src/ion/MCallOptimize.cpp b/js/src/ion/MCallOptimize.cpp | |
--- a/js/src/ion/MCallOptimize.cpp | |
+++ b/js/src/ion/MCallOptimize.cpp | |
@@ -32,16 +32,20 @@ IonBuilder::inlineNativeCall(JSNative na | |
if (native == js_math_abs) | |
return inlineMathAbs(argc, constructing); | |
if (native == js_math_floor) | |
return inlineMathFloor(argc, constructing); | |
if (native == js_math_round) | |
return inlineMathRound(argc, constructing); | |
if (native == js_math_sqrt) | |
return inlineMathSqrt(argc, constructing); | |
+ if (native == js_math_max) | |
+ return inlineMathMinMax(true /* max */, argc, constructing); | |
+ if (native == js_math_min) | |
+ return inlineMathMinMax(false /* max */, argc, constructing); | |
if (native == js_math_pow) | |
return inlineMathPow(argc, constructing); | |
if (native == js::math_sin) | |
return inlineMathFunction(MMathFunction::Sin, argc, constructing); | |
if (native == js::math_cos) | |
return inlineMathFunction(MMathFunction::Cos, argc, constructing); | |
if (native == js::math_tan) | |
return inlineMathFunction(MMathFunction::Tan, argc, constructing); | |
@@ -535,16 +539,50 @@ IonBuilder::inlineMathPow(uint32 argc, b | |
MPow *ins = MPow::New(argv[1], argv[2], arg2Type); | |
current->add(ins); | |
current->push(ins); | |
return InliningStatus_Inlined; | |
} | |
IonBuilder::InliningStatus | |
+IonBuilder::inlineMathMinMax(bool max, uint32 argc, bool constructing) | |
+{ | |
+ if (argc != 2 || constructing) | |
+ return InliningStatus_NotInlined; | |
+ | |
+ MIRType returnType = getInlineReturnType(); | |
+ if (returnType != MIRType_Double && returnType != MIRType_Int32) | |
+ return InliningStatus_NotInlined; | |
+ | |
+ MIRType arg1Type = getInlineArgType(argc, 1); | |
+ if (arg1Type != MIRType_Double && arg1Type != MIRType_Int32) | |
+ return InliningStatus_NotInlined; | |
+ MIRType arg2Type = getInlineArgType(argc, 2); | |
+ if (arg2Type != MIRType_Double && arg2Type != MIRType_Int32) | |
+ return InliningStatus_NotInlined; | |
+ | |
+ if (returnType == MIRType_Int32 && | |
+ (arg1Type == MIRType_Double || arg2Type == MIRType_Double)) | |
+ { | |
+ // We would need to inform TI, if we happen to return a double. | |
+ return InliningStatus_NotInlined; | |
+ } | |
+ | |
+ MDefinitionVector argv; | |
+ if (!discardCall(argc, argv, current)) | |
+ return InliningStatus_Error; | |
+ | |
+ MMinMax *ins = MMinMax::New(argv[1], argv[2], returnType, max); | |
+ current->add(ins); | |
+ current->push(ins); | |
+ return InliningStatus_Inlined; | |
+} | |
+ | |
+IonBuilder::InliningStatus | |
IonBuilder::inlineStrCharCodeAt(uint32 argc, bool constructing) | |
{ | |
if (argc != 1 || constructing) | |
return InliningStatus_NotInlined; | |
if (getInlineReturnType() != MIRType_Int32) | |
return InliningStatus_NotInlined; | |
if (getInlineArgType(argc, 0) != MIRType_String) | |
diff --git a/js/src/ion/MIR.h b/js/src/ion/MIR.h | |
--- a/js/src/ion/MIR.h | |
+++ b/js/src/ion/MIR.h | |
@@ -2117,16 +2117,61 @@ class MBinaryArithInstruction | |
} | |
AliasSet getAliasSet() const { | |
if (specialization_ >= MIRType_Object) | |
return AliasSet::Store(AliasSet::Any); | |
return AliasSet::None(); | |
} | |
}; | |
+class MMinMax | |
+ : public MBinaryInstruction, | |
+ public ArithPolicy | |
+{ | |
+ bool isMax_; | |
+ | |
+ MMinMax(MDefinition *left, MDefinition *right, MIRType type, bool isMax) | |
+ : MBinaryInstruction(left, right), | |
+ isMax_(isMax) | |
+ { | |
+ JS_ASSERT(type == MIRType_Double || type == MIRType_Int32); | |
+ setResultType(type); | |
+ setMovable(); | |
+ specialization_ = type; | |
+ } | |
+ | |
+ public: | |
+ INSTRUCTION_HEADER(MinMax); | |
+ static MMinMax *New(MDefinition *left, MDefinition *right, MIRType type, bool isMax) { | |
+ return new MMinMax(left, right, type, isMax); | |
+ } | |
+ | |
+ bool isMax() const { | |
+ return isMax_; | |
+ } | |
+ MIRType specialization() const { | |
+ return specialization_; | |
+ } | |
+ | |
+ TypePolicy *typePolicy() { | |
+ return this; | |
+ } | |
+ bool congruentTo(MDefinition *const &ins) const { | |
+ if (!ins->isMinMax()) | |
+ return false; | |
+ if (isMax() != ins->toMinMax()->isMax()) | |
+ return false; | |
+ return congruentIfOperandsEqual(ins); | |
+ } | |
+ | |
+ AliasSet getAliasSet() const { | |
+ return AliasSet::None(); | |
+ } | |
+}; | |
+ | |
class MAbs | |
: public MUnaryInstruction, | |
public ArithPolicy | |
{ | |
MAbs(MDefinition *num, MIRType type) | |
: MUnaryInstruction(num) | |
{ | |
JS_ASSERT(type == MIRType_Double || type == MIRType_Int32); | |
diff --git a/js/src/ion/MOpcodes.h b/js/src/ion/MOpcodes.h | |
--- a/js/src/ion/MOpcodes.h | |
+++ b/js/src/ion/MOpcodes.h | |
@@ -38,16 +38,17 @@ namespace ion { | |
_(TypeOf) \ | |
_(ToId) \ | |
_(BitAnd) \ | |
_(BitOr) \ | |
_(BitXor) \ | |
_(Lsh) \ | |
_(Rsh) \ | |
_(Ursh) \ | |
+ _(MinMax) \ | |
_(Abs) \ | |
_(Sqrt) \ | |
_(Pow) \ | |
_(PowHalf) \ | |
_(MathFunction) \ | |
_(Add) \ | |
_(Sub) \ | |
_(Mul) \ | |
diff --git a/js/src/ion/arm/CodeGenerator-arm.cpp b/js/src/ion/arm/CodeGenerator-arm.cpp | |
--- a/js/src/ion/arm/CodeGenerator-arm.cpp | |
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp | |
@@ -271,16 +271,106 @@ CodeGeneratorARM::visitOutOfLineBailout( | |
masm.ma_mov(Imm32(ool->snapshot()->snapshotOffset()), ScratchRegister); | |
masm.ma_push(ScratchRegister); | |
masm.ma_push(ScratchRegister); | |
masm.ma_b(deoptLabel_); | |
return true; | |
} | |
bool | |
+CodeGeneratorARM::visitMinMaxD(LMinMaxD *ins) | |
+{ | |
+ FloatRegister first = ToFloatRegister(ins->first()); | |
+ FloatRegister second = ToFloatRegister(ins->second()); | |
+ FloatRegister output = ToFloatRegister(ins->output()); | |
+ | |
+ JS_ASSERT(first == output); | |
+ | |
+ Assembler::Condition cond = ins->mir()->isMax() | |
+ ? VFP_GreaterThanOrEqual | |
+ : VFP_LessThanOrEqual; | |
+ Label nan, equal, returnSecond, done; | |
+ | |
+ masm.compareDouble(first, second); | |
+ masm.ma_b(&nan, VFP_Unordered); // first or second is NaN, result is NaN. | |
+ masm.ma_b(&equal, VFP_Equal); // make sure we handle -0 and 0 right. | |
+ masm.ma_b(&returnSecond, cond); | |
+ masm.ma_b(&done); | |
+ | |
+ // Check for zero. | |
+ masm.bind(&equal); | |
+ masm.compareDouble(first, InvalidFloatReg); | |
+ masm.ma_b(&done, VFP_NotEqual); // first wasn't 0 or -0, so just return it. | |
+ // So now both operands are either -0 or 0. | |
+ if (ins->mir()->isMax()) { | |
+ masm.ma_vadd(second, first, first); // -0 + -0 = -0 and -0 + 0 = 0. | |
+ } else { | |
+ masm.ma_vneg(first, first); | |
+ masm.ma_vsub(first, second, first); | |
+ masm.ma_vneg(first, first); | |
+ } | |
+ masm.jmp(&done); | |
+ | |
+ masm.bind(&nan); | |
+ masm.loadStaticDouble(&js_NaN, output); | |
+ masm.jmp(&done); | |
+ | |
+ masm.bind(&returnSecond); | |
+ masm.movsd(second, output); | |
+ | |
+ masm.bind(&done); | |
+ return true; | |
+ | |
+ Assembler::Condition cond = ins->mir()->isMax() | |
+ ? Assembler::Above | |
+ : Assembler::Below; | |
+ | |
+ | |
+ | |
+ Label nan, secondCheck, normal, done; | |
+ | |
+ masm.compareDouble(first, second); | |
+ masm.ma_b(&nan, VFP_Unordered); // If one argument is NaN the result is NaN. | |
+ masm.ma_b(&secondCheck, VFP_NotEqual); | |
+ | |
+ masm.compareDouble(second, InvalidFloatReg); | |
+ masm.ma_b(&nan, VFP_Unordered); // Second argument was NaN. | |
+ masm.ma_b(&normal, VFP_NotEqual); | |
+ | |
+ // We need to check which argument could be -0. | |
+ { | |
+ if (ins->mir()->isMax()) | |
+ masm.branchTestNegativeZero(Assembler::NotEqual, first, &done); | |
+ else | |
+ masm.branchTestNegativeZero(Assembler::Equal, first, &done); | |
+ masm.moveDouble(second, output); | |
+ masm.ma_b(&done); | |
+ } | |
+ | |
+ masm.bind(&nan); | |
+ masm.loadStaticDouble(&js_NaN, output); | |
+ masm.ma_b(&done); | |
+ | |
+ masm.bind(&secondCheck); | |
+ // first is not NaN and not zero so we only need to check if second is NaN. | |
+ masm.compareDouble(second, InvalidFloatReg); | |
+ masm.ma_b(&nan, VFP_Unordered); | |
+ | |
+ masm.bind(&normal); | |
+ if (ins->mir()->isMax()) | |
+ masm.branchDouble(Assembler::DoubleGreaterThan, first, second, &done); | |
+ else | |
+ masm.branchDouble(Assembler::DoubleLessThan, first, second, &done); | |
+ masm.moveDouble(second, output); | |
+ | |
+ masm.bind(&done); | |
+ return true; | |
+} | |
+ | |
+bool | |
CodeGeneratorARM::visitAbsD(LAbsD *ins) | |
{ | |
FloatRegister input = ToFloatRegister(ins->input()); | |
JS_ASSERT(input == ToFloatRegister(ins->output())); | |
masm.as_vabs(input, input); | |
return true; | |
} | |
diff --git a/js/src/ion/arm/CodeGenerator-arm.h b/js/src/ion/arm/CodeGenerator-arm.h | |
--- a/js/src/ion/arm/CodeGenerator-arm.h | |
+++ b/js/src/ion/arm/CodeGenerator-arm.h | |
@@ -61,16 +61,17 @@ class CodeGeneratorARM : public CodeGene | |
void emitSet(Assembler::Condition cond, const Register &dest); | |
// Emits a branch that directs control flow to the true block if |cond| is | |
// true, and the false block if |cond| is false. | |
void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse); | |
public: | |
// Instruction visitors. | |
+ virtual bool visitMinMaxD(LMinMaxD *ins); | |
virtual bool visitAbsD(LAbsD *ins); | |
virtual bool visitSqrtD(LSqrtD *ins); | |
virtual bool visitAddI(LAddI *ins); | |
virtual bool visitSubI(LSubI *ins); | |
virtual bool visitBitNotI(LBitNotI *ins); | |
virtual bool visitBitOpI(LBitOpI *ins); | |
virtual bool visitMulI(LMulI *ins); | |
diff --git a/js/src/ion/arm/MacroAssembler-arm.cpp b/js/src/ion/arm/MacroAssembler-arm.cpp | |
--- a/js/src/ion/arm/MacroAssembler-arm.cpp | |
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp | |
@@ -1922,16 +1922,25 @@ MacroAssemblerARMCompat::branchDouble(Do | |
ma_b(label, VFP_Unordered); | |
ma_b(label, VFP_Equal); | |
return; | |
} | |
ma_b(label, ConditionFromDoubleCondition(cond)); | |
} | |
+void | |
+MacroAssemblerARMCompat::branchTestNegativeZero(Assembler::Condition cond, const FloatRegister &input, | |
+ Label *label) | |
+{ | |
+ as_vxfer(ScratchRegister, InvalidReg, input, FloatToCore, Always, 1); | |
+ as_tst(ScratchRegister, O2Reg(ScratchRegister)); | |
+ as_b(label, InvertCondition(cond)); | |
+} | |
+ | |
// higher level tag testing code | |
Operand ToPayload(Operand base) { | |
return Operand(Register::FromCode(base.base()), | |
base.disp()); | |
} | |
Operand ToType(Operand base) { | |
return Operand(Register::FromCode(base.base()), | |
base.disp() + sizeof(void *)); | |
diff --git a/js/src/ion/arm/MacroAssembler-arm.h b/js/src/ion/arm/MacroAssembler-arm.h | |
--- a/js/src/ion/arm/MacroAssembler-arm.h | |
+++ b/js/src/ion/arm/MacroAssembler-arm.h | |
@@ -954,16 +954,20 @@ class MacroAssemblerARMCompat : public M | |
} | |
void storeDouble(FloatRegister src, BaseIndex addr) { | |
// Harder cases not handled yet. | |
JS_ASSERT(addr.offset == 0); | |
uint32 scale = Imm32::ShiftOf(addr.scale).value; | |
ma_vstr(src, addr.base, addr.index, scale); | |
} | |
+ void moveDouble(FloatRegister src, FloatRegister dest) { | |
+ ma_vmov(src, dest); | |
+ } | |
+ | |
void storeFloat(FloatRegister src, Address addr) { | |
ma_vstr(VFPRegister(src).singleOverlay(), Operand(addr)); | |
} | |
void storeFloat(FloatRegister src, BaseIndex addr) { | |
// Harder cases not handled yet. | |
JS_ASSERT(addr.offset == 0); | |
uint32 scale = Imm32::ShiftOf(addr.scale).value; | |
ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale); | |
@@ -993,16 +997,18 @@ class MacroAssemblerARMCompat : public M | |
void setStackArg(const Register ®, uint32 arg); | |
void breakpoint(); | |
void compareDouble(FloatRegister lhs, FloatRegister rhs); | |
void branchDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs, | |
Label *label); | |
+ void branchTestNegativeZero(Condition cond, const FloatRegister &input, Label *label); | |
+ | |
void checkStackAlignment(); | |
void rshiftPtr(Imm32 imm, Register dest) { | |
ma_lsr(imm, dest, dest); | |
} | |
void lshiftPtr(Imm32 imm, Register dest) { | |
ma_lsl(imm, dest, dest); | |
} | |
diff --git a/js/src/ion/shared/Assembler-x86-shared.h b/js/src/ion/shared/Assembler-x86-shared.h | |
--- a/js/src/ion/shared/Assembler-x86-shared.h | |
+++ b/js/src/ion/shared/Assembler-x86-shared.h | |
@@ -1050,16 +1050,19 @@ class AssemblerX86Shared | |
masm.mulsd_rr(src.code(), dest.code()); | |
} | |
void divsd(const FloatRegister &src, const FloatRegister &dest) { | |
masm.divsd_rr(src.code(), dest.code()); | |
} | |
void xorpd(const FloatRegister &src, const FloatRegister &dest) { | |
masm.xorpd_rr(src.code(), dest.code()); | |
} | |
+ void orpd(const FloatRegister &src, const FloatRegister &dest) { | |
+ masm.orpd_rr(src.code(), dest.code()); | |
+ } | |
void andpd(const FloatRegister &src, const FloatRegister &dest) { | |
masm.andpd_rr(src.code(), dest.code()); | |
} | |
void sqrtsd(const FloatRegister &src, const FloatRegister &dest) { | |
masm.sqrtsd_rr(src.code(), dest.code()); | |
} | |
void roundsd(const FloatRegister &src, const FloatRegister &dest, | |
JSC::X86Assembler::RoundingMode mode) | |
diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.cpp b/js/src/ion/shared/CodeGenerator-x86-shared.cpp | |
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp | |
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp | |
@@ -397,16 +397,60 @@ CodeGeneratorX86Shared::visitOutOfLineBa | |
if (!deoptLabel_) | |
deoptLabel_ = new HeapLabel(); | |
masm.push(Imm32(ool->snapshot()->snapshotOffset())); | |
masm.jmp(deoptLabel_); | |
return true; | |
} | |
+ | |
+bool | |
+CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins) | |
+{ | |
+ FloatRegister first = ToFloatRegister(ins->first()); | |
+ FloatRegister second = ToFloatRegister(ins->second()); | |
+ FloatRegister output = ToFloatRegister(ins->output()); | |
+ | |
+ JS_ASSERT(first == output); | |
+ | |
+ Assembler::Condition cond = ins->mir()->isMax() | |
+ ? Assembler::Above | |
+ : Assembler::Below; | |
+ Label nan, equal, returnSecond, done; | |
+ | |
+ masm.ucomisd(second, first); | |
+ masm.j(Assembler::Parity, &nan); // first or second is NaN, result is NaN. | |
+ masm.j(Assembler::Equal, &equal); // make sure we handle -0 and 0 right. | |
+ masm.j(cond, &returnSecond); | |
+ masm.jmp(&done); | |
+ | |
+ // Check for zero. | |
+ masm.bind(&equal); | |
+ masm.xorpd(ScratchFloatReg, ScratchFloatReg); | |
+ masm.ucomisd(first, ScratchFloatReg); | |
+ masm.j(Assembler::NotEqual, &done); // first wasn't 0 or -0, so just return it. | |
+ // So now both operands are either -0 or 0. | |
+ if (ins->mir()->isMax()) | |
+ masm.addsd(second, first); // -0 + -0 = -0 and -0 + 0 = 0. | |
+ else | |
+ masm.orpd(second, first); // This just ors the sign bit. | |
+ masm.jmp(&done); | |
+ | |
+ masm.bind(&nan); | |
+ masm.loadStaticDouble(&js_NaN, output); | |
+ masm.jmp(&done); | |
+ | |
+ masm.bind(&returnSecond); | |
+ masm.movsd(second, output); | |
+ | |
+ masm.bind(&done); | |
+ return true; | |
+} | |
+ | |
bool | |
CodeGeneratorX86Shared::visitAbsD(LAbsD *ins) | |
{ | |
FloatRegister input = ToFloatRegister(ins->input()); | |
JS_ASSERT(input == ToFloatRegister(ins->output())); | |
masm.xorpd(ScratchFloatReg, ScratchFloatReg); | |
masm.subsd(input, ScratchFloatReg); // negate the sign bit. | |
masm.andpd(ScratchFloatReg, input); // s & ~s | |
diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.h b/js/src/ion/shared/CodeGenerator-x86-shared.h | |
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h | |
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h | |
@@ -80,16 +80,17 @@ class CodeGeneratorX86Shared : public Co | |
NaNCond ifNaN = NaN_Unexpected); | |
void emitBranch(Assembler::DoubleCondition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse); | |
public: | |
CodeGeneratorX86Shared(MIRGenerator *gen, LIRGraph &graph); | |
public: | |
// Instruction visitors. | |
+ virtual bool visitMinMaxD(LMinMaxD *ins); | |
virtual bool visitAbsD(LAbsD *ins); | |
virtual bool visitSqrtD(LSqrtD *ins); | |
virtual bool visitPowHalfD(LPowHalfD *ins); | |
virtual bool visitAddI(LAddI *ins); | |
virtual bool visitSubI(LSubI *ins); | |
virtual bool visitMulI(LMulI *ins); | |
virtual bool visitDivI(LDivI *ins); | |
virtual bool visitModI(LModI *ins); | |
diff --git a/js/src/ion/shared/MacroAssembler-x86-shared.h b/js/src/ion/shared/MacroAssembler-x86-shared.h | |
--- a/js/src/ion/shared/MacroAssembler-x86-shared.h | |
+++ b/js/src/ion/shared/MacroAssembler-x86-shared.h | |
@@ -242,16 +242,19 @@ class MacroAssemblerX86Shared : public A | |
movsd(Operand(src), dest); | |
} | |
void storeDouble(FloatRegister src, const Address &dest) { | |
movsd(src, Operand(dest)); | |
} | |
void storeDouble(FloatRegister src, const BaseIndex &dest) { | |
movsd(src, Operand(dest)); | |
} | |
+ void moveDouble(FloatRegister src, FloatRegister dest) { | |
+ movsd(src, dest); | |
+ } | |
void zeroDouble(FloatRegister reg) { | |
xorpd(reg, reg); | |
} | |
void addDouble(FloatRegister src, FloatRegister dest) { | |
addsd(src, dest); | |
} | |
void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest) { | |
cvtsd2ss(src, dest); | |
@@ -270,16 +273,28 @@ class MacroAssemblerX86Shared : public A | |
} | |
void storeFloat(FloatRegister src, const Address &dest) { | |
movss(src, Operand(dest)); | |
} | |
void storeFloat(FloatRegister src, const BaseIndex &dest) { | |
movss(src, Operand(dest)); | |
} | |
+ void branchTestNegativeZero(Condition cond, const FloatRegister &input, Label *label) { | |
+ JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual); | |
+ | |
+ movsd(input, ScratchFloatReg); | |
+ // Move the sign bit into some position so it's not NaN. (It's 2.0 when -0.0) | |
+ psrlq(Imm32(1), ScratchFloatReg); | |
+ // If |input| was 0 it is still going to compare equal to ScratchFloatReg. | |
+ compareDouble(Assembler::DoubleEqual, input, ScratchFloatReg); | |
+ | |
+ j(InvertCondition(cond), label); | |
+ } | |
+ | |
void clampIntToUint8(Register src, Register dest) { | |
Label inRange, done; | |
branchTest32(Assembler::Zero, src, Imm32(0xffffff00), &inRange); | |
{ | |
Label negative; | |
branchTest32(Assembler::Signed, src, src, &negative); | |
{ | |
movl(Imm32(255), dest); | |
diff --git a/js/src/jit-test/tests/ion/mathMinMax.js b/js/src/jit-test/tests/ion/mathMinMax.js | |
new file mode 100644 | |
--- /dev/null | |
+++ b/js/src/jit-test/tests/ion/mathMinMax.js | |
@@ -0,0 +1,39 @@ | |
+var nan = Number.NaN; | |
+var negative_zero = -0; | |
+ | |
+function max(a, b) { | |
+ return Math.max(a, b); | |
+} | |
+function min(a, b) { | |
+ return Math.min(a, b); | |
+} | |
+ | |
+function main() { | |
+ for (var i = 0; i < 100; i++) { | |
+ assertEq(max(negative_zero, 0), 0); | |
+ assertEq(max(0, negative_zero), 0); | |
+ assertEq(min(0, negative_zero), negative_zero); | |
+ assertEq(min(negative_zero, 0), negative_zero); | |
+ | |
+ assertEq(min(negative_zero, negative_zero), negative_zero); | |
+ assertEq(max(negative_zero, negative_zero), negative_zero); | |
+ | |
+ assertEq(max(nan, 0), nan); | |
+ assertEq(min(nan, 0), nan); | |
+ | |
+ assertEq(max(0, nan), nan); | |
+ assertEq(max(nan, 0), nan); | |
+ | |
+ assertEq(max(3, 5), 5); | |
+ assertEq(max(5, 3), 5); | |
+ | |
+ assertEq(max(Infinity, -Infinity), Infinity); | |
+ assertEq(min(Infinity, -Infinity), -Infinity); | |
+ assertEq(max(Infinity, nan), nan); | |
+ | |
+ assertEq(max(negative_zero, -5), negative_zero); | |
+ assertEq(min(negative_zero, -5), -5); | |
+ } | |
+} | |
+ | |
+main(); | |
\ No newline at end of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment