Bug 865259 - Make comparisons work in Par Exec mode regardless of TI results r=bhackett

This commit is contained in:
Nicholas D. Matsakis 2013-04-25 08:47:16 -04:00
parent 8f36fd56c8
commit c40c2e6e55
8 changed files with 301 additions and 116 deletions

View File

@ -3176,40 +3176,18 @@ CodeGenerator::visitBinaryV(LBinaryV *lir)
}
}
bool
CodeGenerator::visitParCompareS(LParCompareS *lir)
{
JSOp op = lir->mir()->jsop();
Register left = ToRegister(lir->left());
Register right = ToRegister(lir->right());
JS_ASSERT((op == JSOP_EQ || op == JSOP_STRICTEQ) ||
(op == JSOP_NE || op == JSOP_STRICTNE));
masm.setupUnalignedABICall(2, CallTempReg2);
masm.passABIArg(left);
masm.passABIArg(right);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCompareStrings));
masm.and32(Imm32(0xF), ReturnReg); // The C functions return an enum whose size is undef
// Check for cases that we do not currently handle in par exec
Label *bail;
if (!ensureOutOfLineParallelAbort(&bail))
return false;
masm.branch32(Assembler::Equal, ReturnReg, Imm32(ParCompareUnknown), bail);
if (op == JSOP_NE || op == JSOP_STRICTNE)
masm.xor32(Imm32(1), ReturnReg);
return true;
}
typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, JSBool *);
static const VMFunction stringsEqualInfo =
FunctionInfo<StringCompareFn>(ion::StringsEqual<true>);
static const VMFunction stringsNotEqualInfo =
FunctionInfo<StringCompareFn>(ion::StringsEqual<false>);
typedef ParallelResult (*ParStringCompareFn)(ForkJoinSlice *, HandleString, HandleString, JSBool *);
static const VMFunction parStringsEqualInfo =
FunctionInfo<ParStringCompareFn>(ion::ParStringsEqual);
static const VMFunction parStringsNotEqualInfo =
FunctionInfo<ParStringCompareFn>(ion::ParStringsUnequal);
bool
CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
Register output, Register temp)
@ -3217,11 +3195,25 @@ CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register
JS_ASSERT(lir->isCompareS() || lir->isCompareStrictS());
OutOfLineCode *ool = NULL;
if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
ool = oolCallVM(stringsEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
} else {
JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
ool = oolCallVM(stringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
switch (gen->info().executionMode()) {
case SequentialExecution:
if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
ool = oolCallVM(stringsEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
} else {
JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
ool = oolCallVM(stringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
}
break;
case ParallelExecution:
if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
ool = oolCallVM(parStringsEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
} else {
JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
ool = oolCallVM(parStringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
}
break;
}
if (!ool)
@ -3282,41 +3274,87 @@ static const VMFunction LeInfo = FunctionInfo<CompareFn>(ion::LessThanOrEqual);
static const VMFunction GtInfo = FunctionInfo<CompareFn>(ion::GreaterThan);
static const VMFunction GeInfo = FunctionInfo<CompareFn>(ion::GreaterThanOrEqual);
typedef ParallelResult (*ParCompareFn)(ForkJoinSlice *, MutableHandleValue, MutableHandleValue, JSBool *);
static const VMFunction ParLooselyEqInfo = FunctionInfo<ParCompareFn>(ion::ParLooselyEqual);
static const VMFunction ParStrictlyEqInfo = FunctionInfo<ParCompareFn>(ion::ParStrictlyEqual);
static const VMFunction ParLooselyNeInfo = FunctionInfo<ParCompareFn>(ion::ParLooselyUnequal);
static const VMFunction ParStrictlyNeInfo = FunctionInfo<ParCompareFn>(ion::ParStrictlyUnequal);
static const VMFunction ParLtInfo = FunctionInfo<ParCompareFn>(ion::ParLessThan);
static const VMFunction ParLeInfo = FunctionInfo<ParCompareFn>(ion::ParLessThanOrEqual);
static const VMFunction ParGtInfo = FunctionInfo<ParCompareFn>(ion::ParGreaterThan);
static const VMFunction ParGeInfo = FunctionInfo<ParCompareFn>(ion::ParGreaterThanOrEqual);
bool
CodeGenerator::visitCompareVM(LCompareVM *lir)
{
pushArg(ToValue(lir, LBinaryV::RhsInput));
pushArg(ToValue(lir, LBinaryV::LhsInput));
switch (lir->mir()->jsop()) {
case JSOP_EQ:
return callVM(EqInfo, lir);
switch (gen->info().executionMode()) {
case SequentialExecution:
switch (lir->mir()->jsop()) {
case JSOP_EQ:
return callVM(EqInfo, lir);
case JSOP_NE:
return callVM(NeInfo, lir);
case JSOP_NE:
return callVM(NeInfo, lir);
case JSOP_STRICTEQ:
return callVM(StrictEqInfo, lir);
case JSOP_STRICTEQ:
return callVM(StrictEqInfo, lir);
case JSOP_STRICTNE:
return callVM(StrictNeInfo, lir);
case JSOP_STRICTNE:
return callVM(StrictNeInfo, lir);
case JSOP_LT:
return callVM(LtInfo, lir);
case JSOP_LT:
return callVM(LtInfo, lir);
case JSOP_LE:
return callVM(LeInfo, lir);
case JSOP_LE:
return callVM(LeInfo, lir);
case JSOP_GT:
return callVM(GtInfo, lir);
case JSOP_GT:
return callVM(GtInfo, lir);
case JSOP_GE:
return callVM(GeInfo, lir);
case JSOP_GE:
return callVM(GeInfo, lir);
default:
JS_NOT_REACHED("Unexpected compare op");
return false;
default:
JS_NOT_REACHED("Unexpected compare op");
return false;
}
case ParallelExecution:
switch (lir->mir()->jsop()) {
case JSOP_EQ:
return callVM(ParLooselyEqInfo, lir);
case JSOP_STRICTEQ:
return callVM(ParStrictlyEqInfo, lir);
case JSOP_NE:
return callVM(ParLooselyNeInfo, lir);
case JSOP_STRICTNE:
return callVM(ParStrictlyNeInfo, lir);
case JSOP_LT:
return callVM(ParLtInfo, lir);
case JSOP_LE:
return callVM(ParLeInfo, lir);
case JSOP_GT:
return callVM(ParGtInfo, lir);
case JSOP_GE:
return callVM(ParGeInfo, lir);
default:
JS_NOT_REACHED("Unexpected compare op");
return false;
}
}
JS_NOT_REACHED("Unexpected exec mode");
}
bool

View File

@ -153,7 +153,6 @@ class CodeGenerator : public CodeGeneratorSpecific
Register output, Register temp);
bool visitCompareS(LCompareS *lir);
bool visitCompareStrictS(LCompareStrictS *lir);
bool visitParCompareS(LParCompareS *lir);
bool visitCompareVM(LCompareVM *lir);
bool visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
bool visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir);

View File

@ -1558,27 +1558,6 @@ class LCompareStrictS : public LInstructionHelper<1, BOX_PIECES + 1, 2>
}
};
class LParCompareS : public LCallInstructionHelper<1, 2, 0>
{
public:
LIR_HEADER(ParCompareS);
LParCompareS(const LAllocation &left, const LAllocation &right) {
setOperand(0, left);
setOperand(1, right);
}
const LAllocation *left() {
return getOperand(0);
}
const LAllocation *right() {
return getOperand(1);
}
MCompare *mir() {
return mir_->toCompare();
}
};
// Used for strict-equality comparisons where one side is a boolean
// and the other is a value. Note that CompareI is used to compare
// two booleans.

View File

@ -76,7 +76,6 @@
_(CompareDAndBranch) \
_(CompareS) \
_(CompareStrictS) \
_(ParCompareS) \
_(CompareB) \
_(CompareBAndBranch) \
_(CompareV) \

View File

@ -743,24 +743,10 @@ LIRGenerator::visitCompare(MCompare *comp)
// LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
// make sense and avoids confusion.
if (comp->compareType() == MCompare::Compare_String) {
switch (comp->block()->info().executionMode()) {
case SequentialExecution:
{
LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
if (!define(lir, comp))
return false;
return assignSafepoint(lir, comp);
}
case ParallelExecution:
{
LParCompareS *lir = new LParCompareS(useFixed(left, CallTempReg0),
useFixed(right, CallTempReg1));
return defineReturn(lir, comp);
}
}
JS_NOT_REACHED("Unexpected execution mode");
LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
if (!define(lir, comp))
return false;
return assignSafepoint(lir, comp);
}
// Strict compare between value and string

View File

@ -493,13 +493,26 @@ bool
ParallelArrayVisitor::visitCompare(MCompare *compare)
{
MCompare::CompareType type = compare->compareType();
if (type != MCompare::Compare_Int32 &&
type != MCompare::Compare_Double &&
type != MCompare::Compare_String)
{
MBox *lhsBox, *rhsBox;
MBasicBlock *block;
switch (type) {
case MCompare::Compare_Int32:
case MCompare::Compare_Double:
case MCompare::Compare_Null:
case MCompare::Compare_Undefined:
case MCompare::Compare_Boolean:
case MCompare::Compare_Object:
case MCompare::Compare_Value:
case MCompare::Compare_Unknown:
case MCompare::Compare_String:
// These paths through compare are ok in any mode.
return true;
default:
SpewMIR(compare, "unsafe compareType=%d\n", type);
return markUnsafe();
}
return true;
}
bool

View File

@ -17,6 +17,7 @@ using namespace js;
using namespace ion;
using parallel::Spew;
using parallel::SpewOps;
using parallel::SpewBailouts;
using parallel::SpewBailoutIR;
@ -183,20 +184,183 @@ ion::ParExtendArray(ForkJoinSlice *slice, JSObject *array, uint32_t length)
return array;
}
ParCompareResult
ion::ParCompareStrings(JSString *str1, JSString *str2)
{
// NYI---the rope case
if (!str1->isLinear())
return ParCompareUnknown;
if (!str2->isLinear())
return ParCompareUnknown;
#define PAR_RELATIONAL_OP(OP, EXPECTED) \
do { \
/* Optimize for two int-tagged operands (typical loop control). */ \
if (lhs.isInt32() && rhs.isInt32()) { \
*res = (lhs.toInt32() OP rhs.toInt32()) == EXPECTED; \
} else if (lhs.isNumber() && rhs.isNumber()) { \
double l = lhs.toNumber(), r = rhs.toNumber(); \
*res = (l OP r) == EXPECTED; \
} else if (lhs.isBoolean() && rhs.isBoolean()) { \
bool l = lhs.toBoolean(); \
bool r = rhs.toBoolean(); \
*res = (l OP r) == EXPECTED; \
} else if (lhs.isBoolean() && rhs.isNumber()) { \
bool l = lhs.toBoolean(); \
double r = rhs.toNumber(); \
*res = (l OP r) == EXPECTED; \
} else if (lhs.isNumber() && rhs.isBoolean()) { \
double l = lhs.toNumber(); \
bool r = rhs.toBoolean(); \
*res = (l OP r) == EXPECTED; \
} else { \
int32_t vsZero; \
ParallelResult ret = ParCompareMaybeStrings(slice, lhs, rhs, &vsZero); \
if (ret != TP_SUCCESS) \
return ret; \
*res = (vsZero OP 0) == EXPECTED; \
} \
return TP_SUCCESS; \
} while(0)
static ParallelResult
ParCompareStrings(ForkJoinSlice *slice, HandleString str1,
HandleString str2, int32_t *res)
{
if (!str1->isLinear())
return TP_RETRY_SEQUENTIALLY;
if (!str2->isLinear())
return TP_RETRY_SEQUENTIALLY;
JSLinearString &linearStr1 = str1->asLinear();
JSLinearString &linearStr2 = str2->asLinear();
if (EqualStrings(&linearStr1, &linearStr2))
return ParCompareEq;
return ParCompareNe;
if (!CompareChars(linearStr1.chars(), linearStr1.length(),
linearStr2.chars(), linearStr2.length(),
res))
return TP_FATAL;
return TP_SUCCESS;
}
static ParallelResult
ParCompareMaybeStrings(ForkJoinSlice *slice,
HandleValue v1,
HandleValue v2,
int32_t *res)
{
if (!v1.isString())
return TP_RETRY_SEQUENTIALLY;
if (!v2.isString())
return TP_RETRY_SEQUENTIALLY;
RootedString str1(slice->perThreadData, v1.toString());
RootedString str2(slice->perThreadData, v2.toString());
return ParCompareStrings(slice, str1, str2, res);
}
template<bool Equal>
ParallelResult
ParLooselyEqualImpl(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
PAR_RELATIONAL_OP(==, Equal);
}
ParallelResult
js::ion::ParLooselyEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
return ParLooselyEqualImpl<true>(slice, lhs, rhs, res);
}
ParallelResult
js::ion::ParLooselyUnequal(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
return ParLooselyEqualImpl<false>(slice, lhs, rhs, res);
}
template<bool Equal>
ParallelResult
ParStrictlyEqualImpl(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
if (lhs.isNumber()) {
if (rhs.isNumber()) {
*res = (lhs.toNumber() == rhs.toNumber()) == Equal;
return TP_SUCCESS;
}
} else if (lhs.isBoolean()) {
if (rhs.isBoolean()) {
*res = (lhs.toBoolean() == rhs.toBoolean()) == Equal;
return TP_SUCCESS;
}
} else if (lhs.isNull()) {
if (rhs.isNull()) {
*res = Equal;
return TP_SUCCESS;
}
} else if (lhs.isUndefined()) {
if (rhs.isUndefined()) {
*res = Equal;
return TP_SUCCESS;
}
} else if (lhs.isObject()) {
if (rhs.isObject()) {
*res = (lhs.toObjectOrNull() == rhs.toObjectOrNull()) == Equal;
return TP_SUCCESS;
}
} else if (lhs.isString()) {
if (rhs.isString())
return ParLooselyEqualImpl<Equal>(slice, lhs, rhs, res);
}
return TP_RETRY_SEQUENTIALLY;
}
ParallelResult
js::ion::ParStrictlyEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
return ParStrictlyEqualImpl<true>(slice, lhs, rhs, res);
}
ParallelResult
js::ion::ParStrictlyUnequal(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
return ParStrictlyEqualImpl<false>(slice, lhs, rhs, res);
}
ParallelResult
js::ion::ParLessThan(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
PAR_RELATIONAL_OP(<, true);
}
ParallelResult
js::ion::ParLessThanOrEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
PAR_RELATIONAL_OP(<=, true);
}
ParallelResult
js::ion::ParGreaterThan(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
PAR_RELATIONAL_OP(>, true);
}
ParallelResult
js::ion::ParGreaterThanOrEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
{
PAR_RELATIONAL_OP(>=, true);
}
template<bool Equal>
ParallelResult
ParStringsEqualImpl(ForkJoinSlice *slice, HandleString lhs, HandleString rhs, JSBool *res)
{
int32_t vsZero;
ParallelResult ret = ParCompareStrings(slice, lhs, rhs, &vsZero);
if (ret != TP_SUCCESS)
return ret;
*res = (vsZero == 0) == Equal;
return TP_SUCCESS;
}
ParallelResult
js::ion::ParStringsEqual(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *res)
{
return ParStringsEqualImpl<true>(slice, v1, v2, res);
}
ParallelResult
js::ion::ParStringsUnequal(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *res)
{
return ParStringsEqualImpl<false>(slice, v1, v2, res);
}
void

View File

@ -41,12 +41,19 @@ JSObject* ParPush(ParPushArgs *args);
// generation.
JSObject *ParExtendArray(ForkJoinSlice *slice, JSObject *array, uint32_t length);
enum ParCompareResult {
ParCompareNe = false,
ParCompareEq = true,
ParCompareUnknown = 2
};
ParCompareResult ParCompareStrings(JSString *str1, JSString *str2);
// These parallel operations fail if they would be required to convert
// to a string etc etc.
ParallelResult ParStrictlyEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParStrictlyUnequal(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParLooselyEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParLooselyUnequal(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParLessThan(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParLessThanOrEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParGreaterThan(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParGreaterThanOrEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
ParallelResult ParStringsEqual(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *);
ParallelResult ParStringsUnequal(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *);
void ParallelAbort(JSScript *script);