mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-27 02:29:35 +00:00
336 lines
13 KiB
C++
336 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
*/
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
|
|
#include "jit/IonAnalysis.h"
|
|
#include "jit/MIRGenerator.h"
|
|
#include "jit/MIRGraph.h"
|
|
#include "jit/RangeAnalysis.h"
|
|
|
|
#include "jsapi-tests/testJitMinimalFunc.h"
|
|
#include "jsapi-tests/tests.h"
|
|
|
|
using namespace js;
|
|
using namespace js::jit;
|
|
|
|
static bool
|
|
EquivalentRanges(const Range* a, const Range* b) {
|
|
if (a->hasInt32UpperBound() != b->hasInt32UpperBound())
|
|
return false;
|
|
if (a->hasInt32LowerBound() != b->hasInt32LowerBound())
|
|
return false;
|
|
if (a->hasInt32UpperBound() && (a->upper() != b->upper()))
|
|
return false;
|
|
if (a->hasInt32LowerBound() && (a->lower() != b->lower()))
|
|
return false;
|
|
if (a->canHaveFractionalPart() != b->canHaveFractionalPart())
|
|
return false;
|
|
if (a->canBeNegativeZero() != b->canBeNegativeZero())
|
|
return false;
|
|
if (a->canBeNaN() != b->canBeNaN())
|
|
return false;
|
|
if (a->canBeInfiniteOrNaN() != b->canBeInfiniteOrNaN())
|
|
return false;
|
|
if (!a->canBeInfiniteOrNaN() && (a->exponent() != b->exponent()))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
BEGIN_TEST(testJitRangeAnalysis_MathSign)
|
|
{
|
|
MinimalAlloc func;
|
|
|
|
Range* xnan = new(func.alloc) Range();
|
|
|
|
Range* ninf = Range::NewDoubleSingletonRange(func.alloc, mozilla::NegativeInfinity<double>());
|
|
Range* n1_5 = Range::NewDoubleSingletonRange(func.alloc, -1.5);
|
|
Range* n1_0 = Range::NewDoubleSingletonRange(func.alloc, -1);
|
|
Range* n0_5 = Range::NewDoubleSingletonRange(func.alloc, -0.5);
|
|
Range* n0_0 = Range::NewDoubleSingletonRange(func.alloc, -0.0);
|
|
|
|
Range* p0_0 = Range::NewDoubleSingletonRange(func.alloc, 0.0);
|
|
Range* p0_5 = Range::NewDoubleSingletonRange(func.alloc, 0.5);
|
|
Range* p1_0 = Range::NewDoubleSingletonRange(func.alloc, 1.0);
|
|
Range* p1_5 = Range::NewDoubleSingletonRange(func.alloc, 1.5);
|
|
Range* pinf = Range::NewDoubleSingletonRange(func.alloc, mozilla::PositiveInfinity<double>());
|
|
|
|
Range* xnanSign = Range::sign(func.alloc, xnan);
|
|
|
|
Range* ninfSign = Range::sign(func.alloc, ninf);
|
|
Range* n1_5Sign = Range::sign(func.alloc, n1_5);
|
|
Range* n1_0Sign = Range::sign(func.alloc, n1_0);
|
|
Range* n0_5Sign = Range::sign(func.alloc, n0_5);
|
|
Range* n0_0Sign = Range::sign(func.alloc, n0_0);
|
|
|
|
Range* p0_0Sign = Range::sign(func.alloc, p0_0);
|
|
Range* p0_5Sign = Range::sign(func.alloc, p0_5);
|
|
Range* p1_0Sign = Range::sign(func.alloc, p1_0);
|
|
Range* p1_5Sign = Range::sign(func.alloc, p1_5);
|
|
Range* pinfSign = Range::sign(func.alloc, pinf);
|
|
|
|
CHECK(!xnanSign);
|
|
CHECK(EquivalentRanges(ninfSign, Range::NewInt32SingletonRange(func.alloc, -1)));
|
|
CHECK(EquivalentRanges(n1_5Sign, Range::NewInt32SingletonRange(func.alloc, -1)));
|
|
CHECK(EquivalentRanges(n1_0Sign, Range::NewInt32SingletonRange(func.alloc, -1)));
|
|
|
|
// This should ideally be just -1, but range analysis can't represent the
|
|
// specific fractional range of the constant.
|
|
CHECK(EquivalentRanges(n0_5Sign, Range::NewInt32Range(func.alloc, -1, 0)));
|
|
|
|
CHECK(EquivalentRanges(n0_0Sign, Range::NewDoubleSingletonRange(func.alloc, -0.0)));
|
|
|
|
CHECK(!n0_0Sign->canHaveFractionalPart());
|
|
CHECK(n0_0Sign->canBeNegativeZero());
|
|
CHECK(n0_0Sign->lower() == 0);
|
|
CHECK(n0_0Sign->upper() == 0);
|
|
|
|
CHECK(EquivalentRanges(p0_0Sign, Range::NewInt32SingletonRange(func.alloc, 0)));
|
|
|
|
CHECK(!p0_0Sign->canHaveFractionalPart());
|
|
CHECK(!p0_0Sign->canBeNegativeZero());
|
|
CHECK(p0_0Sign->lower() == 0);
|
|
CHECK(p0_0Sign->upper() == 0);
|
|
|
|
// This should ideally be just 1, but range analysis can't represent the
|
|
// specific fractional range of the constant.
|
|
CHECK(EquivalentRanges(p0_5Sign, Range::NewInt32Range(func.alloc, 0, 1)));
|
|
|
|
CHECK(EquivalentRanges(p1_0Sign, Range::NewInt32SingletonRange(func.alloc, 1)));
|
|
CHECK(EquivalentRanges(p1_5Sign, Range::NewInt32SingletonRange(func.alloc, 1)));
|
|
CHECK(EquivalentRanges(pinfSign, Range::NewInt32SingletonRange(func.alloc, 1)));
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testJitRangeAnalysis_MathSign)
|
|
|
|
BEGIN_TEST(testJitRangeAnalysis_MathSignBeta)
|
|
{
|
|
MinimalFunc func;
|
|
MathCache cache;
|
|
|
|
MBasicBlock* entry = func.createEntryBlock();
|
|
MBasicBlock* thenBlock = func.createBlock(entry);
|
|
MBasicBlock* elseBlock = func.createBlock(entry);
|
|
MBasicBlock* elseThenBlock = func.createBlock(elseBlock);
|
|
MBasicBlock* elseElseBlock = func.createBlock(elseBlock);
|
|
|
|
// if (p < 0)
|
|
MParameter* p = func.createParameter();
|
|
entry->add(p);
|
|
MConstant* c0 = MConstant::New(func.alloc, DoubleValue(0.0));
|
|
entry->add(c0);
|
|
MConstant* cm0 = MConstant::New(func.alloc, DoubleValue(-0.0));
|
|
entry->add(cm0);
|
|
MCompare* cmp = MCompare::New(func.alloc, p, c0, JSOP_LT);
|
|
cmp->setCompareType(MCompare::Compare_Double);
|
|
entry->add(cmp);
|
|
entry->end(MTest::New(func.alloc, cmp, thenBlock, elseBlock));
|
|
|
|
// {
|
|
// return Math.sign(p + -0);
|
|
// }
|
|
MAdd* thenAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double);
|
|
thenBlock->add(thenAdd);
|
|
MMathFunction* thenSign = MMathFunction::New(func.alloc, thenAdd, MMathFunction::Sign, &cache);
|
|
thenBlock->add(thenSign);
|
|
MReturn* thenRet = MReturn::New(func.alloc, thenSign);
|
|
thenBlock->end(thenRet);
|
|
|
|
// else
|
|
// {
|
|
// if (p >= 0)
|
|
MCompare* elseCmp = MCompare::New(func.alloc, p, c0, JSOP_GE);
|
|
elseCmp->setCompareType(MCompare::Compare_Double);
|
|
elseBlock->add(elseCmp);
|
|
elseBlock->end(MTest::New(func.alloc, elseCmp, elseThenBlock, elseElseBlock));
|
|
|
|
// {
|
|
// return Math.sign(p + -0);
|
|
// }
|
|
MAdd* elseThenAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double);
|
|
elseThenBlock->add(elseThenAdd);
|
|
MMathFunction* elseThenSign = MMathFunction::New(func.alloc, elseThenAdd, MMathFunction::Sign, &cache);
|
|
elseThenBlock->add(elseThenSign);
|
|
MReturn* elseThenRet = MReturn::New(func.alloc, elseThenSign);
|
|
elseThenBlock->end(elseThenRet);
|
|
|
|
// else
|
|
// {
|
|
// return Math.sign(p + -0);
|
|
// }
|
|
// }
|
|
MAdd* elseElseAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double);
|
|
elseElseBlock->add(elseElseAdd);
|
|
MMathFunction* elseElseSign = MMathFunction::New(func.alloc, elseElseAdd, MMathFunction::Sign, &cache);
|
|
elseElseBlock->add(elseElseSign);
|
|
MReturn* elseElseRet = MReturn::New(func.alloc, elseElseSign);
|
|
elseElseBlock->end(elseElseRet);
|
|
|
|
if (!func.runRangeAnalysis())
|
|
return false;
|
|
|
|
CHECK(!p->range());
|
|
CHECK(EquivalentRanges(c0->range(), Range::NewDoubleSingletonRange(func.alloc, 0.0)));
|
|
CHECK(EquivalentRanges(cm0->range(), Range::NewDoubleSingletonRange(func.alloc, -0.0)));
|
|
|
|
// On the (p < 0) side, p is negative and not -0 (surprise!)
|
|
CHECK(EquivalentRanges(thenAdd->range(),
|
|
new(func.alloc) Range(Range::NoInt32LowerBound, 0,
|
|
Range::IncludesFractionalParts,
|
|
Range::ExcludesNegativeZero,
|
|
Range::IncludesInfinity)));
|
|
|
|
// Consequently, its Math.sign value is not -0 either.
|
|
CHECK(EquivalentRanges(thenSign->range(),
|
|
new(func.alloc) Range(-1, 0,
|
|
Range::ExcludesFractionalParts,
|
|
Range::ExcludesNegativeZero,
|
|
0)));
|
|
|
|
// On the (p >= 0) side, p is not negative and may be -0 (surprise!)
|
|
CHECK(EquivalentRanges(elseThenAdd->range(),
|
|
new(func.alloc) Range(0, Range::NoInt32UpperBound,
|
|
Range::IncludesFractionalParts,
|
|
Range::IncludesNegativeZero,
|
|
Range::IncludesInfinity)));
|
|
|
|
// Consequently, its Math.sign value may be -0 too.
|
|
CHECK(EquivalentRanges(elseThenSign->range(),
|
|
new(func.alloc) Range(0, 1,
|
|
Range::ExcludesFractionalParts,
|
|
Range::IncludesNegativeZero,
|
|
0)));
|
|
|
|
// Otherwise, p may be NaN.
|
|
CHECK(elseElseAdd->range()->isUnknown());
|
|
CHECK(!elseElseSign->range());
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testJitRangeAnalysis_MathSignBeta)
|
|
|
|
BEGIN_TEST(testJitRangeAnalysis_StrictCompareBeta)
|
|
{
|
|
MinimalFunc func;
|
|
|
|
MBasicBlock* entry = func.createEntryBlock();
|
|
MBasicBlock* thenBlock = func.createBlock(entry);
|
|
MBasicBlock* elseBlock = func.createBlock(entry);
|
|
|
|
// if (p === 0)
|
|
MParameter* p = func.createParameter();
|
|
entry->add(p);
|
|
MConstant* c0 = MConstant::New(func.alloc, DoubleValue(0.0));
|
|
entry->add(c0);
|
|
MCompare* cmp = MCompare::New(func.alloc, p, c0, JSOP_STRICTEQ);
|
|
entry->add(cmp);
|
|
entry->end(MTest::New(func.alloc, cmp, thenBlock, elseBlock));
|
|
|
|
// {
|
|
// return p + -0;
|
|
// }
|
|
MConstant* cm0 = MConstant::New(func.alloc, DoubleValue(-0.0));
|
|
thenBlock->add(cm0);
|
|
MAdd* thenAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double);
|
|
thenBlock->add(thenAdd);
|
|
MReturn* thenRet = MReturn::New(func.alloc, thenAdd);
|
|
thenBlock->end(thenRet);
|
|
|
|
// else
|
|
// {
|
|
// return 0;
|
|
// }
|
|
MReturn* elseRet = MReturn::New(func.alloc, c0);
|
|
elseBlock->end(elseRet);
|
|
|
|
// If range analysis inserts a beta node for p, it will be able to compute
|
|
// a meaningful range for p + -0.
|
|
|
|
// We can't do beta node insertion with STRICTEQ and a non-numeric
|
|
// comparison though.
|
|
MCompare::CompareType nonNumerics[] = {
|
|
MCompare::Compare_Unknown,
|
|
MCompare::Compare_Object,
|
|
MCompare::Compare_Bitwise,
|
|
MCompare::Compare_String
|
|
};
|
|
for (size_t i = 0; i < mozilla::ArrayLength(nonNumerics); ++i) {
|
|
cmp->setCompareType(nonNumerics[i]);
|
|
if (!func.runRangeAnalysis())
|
|
return false;
|
|
CHECK(!thenAdd->range() || thenAdd->range()->isUnknown());
|
|
ClearDominatorTree(func.graph);
|
|
}
|
|
|
|
// We can do it with a numeric comparison.
|
|
cmp->setCompareType(MCompare::Compare_Double);
|
|
if (!func.runRangeAnalysis())
|
|
return false;
|
|
CHECK(EquivalentRanges(thenAdd->range(),
|
|
Range::NewDoubleRange(func.alloc, 0.0, 0.0)));
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testJitRangeAnalysis_StrictCompareBeta)
|
|
|
|
|
|
static void
|
|
deriveShiftRightRange(int32_t lhsLower, int32_t lhsUpper,
|
|
int32_t rhsLower, int32_t rhsUpper,
|
|
int32_t* min, int32_t* max)
|
|
{
|
|
// This is the reference algorithm and should be verifiable by inspection.
|
|
int64_t i, j;
|
|
*min = INT32_MAX; *max = INT32_MIN;
|
|
for (i = lhsLower; i <= lhsUpper; i++) {
|
|
for (j = rhsLower; j <= rhsUpper; j++) {
|
|
int32_t r = int32_t(i) >> (int32_t(j) & 0x1f);
|
|
if (r > *max) *max = r;
|
|
if (r < *min) *min = r;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
checkShiftRightRange(int32_t lhsLow, int32_t lhsHigh, int32_t lhsInc,
|
|
int32_t rhsLow, int32_t rhsHigh, int32_t rhsInc)
|
|
{
|
|
MinimalAlloc func;
|
|
int64_t lhsLower, lhsUpper, rhsLower, rhsUpper;
|
|
|
|
for (lhsLower = lhsLow; lhsLower <= lhsHigh; lhsLower += lhsInc) {
|
|
for (lhsUpper = lhsLower; lhsUpper <= lhsHigh; lhsUpper += lhsInc) {
|
|
Range* lhsRange = Range::NewInt32Range(func.alloc, lhsLower, lhsUpper);
|
|
for (rhsLower = rhsLow; rhsLower <= rhsHigh; rhsLower += rhsInc) {
|
|
for (rhsUpper = rhsLower; rhsUpper <= rhsHigh; rhsUpper += rhsInc) {
|
|
Range* rhsRange = Range::NewInt32Range(func.alloc, rhsLower, rhsUpper);
|
|
Range* result = Range::rsh(func.alloc, lhsRange, rhsRange);
|
|
int32_t min, max;
|
|
deriveShiftRightRange(lhsLower, lhsUpper,
|
|
rhsLower, rhsUpper,
|
|
&min, &max);
|
|
if (!result->isInt32() ||
|
|
result->lower() != min ||
|
|
result->upper() != max) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
BEGIN_TEST(testJitRangeAnalysis_shiftRight)
|
|
{
|
|
CHECK(checkShiftRightRange(-16, 15, 1, 0, 31, 1));
|
|
CHECK(checkShiftRightRange( -8, 7, 1, -64, 63, 1));
|
|
return true;
|
|
}
|
|
END_TEST(testJitRangeAnalysis_shiftRight)
|