Bug 1000605 - Prevent division's truncation after removal of Math functions. r=sunfish,bbouvier

This commit is contained in:
Nicolas B. Pierron 2014-05-21 07:51:59 -07:00
parent 4a35fe6ead
commit 4a8cc281cc
8 changed files with 138 additions and 3 deletions

View File

@ -0,0 +1,43 @@
setJitCompilerOption("baseline.usecount.trigger", 0);
setJitCompilerOption("ion.usecount.trigger", 0);
function ceil(a, b) {
return Math.ceil((a | 0) / (b | 0)) | 0;
}
function floor(a, b) {
return Math.floor((a | 0) / (b | 0)) | 0;
}
function round(a, b) {
return Math.round((a | 0) / (b | 0)) | 0;
}
function intdiv(a, b) {
return ((a | 0) / (b | 0)) | 0;
}
// Always rounds up.
assertEq(ceil(5, 5), 1);
assertEq(ceil(4, 3), 2);
assertEq(ceil(5, 3), 2);
assertEq(ceil(-4, 3), -1);
assertEq(ceil(-5, 3), -1);
// Always rounds down.
assertEq(floor(5, 5), 1);
assertEq(floor(4, 3), 1);
assertEq(floor(5, 3), 1);
assertEq(floor(-4, 3), -2);
assertEq(floor(-5, 3), -2);
// Always rounds towards the nearest.
assertEq(round(5, 5), 1);
assertEq(round(4, 3), 1);
assertEq(round(5, 3), 2);
assertEq(round(-4, 3), -1);
assertEq(round(-5, 3), -2);
// Always rounds towards zero.
assertEq(intdiv(5, 5), 1);
assertEq(intdiv(4, 3), 1);
assertEq(intdiv(5, 3), 1);
assertEq(intdiv(-4, 3), -1);
assertEq(intdiv(-5, 3), -1);

View File

@ -1638,6 +1638,12 @@ LIRGenerator::visitNop(MNop *nop)
return true;
}
bool
LIRGenerator::visitLimitedTruncate(MLimitedTruncate *nop)
{
return redefine(nop, nop->input());
}
bool
LIRGenerator::visitOsrEntry(MOsrEntry *entry)
{

View File

@ -141,6 +141,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitStart(MStart *start);
bool visitOsrEntry(MOsrEntry *entry);
bool visitNop(MNop *nop);
bool visitLimitedTruncate(MLimitedTruncate *nop);
bool visitOsrValue(MOsrValue *value);
bool visitOsrScopeChain(MOsrScopeChain *object);
bool visitOsrReturnValue(MOsrReturnValue *value);

View File

@ -651,7 +651,14 @@ IonBuilder::inlineMathFloor(CallInfo &callInfo)
// Math.floor(int(x)) == int(x)
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
callInfo.setImplicitlyUsedUnchecked();
current->push(callInfo.getArg(0));
// The int operand may be something which bails out if the actual value
// is not in the range of the result type of the MIR. We need to tell
// the optimizer to preserve this bailout even if the final result is
// fully truncated.
MLimitedTruncate *ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
@ -689,7 +696,14 @@ IonBuilder::inlineMathCeil(CallInfo &callInfo)
// Math.ceil(int(x)) == int(x)
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
callInfo.setImplicitlyUsedUnchecked();
current->push(callInfo.getArg(0));
// The int operand may be something which bails out if the actual value
// is not in the range of the result type of the MIR. We need to tell
// the optimizer to preserve this bailout even if the final result is
// fully truncated.
MLimitedTruncate *ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}
@ -719,7 +733,14 @@ IonBuilder::inlineMathRound(CallInfo &callInfo)
// Math.round(int(x)) == int(x)
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
callInfo.setImplicitlyUsedUnchecked();
current->push(callInfo.getArg(0));
// The int operand may be something which bails out if the actual value
// is not in the range of the result type of the MIR. We need to tell
// the optimizer to preserve this bailout even if the final result is
// fully truncated.
MLimitedTruncate *ins = MLimitedTruncate::New(alloc(), callInfo.getArg(0),
MDefinition::IndirectTruncate);
current->add(ins);
current->push(ins);
return InliningStatus_Inlined;
}

View File

@ -1013,6 +1013,45 @@ class MNop : public MNullaryInstruction
}
};
// No-op instruction. This cannot be moved or eliminated, and is intended for
// protecting the input against follow-up optimization.
class MLimitedTruncate : public MUnaryInstruction
{
public:
TruncateKind truncate_;
TruncateKind truncateLimit_;
protected:
MLimitedTruncate(MDefinition *input, TruncateKind limit)
: MUnaryInstruction(input),
truncate_(NoTruncate),
truncateLimit_(limit)
{
setResultType(input->type());
setResultTypeSet(input->resultTypeSet());
}
public:
INSTRUCTION_HEADER(LimitedTruncate)
static MLimitedTruncate *New(TempAllocator &alloc, MDefinition *input, TruncateKind kind) {
return new(alloc) MLimitedTruncate(input, kind);
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
TruncateKind operandTruncateKind(size_t index) const;
TruncateKind truncateKind() const {
return truncate_;
}
void setTruncateKind(TruncateKind kind) {
truncate_ = kind;
}
};
// A constant js::Value.
class MConstant : public MNullaryInstruction
{

View File

@ -101,6 +101,7 @@ namespace jit {
_(Start) \
_(OsrEntry) \
_(Nop) \
_(LimitedTruncate) \
_(RegExp) \
_(RegExpExec) \
_(RegExpTest) \

View File

@ -194,6 +194,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
SAFE_OP(Start)
UNSAFE_OP(OsrEntry)
SAFE_OP(Nop)
SAFE_OP(LimitedTruncate)
UNSAFE_OP(RegExp)
CUSTOM_OP(Lambda)
UNSAFE_OP(LambdaArrow)

View File

@ -1384,6 +1384,13 @@ MToInt32::computeRange(TempAllocator &alloc)
setRange(output);
}
void
MLimitedTruncate::computeRange(TempAllocator &alloc)
{
Range *output = new(alloc) Range(input());
setRange(output);
}
static Range *GetTypedArrayRange(TempAllocator &alloc, int type)
{
switch (type) {
@ -2283,6 +2290,16 @@ MLoadTypedArrayElementStatic::truncate(TruncateKind kind)
return false;
}
bool
MLimitedTruncate::truncate(TruncateKind kind)
{
setTruncateKind(kind);
setResultType(MIRType_Int32);
if (kind >= IndirectTruncate && range())
range()->wrapAroundToInt32();
return false;
}
MDefinition::TruncateKind
MDefinition::operandTruncateKind(size_t index) const
{
@ -2304,6 +2321,12 @@ MBinaryBitwiseInstruction::operandTruncateKind(size_t index) const
return Truncate;
}
MDefinition::TruncateKind
MLimitedTruncate::operandTruncateKind(size_t index) const
{
return Min(truncateKind(), truncateLimit_);
}
MDefinition::TruncateKind
MAdd::operandTruncateKind(size_t index) const
{