mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1000605 - Prevent division's truncation after removal of Math functions. r=sunfish,bbouvier
This commit is contained in:
parent
4a35fe6ead
commit
4a8cc281cc
43
js/src/jit-test/tests/ion/bug1000605.js
Normal file
43
js/src/jit-test/tests/ion/bug1000605.js
Normal 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);
|
@ -1638,6 +1638,12 @@ LIRGenerator::visitNop(MNop *nop)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LIRGenerator::visitLimitedTruncate(MLimitedTruncate *nop)
|
||||||
|
{
|
||||||
|
return redefine(nop, nop->input());
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LIRGenerator::visitOsrEntry(MOsrEntry *entry)
|
LIRGenerator::visitOsrEntry(MOsrEntry *entry)
|
||||||
{
|
{
|
||||||
|
@ -141,6 +141,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||||||
bool visitStart(MStart *start);
|
bool visitStart(MStart *start);
|
||||||
bool visitOsrEntry(MOsrEntry *entry);
|
bool visitOsrEntry(MOsrEntry *entry);
|
||||||
bool visitNop(MNop *nop);
|
bool visitNop(MNop *nop);
|
||||||
|
bool visitLimitedTruncate(MLimitedTruncate *nop);
|
||||||
bool visitOsrValue(MOsrValue *value);
|
bool visitOsrValue(MOsrValue *value);
|
||||||
bool visitOsrScopeChain(MOsrScopeChain *object);
|
bool visitOsrScopeChain(MOsrScopeChain *object);
|
||||||
bool visitOsrReturnValue(MOsrReturnValue *value);
|
bool visitOsrReturnValue(MOsrReturnValue *value);
|
||||||
|
@ -651,7 +651,14 @@ IonBuilder::inlineMathFloor(CallInfo &callInfo)
|
|||||||
// Math.floor(int(x)) == int(x)
|
// Math.floor(int(x)) == int(x)
|
||||||
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
|
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
|
||||||
callInfo.setImplicitlyUsedUnchecked();
|
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;
|
return InliningStatus_Inlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,7 +696,14 @@ IonBuilder::inlineMathCeil(CallInfo &callInfo)
|
|||||||
// Math.ceil(int(x)) == int(x)
|
// Math.ceil(int(x)) == int(x)
|
||||||
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
|
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
|
||||||
callInfo.setImplicitlyUsedUnchecked();
|
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;
|
return InliningStatus_Inlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,7 +733,14 @@ IonBuilder::inlineMathRound(CallInfo &callInfo)
|
|||||||
// Math.round(int(x)) == int(x)
|
// Math.round(int(x)) == int(x)
|
||||||
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
|
if (argType == MIRType_Int32 && returnType == MIRType_Int32) {
|
||||||
callInfo.setImplicitlyUsedUnchecked();
|
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;
|
return InliningStatus_Inlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
// A constant js::Value.
|
||||||
class MConstant : public MNullaryInstruction
|
class MConstant : public MNullaryInstruction
|
||||||
{
|
{
|
||||||
|
@ -101,6 +101,7 @@ namespace jit {
|
|||||||
_(Start) \
|
_(Start) \
|
||||||
_(OsrEntry) \
|
_(OsrEntry) \
|
||||||
_(Nop) \
|
_(Nop) \
|
||||||
|
_(LimitedTruncate) \
|
||||||
_(RegExp) \
|
_(RegExp) \
|
||||||
_(RegExpExec) \
|
_(RegExpExec) \
|
||||||
_(RegExpTest) \
|
_(RegExpTest) \
|
||||||
|
@ -194,6 +194,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
|||||||
SAFE_OP(Start)
|
SAFE_OP(Start)
|
||||||
UNSAFE_OP(OsrEntry)
|
UNSAFE_OP(OsrEntry)
|
||||||
SAFE_OP(Nop)
|
SAFE_OP(Nop)
|
||||||
|
SAFE_OP(LimitedTruncate)
|
||||||
UNSAFE_OP(RegExp)
|
UNSAFE_OP(RegExp)
|
||||||
CUSTOM_OP(Lambda)
|
CUSTOM_OP(Lambda)
|
||||||
UNSAFE_OP(LambdaArrow)
|
UNSAFE_OP(LambdaArrow)
|
||||||
|
@ -1384,6 +1384,13 @@ MToInt32::computeRange(TempAllocator &alloc)
|
|||||||
setRange(output);
|
setRange(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MLimitedTruncate::computeRange(TempAllocator &alloc)
|
||||||
|
{
|
||||||
|
Range *output = new(alloc) Range(input());
|
||||||
|
setRange(output);
|
||||||
|
}
|
||||||
|
|
||||||
static Range *GetTypedArrayRange(TempAllocator &alloc, int type)
|
static Range *GetTypedArrayRange(TempAllocator &alloc, int type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -2283,6 +2290,16 @@ MLoadTypedArrayElementStatic::truncate(TruncateKind kind)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MLimitedTruncate::truncate(TruncateKind kind)
|
||||||
|
{
|
||||||
|
setTruncateKind(kind);
|
||||||
|
setResultType(MIRType_Int32);
|
||||||
|
if (kind >= IndirectTruncate && range())
|
||||||
|
range()->wrapAroundToInt32();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
MDefinition::TruncateKind
|
MDefinition::TruncateKind
|
||||||
MDefinition::operandTruncateKind(size_t index) const
|
MDefinition::operandTruncateKind(size_t index) const
|
||||||
{
|
{
|
||||||
@ -2304,6 +2321,12 @@ MBinaryBitwiseInstruction::operandTruncateKind(size_t index) const
|
|||||||
return Truncate;
|
return Truncate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MDefinition::TruncateKind
|
||||||
|
MLimitedTruncate::operandTruncateKind(size_t index) const
|
||||||
|
{
|
||||||
|
return Min(truncateKind(), truncateLimit_);
|
||||||
|
}
|
||||||
|
|
||||||
MDefinition::TruncateKind
|
MDefinition::TruncateKind
|
||||||
MAdd::operandTruncateKind(size_t index) const
|
MAdd::operandTruncateKind(size_t index) const
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user