mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 894813 - IonMonkey: Implement dynamic range analysis checking. r=nbp
This commit is contained in:
parent
0e6eeaf7da
commit
84c78a5607
@ -7304,5 +7304,85 @@ CodeGenerator::visitAsmJSCheckOverRecursed(LAsmJSCheckOverRecursed *lir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitRangeAssert(LRangeAssert *ins)
|
||||
{
|
||||
Register input = ToRegister(ins->input());
|
||||
Range *r = ins->range();
|
||||
|
||||
// Check the lower bound.
|
||||
if (r->lower() != INT32_MIN) {
|
||||
Label success;
|
||||
masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), &success);
|
||||
masm.breakpoint();
|
||||
masm.bind(&success);
|
||||
}
|
||||
|
||||
// Check the upper bound.
|
||||
if (r->upper() != INT32_MAX) {
|
||||
Label success;
|
||||
masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), &success);
|
||||
masm.breakpoint();
|
||||
masm.bind(&success);
|
||||
}
|
||||
|
||||
// For r->isDecimal() and r->exponent(), there's nothing to check, because
|
||||
// if we ended up in the integer range checking code, the value is already
|
||||
// in an integer register in the integer range.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitDoubleRangeAssert(LDoubleRangeAssert *ins)
|
||||
{
|
||||
FloatRegister input = ToFloatRegister(ins->input());
|
||||
FloatRegister temp = ToFloatRegister(ins->temp());
|
||||
Range *r = ins->range();
|
||||
|
||||
// Check the lower bound.
|
||||
if (!r->isLowerInfinite()) {
|
||||
Label success;
|
||||
masm.loadConstantDouble(r->lower(), temp);
|
||||
if (r->isUpperInfinite())
|
||||
masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
|
||||
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &success);
|
||||
masm.breakpoint();
|
||||
masm.bind(&success);
|
||||
}
|
||||
// Check the upper bound.
|
||||
if (!r->isUpperInfinite()) {
|
||||
Label success;
|
||||
masm.loadConstantDouble(r->upper(), temp);
|
||||
if (r->isLowerInfinite())
|
||||
masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
|
||||
masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
|
||||
masm.breakpoint();
|
||||
masm.bind(&success);
|
||||
}
|
||||
|
||||
// This code does not yet check r->isDecimal(). This would require new
|
||||
// assembler interfaces to make rounding instructions available.
|
||||
|
||||
if (!r->isInfinite()) {
|
||||
// Check the bounds implied by the maximum exponent.
|
||||
Label exponentLoOk;
|
||||
masm.loadConstantDouble(pow(2, r->exponent() + 1), temp);
|
||||
masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
|
||||
masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &exponentLoOk);
|
||||
masm.breakpoint();
|
||||
masm.bind(&exponentLoOk);
|
||||
|
||||
Label exponentHiOk;
|
||||
masm.loadConstantDouble(-pow(2, r->exponent() + 1), temp);
|
||||
masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
|
||||
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, &exponentHiOk);
|
||||
masm.breakpoint();
|
||||
masm.bind(&exponentHiOk);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ion
|
||||
} // namespace js
|
||||
|
@ -302,6 +302,9 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitNameIC(OutOfLineUpdateCache *ool, NameIC *ic);
|
||||
bool visitCallsiteCloneIC(OutOfLineUpdateCache *ool, CallsiteCloneIC *ic);
|
||||
|
||||
bool visitRangeAssert(LRangeAssert *ins);
|
||||
bool visitDoubleRangeAssert(LDoubleRangeAssert *ins);
|
||||
|
||||
IonScriptCounts *extractUnassociatedScriptCounts() {
|
||||
IonScriptCounts *counts = unassociatedScriptCounts_;
|
||||
unassociatedScriptCounts_ = NULL; // prevent delete in dtor
|
||||
|
@ -77,6 +77,12 @@ struct IonOptions
|
||||
// Default: true
|
||||
bool rangeAnalysis;
|
||||
|
||||
// Whether to enable extra code to perform dynamic validation of
|
||||
// RangeAnalysis results.
|
||||
//
|
||||
// Default: false
|
||||
bool checkRangeAnalysis;
|
||||
|
||||
// Toggles whether Unreachable Code Elimination is performed.
|
||||
//
|
||||
// Default: true
|
||||
@ -212,6 +218,7 @@ struct IonOptions
|
||||
inlining(true),
|
||||
edgeCaseAnalysis(true),
|
||||
rangeAnalysis(true),
|
||||
checkRangeAnalysis(false),
|
||||
uce(true),
|
||||
eaa(true),
|
||||
parallelCompilation(false),
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef jit_LIR_Common_h
|
||||
#define jit_LIR_Common_h
|
||||
|
||||
#include "RangeAnalysis.h"
|
||||
#include "jit/shared/Assembler-shared.h"
|
||||
|
||||
// This file declares LIR instructions that are common to every platform.
|
||||
@ -4942,6 +4943,55 @@ class LAsmJSCheckOverRecursed : public LInstructionHelper<0, 0, 0>
|
||||
}
|
||||
};
|
||||
|
||||
class LRangeAssert : public LInstructionHelper<0, 1, 0>
|
||||
{
|
||||
Range range_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(RangeAssert)
|
||||
|
||||
LRangeAssert(const LAllocation &input, Range r)
|
||||
: range_(r)
|
||||
{
|
||||
setOperand(0, input);
|
||||
}
|
||||
|
||||
const LAllocation *input() {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
Range *range() {
|
||||
return &range_;
|
||||
}
|
||||
};
|
||||
|
||||
class LDoubleRangeAssert : public LInstructionHelper<0, 1, 1>
|
||||
{
|
||||
Range range_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(DoubleRangeAssert)
|
||||
|
||||
LDoubleRangeAssert(const LAllocation &input, const LDefinition &temp, Range r)
|
||||
: range_(r)
|
||||
{
|
||||
setOperand(0, input);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation *input() {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
Range *range() {
|
||||
return &range_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
} // namespace js
|
||||
|
||||
|
@ -245,7 +245,9 @@
|
||||
_(AsmJSPassStackArg) \
|
||||
_(AsmJSCall) \
|
||||
_(AsmJSCheckOverRecursed) \
|
||||
_(CheckInterruptPar)
|
||||
_(CheckInterruptPar) \
|
||||
_(RangeAssert) \
|
||||
_(DoubleRangeAssert)
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
# include "jit/x86/LOpcodes-x86.h"
|
||||
|
@ -2943,6 +2943,25 @@ LIRGenerator::visitInstruction(MInstruction *ins)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the computed range for this instruction, if the option is set. Note
|
||||
// that this code is quite invasive; it adds numerous additional
|
||||
// instructions for each MInstruction with a computed range, and it uses
|
||||
// registers, so it also affects register allocation.
|
||||
if (js_IonOptions.checkRangeAnalysis) {
|
||||
if (Range *r = ins->range()) {
|
||||
switch (ins->type()) {
|
||||
case MIRType_Int32:
|
||||
add(new LRangeAssert(useRegisterAtStart(ins), *r));
|
||||
break;
|
||||
case MIRType_Double:
|
||||
add(new LDoubleRangeAssert(useRegister(ins), tempFloat(), *r));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ RangeAnalysis::addBetaNobes()
|
||||
} else if (right->isConstant() && right->toConstant()->value().isInt32()) {
|
||||
bound = right->toConstant()->value().toInt32();
|
||||
val = left;
|
||||
} else {
|
||||
} else if (left->type() == MIRType_Int32 && right->type() == MIRType_Int32) {
|
||||
MDefinition *smaller = NULL;
|
||||
MDefinition *greater = NULL;
|
||||
if (jsop == JSOP_LT) {
|
||||
@ -173,22 +173,22 @@ RangeAnalysis::addBetaNobes()
|
||||
}
|
||||
if (smaller && greater) {
|
||||
MBeta *beta;
|
||||
beta = MBeta::New(smaller, new Range(JSVAL_INT_MIN, JSVAL_INT_MAX-1,
|
||||
smaller->type() != MIRType_Int32,
|
||||
Range::MaxDoubleExponent));
|
||||
beta = MBeta::New(smaller, new Range(JSVAL_INT_MIN, JSVAL_INT_MAX-1));
|
||||
block->insertBefore(*block->begin(), beta);
|
||||
replaceDominatedUsesWith(smaller, beta, block);
|
||||
IonSpew(IonSpew_Range, "Adding beta node for smaller %d", smaller->id());
|
||||
beta = MBeta::New(greater, new Range(JSVAL_INT_MIN+1, JSVAL_INT_MAX,
|
||||
greater->type() != MIRType_Int32,
|
||||
Range::MaxDoubleExponent));
|
||||
beta = MBeta::New(greater, new Range(JSVAL_INT_MIN+1, JSVAL_INT_MAX));
|
||||
block->insertBefore(*block->begin(), beta);
|
||||
replaceDominatedUsesWith(greater, beta, block);
|
||||
IonSpew(IonSpew_Range, "Adding beta node for greater %d", greater->id());
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point, one of the operands if the compare is a constant, and
|
||||
// val is the other operand.
|
||||
JS_ASSERT(val);
|
||||
|
||||
|
||||
@ -853,6 +853,12 @@ MConstant::computeRange()
|
||||
// Safe double comparisons, because there is no precision loss.
|
||||
int64_t l = integral - ((rest < 0) ? 1 : 0);
|
||||
int64_t h = integral + ((rest > 0) ? 1 : 0);
|
||||
// If we adjusted into a new exponent range, adjust exp accordingly.
|
||||
if ((rest < 0 && (l == INT64_MIN || IsPowerOfTwo(Abs(l)))) ||
|
||||
(rest > 0 && (h == INT64_MIN || IsPowerOfTwo(Abs(h)))))
|
||||
{
|
||||
++exp;
|
||||
}
|
||||
setRange(new Range(l, h, (rest != 0), exp));
|
||||
} else {
|
||||
// This double has a precision loss. This also mean that it cannot
|
||||
@ -984,8 +990,8 @@ MAbs::computeRange()
|
||||
Range other(getOperand(0));
|
||||
setRange(Range::abs(&other));
|
||||
|
||||
if (implicitTruncate_ && !range()->isInt32())
|
||||
setRange(new Range(INT32_MIN, INT32_MAX));
|
||||
if (implicitTruncate_)
|
||||
range()->wrapAroundToInt32();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1009,8 +1015,8 @@ MAdd::computeRange()
|
||||
Range *next = Range::add(&left, &right);
|
||||
setRange(next);
|
||||
|
||||
if (isTruncated() && !range()->isInt32())
|
||||
setRange(new Range(INT32_MIN, INT32_MAX));
|
||||
if (isTruncated())
|
||||
range()->wrapAroundToInt32();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1023,8 +1029,8 @@ MSub::computeRange()
|
||||
Range *next = Range::sub(&left, &right);
|
||||
setRange(next);
|
||||
|
||||
if (isTruncated() && !range()->isInt32())
|
||||
setRange(new Range(INT32_MIN, INT32_MAX));
|
||||
if (isTruncated())
|
||||
range()->wrapAroundToInt32();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1039,8 +1045,8 @@ MMul::computeRange()
|
||||
setRange(Range::mul(&left, &right));
|
||||
|
||||
// Truncated multiplications could overflow in both directions
|
||||
if (isTruncated() && !range()->isInt32())
|
||||
setRange(new Range(INT32_MIN, INT32_MAX));
|
||||
if (isTruncated())
|
||||
range()->wrapAroundToInt32();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1056,6 +1062,10 @@ MMod::computeRange()
|
||||
if (lhs.isInfinite() || rhs.isInfinite())
|
||||
return;
|
||||
|
||||
// If RHS can be zero, the result can be NaN.
|
||||
if (rhs.lower() <= 0 && rhs.upper() >= 0)
|
||||
return;
|
||||
|
||||
// Math.abs(lhs % rhs) == Math.abs(lhs) % Math.abs(rhs).
|
||||
// First, the absolute value of the result will always be less than the
|
||||
// absolute value of rhs. (And if rhs is zero, the result is NaN).
|
||||
@ -1663,15 +1673,15 @@ MAdd::truncate()
|
||||
// Remember analysis, needed for fallible checks.
|
||||
setTruncated(true);
|
||||
|
||||
// Modify the instruction if needed.
|
||||
if (type() != MIRType_Double)
|
||||
return false;
|
||||
if (type() == MIRType_Double || type() == MIRType_Int32) {
|
||||
specialization_ = MIRType_Int32;
|
||||
setResultType(MIRType_Int32);
|
||||
if (range())
|
||||
range()->wrapAroundToInt32();
|
||||
return true;
|
||||
}
|
||||
|
||||
specialization_ = MIRType_Int32;
|
||||
setResultType(MIRType_Int32);
|
||||
if (range())
|
||||
range()->wrapAroundToInt32();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1680,15 +1690,15 @@ MSub::truncate()
|
||||
// Remember analysis, needed for fallible checks.
|
||||
setTruncated(true);
|
||||
|
||||
// Modify the instruction if needed.
|
||||
if (type() != MIRType_Double)
|
||||
return false;
|
||||
if (type() == MIRType_Double || type() == MIRType_Int32) {
|
||||
specialization_ = MIRType_Int32;
|
||||
setResultType(MIRType_Int32);
|
||||
if (range())
|
||||
range()->wrapAroundToInt32();
|
||||
return true;
|
||||
}
|
||||
|
||||
specialization_ = MIRType_Int32;
|
||||
setResultType(MIRType_Int32);
|
||||
if (range())
|
||||
range()->wrapAroundToInt32();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1697,22 +1707,16 @@ MMul::truncate()
|
||||
// Remember analysis, needed to remove negative zero checks.
|
||||
setTruncated(true);
|
||||
|
||||
// Modify the instruction.
|
||||
bool truncated = type() == MIRType_Int32;
|
||||
if (type() == MIRType_Double) {
|
||||
if (type() == MIRType_Double || type() == MIRType_Int32) {
|
||||
specialization_ = MIRType_Int32;
|
||||
setResultType(MIRType_Int32);
|
||||
truncated = true;
|
||||
JS_ASSERT(range());
|
||||
}
|
||||
|
||||
if (truncated && range()) {
|
||||
range()->wrapAroundToInt32();
|
||||
setTruncated(true);
|
||||
setCanBeNegativeZero(false);
|
||||
if (range())
|
||||
range()->wrapAroundToInt32();
|
||||
return true;
|
||||
}
|
||||
|
||||
return truncated;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -174,6 +174,9 @@ class Range : public TempObject {
|
||||
symbolicLower_(NULL),
|
||||
symbolicUpper_(NULL)
|
||||
{
|
||||
JS_ASSERT(e >= (h == INT64_MIN ? MaxDoubleExponent : mozilla::FloorLog2(mozilla::Abs(h))));
|
||||
JS_ASSERT(e >= (l == INT64_MIN ? MaxDoubleExponent : mozilla::FloorLog2(mozilla::Abs(l))));
|
||||
|
||||
setLowerInit(l);
|
||||
setUpperInit(h);
|
||||
rectifyExponent();
|
||||
|
@ -5092,6 +5092,9 @@ ProcessArgs(JSContext *cx, JSObject *obj_, OptionParser *op)
|
||||
return OptionFailure("ion-range-analysis", str);
|
||||
}
|
||||
|
||||
if (op->getBoolOption("ion-check-range-analysis"))
|
||||
ion::js_IonOptions.checkRangeAnalysis = true;
|
||||
|
||||
if (const char *str = op->getStringOption("ion-inlining")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
ion::js_IonOptions.inlining = true;
|
||||
@ -5377,6 +5380,8 @@ main(int argc, char **argv, char **envp)
|
||||
"Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-range-analysis", "on/off",
|
||||
"Range analysis (default: off, on to enable)")
|
||||
|| !op.addBoolOption('\0', "ion-check-range-analysis",
|
||||
"Range analysis checking")
|
||||
|| !op.addStringOption('\0', "ion-inlining", "on/off",
|
||||
"Inline methods where possible (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-osr", "on/off",
|
||||
|
Loading…
Reference in New Issue
Block a user