mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 888109: Float32 general optimizations for IonMonkey: framework and arithmetic operations; r=sstangl,nbp
This commit is contained in:
parent
9e93b3a656
commit
69698e03f0
@ -1273,6 +1273,14 @@ DoubleValue(double dbl)
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Value
|
||||
Float32Value(float f)
|
||||
{
|
||||
Value v;
|
||||
v.setDouble(f);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Value
|
||||
StringValue(JSString *str)
|
||||
{
|
||||
|
@ -2180,6 +2180,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void addss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("addss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void addsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("addsd %s0x%x(%s), %s",
|
||||
@ -2188,6 +2196,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void addss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("addss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void addsd_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
@ -2196,6 +2212,13 @@ public:
|
||||
m_formatter.prefix(PRE_SSE_F2);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
void addss_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
spew("addss %p, %s",
|
||||
address, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_ADDSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
#endif
|
||||
|
||||
void cvtss2sd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
@ -2214,6 +2237,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_CVTSD2SS_VsdEd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void cvtsi2ss_rr(RegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2ss %s, %s",
|
||||
nameIReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, src);
|
||||
}
|
||||
|
||||
void cvtsi2sd_rr(RegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2sd %s, %s",
|
||||
@ -2248,6 +2279,22 @@ public:
|
||||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
|
||||
}
|
||||
|
||||
void cvtsi2ss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2ss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void cvtsi2ss_mr(int offset, RegisterID base, RegisterID index, int scale, XMMRegisterID dst)
|
||||
{
|
||||
spew("cvtsi2ss %d(%s,%s,%d), %s",
|
||||
offset, nameIReg(base), nameIReg(index), 1<<scale, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTSI2SD_VsdEd, (RegisterID)dst, base, index, scale, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void cvtsi2sd_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
@ -2266,6 +2313,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void cvttss2si_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
spew("cvttss2si %s, %s",
|
||||
nameFPReg(src), nameIReg(4, dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_CVTTSD2SI_GdWsd, dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
#if WTF_CPU_X86_64
|
||||
void cvttsd2sq_rr(XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
@ -2391,16 +2446,6 @@ public:
|
||||
m_formatter.twoByteOp_disp32(OP2_MOVSD_WsdVsd, (RegisterID)src, base, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void movss_rm(XMMRegisterID src, const void* addr)
|
||||
{
|
||||
spew("movss %s, %p",
|
||||
nameFPReg(src), addr);
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void movss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %s0x%x(%s), %s",
|
||||
@ -2417,16 +2462,6 @@ public:
|
||||
m_formatter.twoByteOp_disp32(OP2_MOVSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void movss_mr(const void* addr, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %p, %s",
|
||||
addr, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void movsd_rm(XMMRegisterID src, int offset, RegisterID base, RegisterID index, int scale)
|
||||
{
|
||||
spew("movsd %s, %d(%s,%s,%d)",
|
||||
@ -2483,6 +2518,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void movss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
#if !WTF_CPU_X86_64
|
||||
void movsd_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
@ -2492,6 +2535,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
|
||||
void movss_mr(const void* address, XMMRegisterID dst)
|
||||
{
|
||||
spew("movss %p, %s",
|
||||
address, nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_VsdWsd, (RegisterID)dst, address);
|
||||
}
|
||||
|
||||
void movsd_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movsd %s, %p",
|
||||
@ -2499,6 +2550,14 @@ public:
|
||||
m_formatter.prefix(PRE_SSE_F2);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
|
||||
}
|
||||
|
||||
void movss_rm(XMMRegisterID src, const void* address)
|
||||
{
|
||||
spew("movss %s, %p",
|
||||
nameFPReg(src), address);
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MOVSD_WsdVsd, (RegisterID)src, address);
|
||||
}
|
||||
#else
|
||||
JmpSrc movsd_ripr(XMMRegisterID dst)
|
||||
{
|
||||
@ -2558,6 +2617,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void mulss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("mulss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void mulsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("mulsd %s0x%x(%s), %s",
|
||||
@ -2566,6 +2633,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void mulss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("mulss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MULSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void pextrw_irr(int whichWord, XMMRegisterID src, RegisterID dst)
|
||||
{
|
||||
FIXME_INSN_PRINTING;
|
||||
@ -2582,6 +2657,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void subss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("subss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void subsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("subsd %s0x%x(%s), %s",
|
||||
@ -2590,6 +2673,21 @@ public:
|
||||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void subss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("subss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_SUBSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void ucomiss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("ucomiss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.twoByteOp(OP2_UCOMISD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void ucomisd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("ucomisd %s, %s",
|
||||
@ -2614,6 +2712,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void divss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("divss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void divsd_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("divsd %s0x%x(%s), %s",
|
||||
@ -2622,6 +2728,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void divss_mr(int offset, RegisterID base, XMMRegisterID dst)
|
||||
{
|
||||
spew("divss %s0x%x(%s), %s",
|
||||
PRETTY_PRINT_OFFSET(offset), nameIReg(base), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_DIVSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void xorpd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("xorpd %s, %s",
|
||||
@ -2630,6 +2744,13 @@ public:
|
||||
m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void xorps_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("xorps %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.twoByteOp(OP2_XORPD_VpdWpd, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void orpd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("orpd %s, %s",
|
||||
@ -2794,6 +2915,11 @@ public:
|
||||
spew(".double %.20f", d);
|
||||
m_formatter.doubleConstant(d);
|
||||
}
|
||||
void floatConstant(float f)
|
||||
{
|
||||
spew(".float %.20f", f);
|
||||
m_formatter.floatConstant(f);
|
||||
}
|
||||
|
||||
void int64Constant(int64_t i)
|
||||
{
|
||||
@ -3434,6 +3560,17 @@ private:
|
||||
m_buffer.putInt64Unchecked(u.u64);
|
||||
}
|
||||
|
||||
void floatConstant(float f)
|
||||
{
|
||||
m_buffer.ensureSpace(sizeof(float));
|
||||
union {
|
||||
uint32_t u32;
|
||||
float f;
|
||||
} u;
|
||||
u.f = f;
|
||||
m_buffer.putIntUnchecked(u.u32);
|
||||
}
|
||||
|
||||
void int64Constant(int64_t i)
|
||||
{
|
||||
m_buffer.ensureSpace(sizeof(int64_t));
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/IonLinker.h"
|
||||
#include "jit/IonSpewer.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/PerfSpewer.h"
|
||||
#include "jit/VMFunctions.h"
|
||||
|
||||
@ -5168,7 +5169,14 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
|
||||
if (type_ == ScalarTypeRepresentation::TYPE_FLOAT32 || type_ == ScalarTypeRepresentation::TYPE_FLOAT64) {
|
||||
masm.ensureDouble(value, FloatReg0, &failure);
|
||||
masm.storeToTypedFloatArray(type_, FloatReg0, dest);
|
||||
if (LIRGenerator::allowFloat32Optimizations() &&
|
||||
type_ == ScalarTypeRepresentation::TYPE_FLOAT32)
|
||||
{
|
||||
masm.convertDoubleToFloat(FloatReg0, ScratchFloatReg);
|
||||
masm.storeToTypedFloatArray(type_, ScratchFloatReg, dest);
|
||||
} else {
|
||||
masm.storeToTypedFloatArray(type_, FloatReg0, dest);
|
||||
}
|
||||
EmitReturnFromIC(masm);
|
||||
} else if (type_ == ScalarTypeRepresentation::TYPE_UINT8_CLAMPED) {
|
||||
Label notInt32;
|
||||
|
@ -302,6 +302,67 @@ CodeGenerator::visitValueToDouble(LValueToDouble *lir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitValueToFloat32(LValueToFloat32 *lir)
|
||||
{
|
||||
MToFloat32 *mir = lir->mir();
|
||||
ValueOperand operand = ToValue(lir, LValueToFloat32::Input);
|
||||
FloatRegister output = ToFloatRegister(lir->output());
|
||||
|
||||
Register tag = masm.splitTagForTest(operand);
|
||||
|
||||
Label isDouble, isInt32, isBool, isNull, isUndefined, done;
|
||||
bool hasBoolean = false, hasNull = false, hasUndefined = false;
|
||||
|
||||
masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
|
||||
masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
|
||||
|
||||
if (mir->conversion() != MToFloat32::NumbersOnly) {
|
||||
masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
|
||||
masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
|
||||
hasBoolean = true;
|
||||
hasUndefined = true;
|
||||
if (mir->conversion() != MToFloat32::NonNullNonStringPrimitives) {
|
||||
masm.branchTestNull(Assembler::Equal, tag, &isNull);
|
||||
hasNull = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bailout(lir->snapshot()))
|
||||
return false;
|
||||
|
||||
if (hasNull) {
|
||||
static float DoubleZeroFloat = DoubleZero;
|
||||
masm.bind(&isNull);
|
||||
masm.loadStaticFloat32(&DoubleZeroFloat, output);
|
||||
masm.jump(&done);
|
||||
}
|
||||
|
||||
if (hasUndefined) {
|
||||
static float js_NaN_float = js_NaN;
|
||||
masm.bind(&isUndefined);
|
||||
masm.loadStaticFloat32(&js_NaN_float, output);
|
||||
masm.jump(&done);
|
||||
}
|
||||
|
||||
if (hasBoolean) {
|
||||
masm.bind(&isBool);
|
||||
masm.boolValueToFloat32(operand, output);
|
||||
masm.jump(&done);
|
||||
}
|
||||
|
||||
masm.bind(&isInt32);
|
||||
masm.int32ValueToFloat32(operand, output);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&isDouble);
|
||||
masm.unboxDouble(operand, output);
|
||||
masm.convertDoubleToFloat(output, output);
|
||||
masm.bind(&done);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
|
||||
{
|
||||
@ -309,6 +370,27 @@ CodeGenerator::visitInt32ToDouble(LInt32ToDouble *lir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble *lir)
|
||||
{
|
||||
masm.convertFloatToDouble(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32 *lir)
|
||||
{
|
||||
masm.convertDoubleToFloat(ToFloatRegister(lir->input()), ToFloatRegister(lir->output()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32 *lir)
|
||||
{
|
||||
masm.convertInt32ToFloat32(ToRegister(lir->input()), ToFloatRegister(lir->output()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitDoubleToInt32(LDoubleToInt32 *lir)
|
||||
{
|
||||
@ -7439,6 +7521,19 @@ CodeGenerator::visitAssertRangeD(LAssertRangeD *ins)
|
||||
return emitAssertRangeD(r, input, temp);
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitAssertRangeF(LAssertRangeF *ins)
|
||||
{
|
||||
FloatRegister input = ToFloatRegister(ins->input());
|
||||
FloatRegister temp = ToFloatRegister(ins->temp());
|
||||
Range *r = ins->range();
|
||||
|
||||
masm.convertFloatToDouble(input, input);
|
||||
bool success = emitAssertRangeD(r, input, temp);
|
||||
masm.convertDoubleToFloat(input, input);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitAssertRangeV(LAssertRangeV *ins)
|
||||
{
|
||||
|
@ -71,6 +71,10 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
bool visitMoveGroup(LMoveGroup *group);
|
||||
bool visitValueToInt32(LValueToInt32 *lir);
|
||||
bool visitValueToDouble(LValueToDouble *lir);
|
||||
bool visitValueToFloat32(LValueToFloat32 *lir);
|
||||
bool visitFloat32ToDouble(LFloat32ToDouble *lir);
|
||||
bool visitDoubleToFloat32(LDoubleToFloat32 *lir);
|
||||
bool visitInt32ToFloat32(LInt32ToFloat32 *lir);
|
||||
bool visitInt32ToDouble(LInt32ToDouble *lir);
|
||||
void emitOOLTestObject(Register objreg, Label *ifTruthy, Label *ifFalsy, Register scratch);
|
||||
bool visitTestOAndBranch(LTestOAndBranch *lir);
|
||||
@ -305,6 +309,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
|
||||
bool visitAssertRangeI(LAssertRangeI *ins);
|
||||
bool visitAssertRangeD(LAssertRangeD *ins);
|
||||
bool visitAssertRangeF(LAssertRangeF *ins);
|
||||
bool visitAssertRangeV(LAssertRangeV *ins);
|
||||
|
||||
IonScriptCounts *extractUnassociatedScriptCounts() {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/IonBuilder.h"
|
||||
#include "jit/LIR.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
#include "jsinferinlines.h"
|
||||
@ -400,6 +401,12 @@ class TypeAnalyzer
|
||||
bool adjustInputs(MDefinition *def);
|
||||
bool insertConversions();
|
||||
|
||||
bool graphContainsFloat32();
|
||||
bool markPhiConsumers();
|
||||
bool markPhiProducers();
|
||||
bool specializeValidFloatOps();
|
||||
bool tryEmitFloatOperations();
|
||||
|
||||
public:
|
||||
TypeAnalyzer(MIRGenerator *mir, MIRGraph &graph)
|
||||
: mir(mir), graph(graph)
|
||||
@ -415,6 +422,7 @@ static MIRType
|
||||
GuessPhiType(MPhi *phi)
|
||||
{
|
||||
MIRType type = MIRType_None;
|
||||
bool convertibleToFloat32 = false;
|
||||
for (size_t i = 0, e = phi->numOperands(); i < e; i++) {
|
||||
MDefinition *in = phi->getOperand(i);
|
||||
if (in->isPhi()) {
|
||||
@ -429,6 +437,8 @@ GuessPhiType(MPhi *phi)
|
||||
}
|
||||
if (type == MIRType_None) {
|
||||
type = in->type();
|
||||
if (in->isConstant())
|
||||
convertibleToFloat32 = true;
|
||||
continue;
|
||||
}
|
||||
if (type != in->type()) {
|
||||
@ -436,11 +446,20 @@ GuessPhiType(MPhi *phi)
|
||||
if (in->resultTypeSet() && in->resultTypeSet()->empty())
|
||||
continue;
|
||||
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
if (IsNumberType(type) && IsNumberType(in->type()))
|
||||
if (IsFloatType(type) && IsFloatType(in->type())) {
|
||||
// Specialize phis with int32 and float32 operands as float32.
|
||||
type = MIRType_Float32;
|
||||
} else if (convertibleToFloat32 && in->type() == MIRType_Float32) {
|
||||
// If we only saw constants before and encounter a Float32 value, promote previous
|
||||
// constants to Float32
|
||||
type = MIRType_Float32;
|
||||
} else if (IsNumberType(type) && IsNumberType(in->type())) {
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
type = MIRType_Double;
|
||||
else
|
||||
convertibleToFloat32 &= in->isConstant();
|
||||
} else {
|
||||
return MIRType_Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
@ -476,6 +495,13 @@ TypeAnalyzer::propagateSpecialization(MPhi *phi)
|
||||
continue;
|
||||
}
|
||||
if (use->type() != phi->type()) {
|
||||
// Specialize phis with int32 and float operands as floats.
|
||||
if (IsFloatType(use->type()) && IsFloatType(phi->type())) {
|
||||
if (!respecialize(use, MIRType_Float32))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Specialize phis with int32 and double operands as double.
|
||||
if (IsNumberType(use->type()) && IsNumberType(phi->type())) {
|
||||
if (!respecialize(use, MIRType_Double))
|
||||
@ -546,9 +572,24 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
|
||||
} else {
|
||||
MInstruction *replacement;
|
||||
|
||||
if (phiType == MIRType_Double && in->type() == MIRType_Int32) {
|
||||
if (phiType == MIRType_Double && IsFloatType(in->type())) {
|
||||
// Convert int32 operands to double.
|
||||
replacement = MToDouble::New(in);
|
||||
} else if (phiType == MIRType_Float32) {
|
||||
if (in->type() == MIRType_Int32 || in->type() == MIRType_Double) {
|
||||
replacement = MToFloat32::New(in);
|
||||
} else {
|
||||
// See comment below
|
||||
if (in->type() != MIRType_Value) {
|
||||
MBox *box = MBox::New(in);
|
||||
in->block()->insertBefore(in->block()->lastIns(), box);
|
||||
in = box;
|
||||
}
|
||||
|
||||
MUnbox *unbox = MUnbox::New(in, MIRType_Double, MUnbox::Fallible);
|
||||
in->block()->insertBefore(in->block()->lastIns(), unbox);
|
||||
replacement = MToFloat32::New(in);
|
||||
}
|
||||
} else {
|
||||
// If we know this branch will fail to convert to phiType,
|
||||
// insert a box that'll immediately fail in the fallible unbox
|
||||
@ -583,8 +624,7 @@ TypeAnalyzer::adjustPhiInputs(MPhi *phi)
|
||||
// the original box.
|
||||
phi->replaceOperand(i, in->toUnbox()->input());
|
||||
} else {
|
||||
MBox *box = MBox::New(in);
|
||||
in->block()->insertBefore(in->block()->lastIns(), box);
|
||||
MDefinition *box = BoxInputsPolicy::alwaysBoxAt(in->block()->lastIns(), in);
|
||||
phi->replaceOperand(i, box);
|
||||
}
|
||||
}
|
||||
@ -650,9 +690,222 @@ TypeAnalyzer::insertConversions()
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function tries to emit Float32 specialized operations whenever it's possible.
|
||||
// MIR nodes are flagged as:
|
||||
// - Producers, when they can create Float32 that might need to be coerced into a Double.
|
||||
// Loads in Float32 arrays and conversions to Float32 are producers.
|
||||
// - Consumers, when they can have Float32 as inputs and validate a legal use of a Float32.
|
||||
// Stores in Float32 arrays and conversions to Float32 are consumers.
|
||||
// - Float32 commutative, when using the Float32 instruction instead of the Double instruction
|
||||
// does not result in a compound loss of precision. This is the case for +, -, /, * with 2
|
||||
// operands, for instance. However, an addition with 3 operands is not commutative anymore,
|
||||
// so an intermediate coercion is needed.
|
||||
// Except for phis, all these flags are known after Ion building, so they cannot change during
|
||||
// the process.
|
||||
//
|
||||
// The idea behind the algorithm is easy: whenever we can prove that a commutative operation
|
||||
// has only producers as inputs and consumers as uses, we can specialize the operation as a
|
||||
// float32 operation. Otherwise, we have to convert all float32 inputs to doubles. Even
|
||||
// if a lot of conversions are produced, GVN will take care of eliminating the redundant ones.
|
||||
//
|
||||
// Phis have a special status. Phis need to be flagged as producers or consumers as they can
|
||||
// be inputs or outputs of commutative instructions. Fortunately, producers and consumers
|
||||
// properties are such that we can deduce the property using all non phis inputs first (which form
|
||||
// an initial phi graph) and then propagate all properties from one phi to another using a
|
||||
// fixed point algorithm. The algorithm is ensured to terminate as each iteration has less or as
|
||||
// many flagged phis as the previous iteration (so the worst steady state case is all phis being
|
||||
// flagged as false).
|
||||
//
|
||||
// In a nutshell, the algorithm applies three passes:
|
||||
// 1 - Determine which phis are consumers. Each phi gets an initial value by making a global AND on
|
||||
// all its non-phi inputs. Then each phi propagates its value to other phis. If after propagation,
|
||||
// the flag value changed, we have to reapply the algorithm on all phi operands, as a phi is a
|
||||
// consumer if all of its uses are consumers.
|
||||
// 2 - Determine which phis are producers. It's the same algorithm, except that we have to reapply
|
||||
// the algorithm on all phi uses, as a phi is a producer if all of its operands are producers.
|
||||
// 3 - Go through all commutative operations and ensure their inputs are all producers and their
|
||||
// uses are all consumers.
|
||||
bool
|
||||
TypeAnalyzer::markPhiConsumers()
|
||||
{
|
||||
JS_ASSERT(phiWorklist_.empty());
|
||||
|
||||
// Iterate in postorder so worklist is initialized to RPO.
|
||||
for (PostorderIterator block(graph.poBegin()); block != graph.poEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Initial state"))
|
||||
return false;
|
||||
|
||||
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
|
||||
JS_ASSERT(!phi->isInWorklist());
|
||||
bool canConsumeFloat32 = true;
|
||||
for (MUseDefIterator use(*phi); canConsumeFloat32 && use; use++) {
|
||||
MDefinition *usedef = use.def();
|
||||
canConsumeFloat32 &= usedef->isPhi() || usedef->canConsumeFloat32();
|
||||
}
|
||||
phi->setCanConsumeFloat32(canConsumeFloat32);
|
||||
if (canConsumeFloat32 && !addPhiToWorklist(*phi))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (!phiWorklist_.empty()) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Consumer Phis - Fixed point"))
|
||||
return false;
|
||||
|
||||
MPhi *phi = popPhi();
|
||||
JS_ASSERT(phi->canConsumeFloat32());
|
||||
|
||||
bool validConsumer = true;
|
||||
for (MUseDefIterator use(phi); use; use++) {
|
||||
MDefinition *def = use.def();
|
||||
if (def->isPhi() && !def->canConsumeFloat32()) {
|
||||
validConsumer = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validConsumer)
|
||||
continue;
|
||||
|
||||
// Propagate invalidated phis
|
||||
phi->setCanConsumeFloat32(false);
|
||||
for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
|
||||
MDefinition *input = phi->getOperand(i);
|
||||
if (input->isPhi() && !input->isInWorklist() && input->canConsumeFloat32())
|
||||
{
|
||||
if (!addPhiToWorklist(input->toPhi()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::markPhiProducers()
|
||||
{
|
||||
JS_ASSERT(phiWorklist_.empty());
|
||||
|
||||
// Iterate in reverse postorder so worklist is initialized to PO.
|
||||
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - initial state"))
|
||||
return false;
|
||||
|
||||
for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); ++phi) {
|
||||
JS_ASSERT(!phi->isInWorklist());
|
||||
bool canProduceFloat32 = true;
|
||||
for (size_t i = 0, e = phi->numOperands(); canProduceFloat32 && i < e; ++i) {
|
||||
MDefinition *input = phi->getOperand(i);
|
||||
canProduceFloat32 &= input->isPhi() || input->canProduceFloat32();
|
||||
}
|
||||
phi->setCanProduceFloat32(canProduceFloat32);
|
||||
if (canProduceFloat32 && !addPhiToWorklist(*phi))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (!phiWorklist_.empty()) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Producer Phis - Fixed point"))
|
||||
return false;
|
||||
|
||||
MPhi *phi = popPhi();
|
||||
JS_ASSERT(phi->canProduceFloat32());
|
||||
|
||||
bool validProducer = true;
|
||||
for (size_t i = 0, e = phi->numOperands(); i < e; ++i) {
|
||||
MDefinition *input = phi->getOperand(i);
|
||||
if (input->isPhi() && !input->canProduceFloat32()) {
|
||||
validProducer = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validProducer)
|
||||
continue;
|
||||
|
||||
// Propagate invalidated phis
|
||||
phi->setCanProduceFloat32(false);
|
||||
for (MUseDefIterator use(phi); use; use++) {
|
||||
MDefinition *def = use.def();
|
||||
if (def->isPhi() && !def->isInWorklist() && def->canProduceFloat32())
|
||||
{
|
||||
if (!addPhiToWorklist(def->toPhi()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::specializeValidFloatOps()
|
||||
{
|
||||
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Instructions"))
|
||||
return false;
|
||||
|
||||
for (MInstructionIterator ins(block->begin()); ins != block->end(); ++ins) {
|
||||
if (!ins->isFloat32Commutative())
|
||||
continue;
|
||||
|
||||
if (ins->type() == MIRType_Float32)
|
||||
continue;
|
||||
|
||||
// This call will try to specialize the instruction iff all uses are consumers and
|
||||
// all inputs are producers.
|
||||
ins->trySpecializeFloat32();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::graphContainsFloat32()
|
||||
{
|
||||
for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); ++block) {
|
||||
if (mir->shouldCancel("Ensure Float32 commutativity - Graph contains Float32"))
|
||||
return false;
|
||||
|
||||
for (MDefinitionIterator def(*block); def; def++) {
|
||||
if (def->type() == MIRType_Float32)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::tryEmitFloatOperations()
|
||||
{
|
||||
// Backends that currently don't know how to generate Float32 specialized instructions
|
||||
// shouldn't run this pass and just let all instructions as specialized for Double.
|
||||
if (!LIRGenerator::allowFloat32Optimizations())
|
||||
return true;
|
||||
|
||||
// Asm.js uses the ahead of time type checks to specialize operations, no need to check
|
||||
// them again at this point.
|
||||
if (mir->compilingAsmJS())
|
||||
return true;
|
||||
|
||||
// Check ahead of time that there is at least one definition typed as Float32, otherwise we
|
||||
// don't need this pass.
|
||||
if (!graphContainsFloat32())
|
||||
return true;
|
||||
|
||||
if (!markPhiConsumers())
|
||||
return false;
|
||||
if (!markPhiProducers())
|
||||
return false;
|
||||
if (!specializeValidFloatOps())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TypeAnalyzer::analyze()
|
||||
{
|
||||
if (!tryEmitFloatOperations())
|
||||
return false;
|
||||
if (!specializePhis())
|
||||
return false;
|
||||
if (!insertConversions())
|
||||
|
@ -6305,6 +6305,7 @@ jit::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *input
|
||||
case MIRType_Boolean:
|
||||
case MIRType_Int32:
|
||||
case MIRType_Double:
|
||||
case MIRType_Float32:
|
||||
case MIRType_String:
|
||||
case MIRType_Magic:
|
||||
return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input)));
|
||||
@ -7023,6 +7024,8 @@ IonBuilder::jsop_getelem_typed(MDefinition *obj, MDefinition *index,
|
||||
knownType = allowDouble ? MIRType_Double : MIRType_Int32;
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
knownType = (LIRGenerator::allowFloat32Optimizations()) ? MIRType_Float32 : MIRType_Double;
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
knownType = MIRType_Double;
|
||||
break;
|
||||
|
@ -1241,6 +1241,22 @@ SnapshotIterator::slotValue(const Slot &slot)
|
||||
case SnapshotReader::DOUBLE_REG:
|
||||
return DoubleValue(machine_.read(slot.floatReg()));
|
||||
|
||||
case SnapshotReader::FLOAT32_REG:
|
||||
{
|
||||
double asDouble = machine_.read(slot.floatReg());
|
||||
// The register contains the encoding of a float32. We just read
|
||||
// the bits without making any conversion.
|
||||
float asFloat = *(float*) &asDouble;
|
||||
return DoubleValue(asFloat);
|
||||
}
|
||||
|
||||
case SnapshotReader::FLOAT32_STACK:
|
||||
{
|
||||
double asDouble = ReadFrameDoubleSlot(fp_, slot.stackSlot());
|
||||
float asFloat = *(float*) &asDouble; // no conversion, see comment above.
|
||||
return DoubleValue(asFloat);
|
||||
}
|
||||
|
||||
case SnapshotReader::TYPED_REG:
|
||||
return FromTypedPayload(slot.knownType(), machine_.read(slot.reg()));
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/BaselineRegisters.h"
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/MIR.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
|
||||
@ -304,6 +305,41 @@ MacroAssembler::moveNurseryPtr(const ImmMaybeNurseryPtr &ptr, const Register &re
|
||||
movePtr(ptr, reg);
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
static void
|
||||
StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest) {
|
||||
switch (arrayType) {
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
if (LIRGenerator::allowFloat32Optimizations()) {
|
||||
masm.storeFloat(value, dest);
|
||||
} else {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
// See the comment in ToDoubleForTypedArray.
|
||||
masm.canonicalizeDouble(value);
|
||||
#endif
|
||||
masm.convertDoubleToFloat(value, ScratchFloatReg);
|
||||
masm.storeFloat(ScratchFloatReg, dest);
|
||||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
// See the comment in ToDoubleForTypedArray.
|
||||
masm.canonicalizeDouble(value);
|
||||
#endif
|
||||
masm.storeDouble(value, dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest) {
|
||||
StoreToTypedFloatArray(*this, arrayType, value, dest);
|
||||
}
|
||||
void MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest) {
|
||||
StoreToTypedFloatArray(*this, arrayType, value, dest);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp,
|
||||
@ -337,11 +373,16 @@ MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest
|
||||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
if (arrayType == ScalarTypeRepresentation::TYPE_FLOAT32)
|
||||
if (LIRGenerator::allowFloat32Optimizations()) {
|
||||
loadFloat(src, dest.fpu());
|
||||
canonicalizeFloat(dest.fpu());
|
||||
} else {
|
||||
loadFloatAsDouble(src, dest.fpu());
|
||||
else
|
||||
loadDouble(src, dest.fpu());
|
||||
canonicalizeDouble(dest.fpu());
|
||||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
loadDouble(src, dest.fpu());
|
||||
canonicalizeDouble(dest.fpu());
|
||||
break;
|
||||
default:
|
||||
@ -395,6 +436,11 @@ MacroAssembler::loadFromTypedArray(int arrayType, const T &src, const ValueOpera
|
||||
}
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
|
||||
if (LIRGenerator::allowFloat32Optimizations())
|
||||
convertFloatToDouble(ScratchFloatReg, ScratchFloatReg);
|
||||
boxDouble(ScratchFloatReg, dest);
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), NULL);
|
||||
boxDouble(ScratchFloatReg, dest);
|
||||
|
@ -430,7 +430,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
void Push(TypedOrValueRegister v) {
|
||||
if (v.hasValue())
|
||||
Push(v.valueReg());
|
||||
else if (v.type() == MIRType_Double)
|
||||
else if (IsFloatingPointType(v.type()))
|
||||
Push(v.typedReg().fpu());
|
||||
else
|
||||
Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
|
||||
@ -561,6 +561,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
bind(¬NaN);
|
||||
}
|
||||
|
||||
void canonicalizeFloat(FloatRegister reg) {
|
||||
static float js_NaN_float = js_NaN;
|
||||
Label notNaN;
|
||||
branchFloat(DoubleOrdered, reg, reg, ¬NaN);
|
||||
loadStaticFloat32(&js_NaN_float, reg);
|
||||
bind(¬NaN);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);
|
||||
|
||||
@ -589,24 +597,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void storeToTypedFloatArray(int arrayType, FloatRegister value, const T &dest) {
|
||||
#ifdef JS_MORE_DETERMINISTIC
|
||||
// See the comment in ToDoubleForTypedArray.
|
||||
canonicalizeDouble(value);
|
||||
#endif
|
||||
switch (arrayType) {
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
convertDoubleToFloat(value, ScratchFloatReg);
|
||||
storeFloat(ScratchFloatReg, dest);
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
storeDouble(value, dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest);
|
||||
void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest);
|
||||
|
||||
Register extractString(const Address &address, Register scratch) {
|
||||
return extractObject(address, scratch);
|
||||
|
@ -83,6 +83,7 @@ enum MIRType
|
||||
MIRType_Boolean,
|
||||
MIRType_Int32,
|
||||
MIRType_Double,
|
||||
MIRType_Float32,
|
||||
MIRType_String,
|
||||
MIRType_Object,
|
||||
MIRType_Magic,
|
||||
@ -134,6 +135,7 @@ ValueTypeFromMIRType(MIRType type)
|
||||
return JSVAL_TYPE_BOOLEAN;
|
||||
case MIRType_Int32:
|
||||
return JSVAL_TYPE_INT32;
|
||||
case MIRType_Float32: // Fall through, there's no JSVAL for Float32
|
||||
case MIRType_Double:
|
||||
return JSVAL_TYPE_DOUBLE;
|
||||
case MIRType_String:
|
||||
@ -166,6 +168,8 @@ StringFromMIRType(MIRType type)
|
||||
return "Int32";
|
||||
case MIRType_Double:
|
||||
return "Double";
|
||||
case MIRType_Float32:
|
||||
return "Float32";
|
||||
case MIRType_String:
|
||||
return "String";
|
||||
case MIRType_Object:
|
||||
@ -192,7 +196,19 @@ StringFromMIRType(MIRType type)
|
||||
static inline bool
|
||||
IsNumberType(MIRType type)
|
||||
{
|
||||
return type == MIRType_Int32 || type == MIRType_Double;
|
||||
return type == MIRType_Int32 || type == MIRType_Double || type == MIRType_Float32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsFloatType(MIRType type)
|
||||
{
|
||||
return type == MIRType_Int32 || type == MIRType_Float32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsFloatingPointType(MIRType type)
|
||||
{
|
||||
return type == MIRType_Double || type == MIRType_Float32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
|
@ -248,7 +248,7 @@ Loop::requiresHoistedUse(const MDefinition *ins) const
|
||||
// hoisting on their own, in general. Floating-point constants typically
|
||||
// are worth hoisting, unless they'll end up being spilled (eg. due to a
|
||||
// call).
|
||||
if (ins->isConstant() && (ins->type() != MIRType_Double || containsPossibleCall_))
|
||||
if (ins->isConstant() && (IsFloatingPointType(ins->type()) || containsPossibleCall_))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
@ -188,6 +188,22 @@ class LDouble : public LInstructionHelper<1, 0, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Constant float32.
|
||||
class LFloat32 : public LInstructionHelper<1, 0, 0>
|
||||
{
|
||||
float f_;
|
||||
public:
|
||||
LIR_HEADER(Float32);
|
||||
|
||||
LFloat32(float f)
|
||||
: f_(f)
|
||||
{ }
|
||||
|
||||
float getFloat() const {
|
||||
return f_;
|
||||
}
|
||||
};
|
||||
|
||||
// A constant Value.
|
||||
class LValue : public LInstructionHelper<BOX_PIECES, 0, 0>
|
||||
{
|
||||
@ -2102,6 +2118,16 @@ class LNegD : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Negative of a float32.
|
||||
class LNegF : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(NegF)
|
||||
LNegF(const LAllocation &num) {
|
||||
setOperand(0, num);
|
||||
}
|
||||
};
|
||||
|
||||
// Absolute value of an integer.
|
||||
class LAbsI : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
@ -2302,6 +2328,23 @@ class LMathD : public LBinaryMath<0>
|
||||
}
|
||||
};
|
||||
|
||||
// Performs an add, sub, mul, or div on two double values.
|
||||
class LMathF: public LBinaryMath<0>
|
||||
{
|
||||
JSOp jsop_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(MathF)
|
||||
|
||||
LMathF(JSOp jsop)
|
||||
: jsop_(jsop)
|
||||
{ }
|
||||
|
||||
JSOp jsop() const {
|
||||
return jsop_;
|
||||
}
|
||||
};
|
||||
|
||||
class LModD : public LBinaryMath<1>
|
||||
{
|
||||
public:
|
||||
@ -2458,6 +2501,39 @@ class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a 32-bit float to a double.
|
||||
class LFloat32ToDouble : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Float32ToDouble)
|
||||
|
||||
LFloat32ToDouble(const LAllocation &input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a double to a 32-bit float.
|
||||
class LDoubleToFloat32 : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(DoubleToFloat32)
|
||||
|
||||
LDoubleToFloat32(const LAllocation &input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a 32-bit integer to a float32.
|
||||
class LInt32ToFloat32 : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Int32ToFloat32)
|
||||
|
||||
LInt32ToFloat32(const LAllocation &input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a value to a double.
|
||||
class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
|
||||
{
|
||||
@ -2470,6 +2546,18 @@ class LValueToDouble : public LInstructionHelper<1, BOX_PIECES, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a value to a float32.
|
||||
class LValueToFloat32 : public LInstructionHelper<1, BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(ValueToFloat32)
|
||||
static const size_t Input = 0;
|
||||
|
||||
MToFloat32 *mir() {
|
||||
return mir_->toToFloat32();
|
||||
}
|
||||
};
|
||||
|
||||
// Convert a value to an int32.
|
||||
// Input: components of a Value
|
||||
// Output: 32-bit integer
|
||||
@ -5004,6 +5092,32 @@ class LAssertRangeD : public LInstructionHelper<0, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
class LAssertRangeF : public LInstructionHelper<0, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(AssertRangeF)
|
||||
|
||||
LAssertRangeF(const LAllocation &input, const LDefinition &temp) {
|
||||
setOperand(0, input);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
|
||||
const LAllocation *input() {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
|
||||
MAssertRange *mir() {
|
||||
return mir_->toAssertRange();
|
||||
}
|
||||
Range *range() {
|
||||
return mir()->range();
|
||||
}
|
||||
};
|
||||
|
||||
class LAssertRangeV : public LInstructionHelper<0, BOX_PIECES, 3>
|
||||
{
|
||||
public:
|
||||
|
@ -540,6 +540,7 @@ class LDefinition
|
||||
case MIRType_Object:
|
||||
return LDefinition::OBJECT;
|
||||
case MIRType_Double:
|
||||
case MIRType_Float32:
|
||||
return LDefinition::DOUBLE;
|
||||
#if defined(JS_PUNBOX64)
|
||||
case MIRType_Value:
|
||||
|
@ -15,6 +15,7 @@
|
||||
_(Integer) \
|
||||
_(Pointer) \
|
||||
_(Double) \
|
||||
_(Float32) \
|
||||
_(Value) \
|
||||
_(Parameter) \
|
||||
_(Callee) \
|
||||
@ -92,6 +93,7 @@
|
||||
_(MinMaxD) \
|
||||
_(NegI) \
|
||||
_(NegD) \
|
||||
_(NegF) \
|
||||
_(AbsI) \
|
||||
_(AbsD) \
|
||||
_(SqrtD) \
|
||||
@ -108,6 +110,7 @@
|
||||
_(SubI) \
|
||||
_(MulI) \
|
||||
_(MathD) \
|
||||
_(MathF) \
|
||||
_(ModD) \
|
||||
_(BinaryV) \
|
||||
_(Concat) \
|
||||
@ -115,8 +118,12 @@
|
||||
_(CharCodeAt) \
|
||||
_(FromCharCode) \
|
||||
_(Int32ToDouble) \
|
||||
_(Float32ToDouble) \
|
||||
_(DoubleToFloat32) \
|
||||
_(Int32ToFloat32) \
|
||||
_(ValueToDouble) \
|
||||
_(ValueToInt32) \
|
||||
_(ValueToFloat32) \
|
||||
_(DoubleToInt32) \
|
||||
_(TruncateDToInt32) \
|
||||
_(IntToString) \
|
||||
@ -249,6 +256,7 @@
|
||||
_(CheckInterruptPar) \
|
||||
_(AssertRangeI) \
|
||||
_(AssertRangeD) \
|
||||
_(AssertRangeF) \
|
||||
_(AssertRangeV)
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
|
@ -1241,6 +1241,12 @@ LIRGenerator::visitAdd(MAdd *ins)
|
||||
return lowerForFPU(new LMathD(JSOP_ADD), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
return lowerForFPU(new LMathF(JSOP_ADD), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_ADD, ins);
|
||||
}
|
||||
|
||||
@ -1269,6 +1275,10 @@ LIRGenerator::visitSub(MSub *ins)
|
||||
JS_ASSERT(lhs->type() == MIRType_Double);
|
||||
return lowerForFPU(new LMathD(JSOP_SUB), ins, lhs, rhs);
|
||||
}
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
return lowerForFPU(new LMathF(JSOP_SUB), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_SUB, ins);
|
||||
}
|
||||
@ -1295,6 +1305,18 @@ LIRGenerator::visitMul(MMul *ins)
|
||||
|
||||
return lowerForFPU(new LMathD(JSOP_MUL), ins, lhs, rhs);
|
||||
}
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
ReorderCommutative(&lhs, &rhs);
|
||||
|
||||
// We apply the same optimizations as for doubles
|
||||
if (lhs->isConstant() && lhs->toConstant()->value() == JS::Float32Value(-1.0f))
|
||||
return defineReuseInput(new LNegF(useRegisterAtStart(rhs)), ins, 0);
|
||||
if (rhs->isConstant() && rhs->toConstant()->value() == JS::Float32Value(-1.0f))
|
||||
return defineReuseInput(new LNegF(useRegisterAtStart(lhs)), ins, 0);
|
||||
|
||||
return lowerForFPU(new LMathF(JSOP_MUL), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_MUL, ins);
|
||||
}
|
||||
@ -1314,6 +1336,10 @@ LIRGenerator::visitDiv(MDiv *ins)
|
||||
JS_ASSERT(lhs->type() == MIRType_Double);
|
||||
return lowerForFPU(new LMathD(JSOP_DIV), ins, lhs, rhs);
|
||||
}
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
JS_ASSERT(lhs->type() == MIRType_Float32);
|
||||
return lowerForFPU(new LMathF(JSOP_DIV), ins, lhs, rhs);
|
||||
}
|
||||
|
||||
return lowerBinaryV(JSOP_DIV, ins);
|
||||
}
|
||||
@ -1506,6 +1532,12 @@ LIRGenerator::visitToDouble(MToDouble *convert)
|
||||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Float32:
|
||||
{
|
||||
LFloat32ToDouble *lir = new LFloat32ToDouble(useRegister(opd));
|
||||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Double:
|
||||
return redefine(convert, opd);
|
||||
|
||||
@ -1516,6 +1548,56 @@ LIRGenerator::visitToDouble(MToDouble *convert)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitToFloat32(MToFloat32 *convert)
|
||||
{
|
||||
MDefinition *opd = convert->input();
|
||||
mozilla::DebugOnly<MToFloat32::ConversionKind> conversion = convert->conversion();
|
||||
|
||||
switch (opd->type()) {
|
||||
case MIRType_Value:
|
||||
{
|
||||
LValueToFloat32 *lir = new LValueToFloat32();
|
||||
if (!useBox(lir, LValueToFloat32::Input, opd))
|
||||
return false;
|
||||
return assignSnapshot(lir) && define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Null:
|
||||
JS_ASSERT(conversion != MToFloat32::NonStringPrimitives);
|
||||
return lowerConstantFloat32(0, convert);
|
||||
|
||||
case MIRType_Undefined:
|
||||
JS_ASSERT(conversion != MToFloat32::NumbersOnly);
|
||||
return lowerConstantFloat32(js_NaN, convert);
|
||||
|
||||
case MIRType_Boolean:
|
||||
JS_ASSERT(conversion != MToFloat32::NumbersOnly);
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case MIRType_Int32:
|
||||
{
|
||||
LInt32ToFloat32 *lir = new LInt32ToFloat32(useRegister(opd));
|
||||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Double:
|
||||
{
|
||||
LDoubleToFloat32 *lir = new LDoubleToFloat32(useRegister(opd));
|
||||
return define(lir, convert);
|
||||
}
|
||||
|
||||
case MIRType_Float32:
|
||||
return redefine(convert, opd);
|
||||
|
||||
default:
|
||||
// Objects might be effectful.
|
||||
// Strings are complicated - we don't handle them yet.
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitToInt32(MToInt32 *convert)
|
||||
{
|
||||
@ -1822,6 +1904,9 @@ LIRGenerator::visitStoreSlot(MStoreSlot *ins)
|
||||
case MIRType_Double:
|
||||
return add(new LStoreSlotT(useRegister(ins->slots()), useRegister(ins->value())), ins);
|
||||
|
||||
case MIRType_Float32:
|
||||
MOZ_ASSUME_UNREACHABLE("Float32 shouldn't be stored in a slot.");
|
||||
|
||||
default:
|
||||
return add(new LStoreSlotT(useRegister(ins->slots()), useRegisterOrConstant(ins->value())),
|
||||
ins);
|
||||
@ -2252,7 +2337,7 @@ LIRGenerator::visitLoadTypedArrayElement(MLoadTypedArrayElement *ins)
|
||||
|
||||
// We need a temp register for Uint32Array with known double result.
|
||||
LDefinition tempDef = LDefinition::BogusTemp();
|
||||
if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && ins->type() == MIRType_Double)
|
||||
if (ins->arrayType() == ScalarTypeRepresentation::TYPE_UINT32 && IsFloatingPointType(ins->type()))
|
||||
tempDef = temp();
|
||||
|
||||
LLoadTypedArrayElement *lir = new LLoadTypedArrayElement(elements, index, tempDef);
|
||||
@ -2500,6 +2585,10 @@ LIRGenerator::visitAssertRange(MAssertRange *ins)
|
||||
lir = new LAssertRangeD(useRegister(input), tempFloat());
|
||||
break;
|
||||
|
||||
case MIRType_Float32:
|
||||
lir = new LAssertRangeF(useRegister(input), tempFloat());
|
||||
break;
|
||||
|
||||
case MIRType_Value:
|
||||
lir = new LAssertRangeV(tempToUnbox(), tempFloat(), tempFloat());
|
||||
if (!useBox(lir, LAssertRangeV::Input, input))
|
||||
|
@ -153,6 +153,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
bool visitOsrValue(MOsrValue *value);
|
||||
bool visitOsrScopeChain(MOsrScopeChain *object);
|
||||
bool visitToDouble(MToDouble *convert);
|
||||
bool visitToFloat32(MToFloat32 *convert);
|
||||
bool visitToInt32(MToInt32 *convert);
|
||||
bool visitTruncateToInt32(MTruncateToInt32 *truncate);
|
||||
bool visitToString(MToString *convert);
|
||||
|
@ -792,9 +792,9 @@ IonBuilder::inlineMathImul(CallInfo &callInfo)
|
||||
if (returnType != MIRType_Int32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
if (!IsNumberType(callInfo.getArg(0)->type()))
|
||||
if (!IsNumberType(callInfo.getArg(0)->type()) || callInfo.getArg(0)->type() == MIRType_Float32)
|
||||
return InliningStatus_NotInlined;
|
||||
if (!IsNumberType(callInfo.getArg(1)->type()))
|
||||
if (!IsNumberType(callInfo.getArg(1)->type()) || callInfo.getArg(1)->type() == MIRType_Float32)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
callInfo.unwrapArgs();
|
||||
|
@ -452,6 +452,12 @@ MConstant::printOpcode(FILE *fp) const
|
||||
case MIRType_Double:
|
||||
fprintf(fp, "%f", value().toDouble());
|
||||
break;
|
||||
case MIRType_Float32:
|
||||
{
|
||||
float val = value().toDouble();
|
||||
fprintf(fp, "%f", val);
|
||||
break;
|
||||
}
|
||||
case MIRType_Object:
|
||||
if (value().toObject().is<JSFunction>()) {
|
||||
JSFunction *fun = &value().toObject().as<JSFunction>();
|
||||
@ -483,6 +489,28 @@ MConstant::printOpcode(FILE *fp) const
|
||||
}
|
||||
}
|
||||
|
||||
// Needs a static function to avoid overzealous optimizations by certain compilers (MSVC).
|
||||
static bool
|
||||
IsFloat32Representable(double x)
|
||||
{
|
||||
float asFloat = static_cast<float>(x);
|
||||
double floatAsDouble = static_cast<double>(asFloat);
|
||||
return floatAsDouble == x;
|
||||
}
|
||||
|
||||
bool
|
||||
MConstant::canProduceFloat32() const
|
||||
{
|
||||
if (!IsNumberType(type()))
|
||||
return false;
|
||||
|
||||
if (type() == MIRType_Int32)
|
||||
return IsFloat32Representable(static_cast<double>(value_.toInt32()));
|
||||
if (type() == MIRType_Double)
|
||||
return IsFloat32Representable(value_.toDouble());
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MControlInstruction::printOpcode(FILE *fp) const
|
||||
{
|
||||
@ -746,7 +774,9 @@ jit::MergeTypes(MIRType *ptype, types::StackTypeSet **ptypeSet,
|
||||
if (newTypeSet && newTypeSet->empty())
|
||||
return;
|
||||
if (newType != *ptype) {
|
||||
if (IsNumberType(newType) && IsNumberType(*ptype)) {
|
||||
if (IsFloatType(newType) && IsFloatType(*ptype)) {
|
||||
*ptype = MIRType_Float32;
|
||||
} else if (IsNumberType(newType) && IsNumberType(*ptype)) {
|
||||
*ptype = MIRType_Double;
|
||||
} else if (*ptype != MIRType_Value) {
|
||||
if (!*ptypeSet)
|
||||
@ -1170,6 +1200,43 @@ MBinaryArithInstruction::foldsTo(bool useValueNumbers)
|
||||
return this;
|
||||
}
|
||||
|
||||
template<size_t Op> static void
|
||||
ConvertDefinitionToDouble(MDefinition *def, MInstruction *consumer)
|
||||
{
|
||||
MInstruction *replace = MToDouble::New(def);
|
||||
consumer->replaceOperand(Op, replace);
|
||||
consumer->block()->insertBefore(consumer, replace);
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckUsesAreFloat32Consumers(MInstruction *ins)
|
||||
{
|
||||
bool allConsumerUses = true;
|
||||
for (MUseDefIterator use(ins); allConsumerUses && use; use++)
|
||||
allConsumerUses &= use.def()->canConsumeFloat32();
|
||||
return allConsumerUses;
|
||||
}
|
||||
|
||||
void
|
||||
MBinaryArithInstruction::trySpecializeFloat32()
|
||||
{
|
||||
MDefinition *left = lhs();
|
||||
MDefinition *right = rhs();
|
||||
|
||||
if (!left->canProduceFloat32() || !right->canProduceFloat32()
|
||||
|| !CheckUsesAreFloat32Consumers(this))
|
||||
{
|
||||
if (left->type() == MIRType_Float32)
|
||||
ConvertDefinitionToDouble<0>(left, this);
|
||||
if (right->type() == MIRType_Float32)
|
||||
ConvertDefinitionToDouble<1>(right, this);
|
||||
return;
|
||||
}
|
||||
|
||||
specialization_ = MIRType_Float32;
|
||||
setResultType(MIRType_Float32);
|
||||
}
|
||||
|
||||
bool
|
||||
MAbs::fallible() const
|
||||
{
|
||||
@ -1408,7 +1475,9 @@ MBinaryArithInstruction::infer(BaselineInspector *inspector,
|
||||
// Don't specialize for neither-integer-nor-double results.
|
||||
if (lhs == MIRType_Int32 && rhs == MIRType_Int32)
|
||||
setResultType(MIRType_Int32);
|
||||
else if (lhs == MIRType_Double || rhs == MIRType_Double)
|
||||
// Double operations are prioritary over float32 operations (i.e. if any operand needs
|
||||
// a double as an input, convert all operands to doubles)
|
||||
else if (IsFloatingPointType(lhs) || IsFloatingPointType(rhs))
|
||||
setResultType(MIRType_Double);
|
||||
else
|
||||
return inferFallback(inspector, pc);
|
||||
@ -1433,7 +1502,7 @@ MBinaryArithInstruction::infer(BaselineInspector *inspector,
|
||||
|
||||
// Don't specialize values when result isn't double
|
||||
if (lhs == MIRType_Value || rhs == MIRType_Value) {
|
||||
if (rval != MIRType_Double) {
|
||||
if (!IsFloatingPointType(rval)) {
|
||||
specialization_ = MIRType_None;
|
||||
return;
|
||||
}
|
||||
@ -1988,6 +2057,28 @@ MToDouble::foldsTo(bool useValueNumbers)
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
MToFloat32::foldsTo(bool useValueNumbers)
|
||||
{
|
||||
if (input()->type() == MIRType_Float32)
|
||||
return input();
|
||||
|
||||
// If x is a Float32, Float32(Double(x)) == x
|
||||
if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32)
|
||||
return input()->toToDouble()->input();
|
||||
|
||||
if (input()->isConstant()) {
|
||||
const Value &v = input()->toConstant()->value();
|
||||
if (v.isNumber()) {
|
||||
float out = v.toNumber();
|
||||
MConstant *c = MConstant::New(DoubleValue(out));
|
||||
c->setResultType(MIRType_Float32);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
MToString::foldsTo(bool useValueNumbers)
|
||||
{
|
||||
|
139
js/src/jit/MIR.h
139
js/src/jit/MIR.h
@ -453,6 +453,13 @@ class MDefinition : public MNode
|
||||
return !resultTypeSet() || resultTypeSet()->mightBeType(ValueTypeFromMIRType(type));
|
||||
}
|
||||
|
||||
// Float32 specialization operations (see big comment in IonAnalysis before the Float32
|
||||
// specialization algorithm).
|
||||
virtual bool isFloat32Commutative() const { return false; }
|
||||
virtual bool canProduceFloat32() const { return false; }
|
||||
virtual bool canConsumeFloat32() const { return false; }
|
||||
virtual void trySpecializeFloat32() {}
|
||||
|
||||
// Returns the beginning of this definition's use chain.
|
||||
MUseIterator usesBegin() const {
|
||||
return uses_.begin();
|
||||
@ -948,8 +955,21 @@ class MConstant : public MNullaryInstruction
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
bool updateForReplacement(MDefinition *def) {
|
||||
MConstant *c = def->toConstant();
|
||||
// During constant folding, we don't want to replace a float32
|
||||
// value by a double value.
|
||||
if (type() == MIRType_Float32)
|
||||
return c->type() == MIRType_Float32;
|
||||
if (type() == MIRType_Double)
|
||||
return c->type() != MIRType_Float32;
|
||||
return true;
|
||||
}
|
||||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
|
||||
bool canProduceFloat32() const;
|
||||
};
|
||||
|
||||
class MParameter : public MNullaryInstruction
|
||||
@ -1723,6 +1743,10 @@ class MCall
|
||||
return getOperand(NumNonArgumentOperands + index);
|
||||
}
|
||||
|
||||
void replaceArg(uint32_t index, MDefinition *def) {
|
||||
replaceOperand(NumNonArgumentOperands + index, def);
|
||||
}
|
||||
|
||||
void rootTargetScript(JSFunction *target) {
|
||||
targetScript_.setRoot(target->nonLazyScript());
|
||||
}
|
||||
@ -2641,6 +2665,65 @@ class MToDouble
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
};
|
||||
|
||||
// Converts a primitive (either typed or untyped) to a float32. If the input is
|
||||
// not primitive at runtime, a bailout occurs.
|
||||
class MToFloat32
|
||||
: public MUnaryInstruction,
|
||||
public ToDoublePolicy
|
||||
{
|
||||
public:
|
||||
// Types of values which can be converted.
|
||||
enum ConversionKind {
|
||||
NonStringPrimitives,
|
||||
NonNullNonStringPrimitives,
|
||||
NumbersOnly
|
||||
};
|
||||
|
||||
protected:
|
||||
ConversionKind conversion_;
|
||||
|
||||
MToFloat32(MDefinition *def, ConversionKind conversion)
|
||||
: MUnaryInstruction(def), conversion_(conversion)
|
||||
{
|
||||
setResultType(MIRType_Float32);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(ToFloat32)
|
||||
static MToFloat32 *New(MDefinition *def, ConversionKind conversion = NonStringPrimitives) {
|
||||
return new MToFloat32(def, conversion);
|
||||
}
|
||||
static MToFloat32 *NewAsmJS(MDefinition *def) {
|
||||
return new MToFloat32(def, NonStringPrimitives);
|
||||
}
|
||||
|
||||
ConversionKind conversion() const {
|
||||
return conversion_;
|
||||
}
|
||||
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual MDefinition *foldsTo(bool useValueNumbers);
|
||||
bool congruentTo(MDefinition *ins) const {
|
||||
if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return true; }
|
||||
bool canProduceFloat32() const { return true; }
|
||||
};
|
||||
|
||||
// Converts a uint32 to a double (coming from asm.js).
|
||||
class MAsmJSUnsignedToDouble
|
||||
: public MUnaryInstruction
|
||||
@ -3106,6 +3189,8 @@ class MBinaryArithInstruction
|
||||
setResultType(MIRType_Int32);
|
||||
}
|
||||
|
||||
virtual void trySpecializeFloat32();
|
||||
|
||||
bool congruentTo(MDefinition *ins) const {
|
||||
return MBinaryInstruction::congruentTo(ins);
|
||||
}
|
||||
@ -3481,6 +3566,8 @@ class MAdd : public MBinaryArithInstruction
|
||||
return add;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
double getIdentity() {
|
||||
return 0;
|
||||
}
|
||||
@ -3517,6 +3604,8 @@ class MSub : public MBinaryArithInstruction
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
bool fallible();
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
@ -3589,6 +3678,8 @@ class MMul : public MBinaryArithInstruction
|
||||
return canBeNegativeZero_ || canOverflow();
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
@ -3657,6 +3748,8 @@ class MDiv : public MBinaryArithInstruction
|
||||
return unsigned_;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
|
||||
bool fallible();
|
||||
bool truncate();
|
||||
};
|
||||
@ -3844,6 +3937,8 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
||||
bool hasBackedgeType_;
|
||||
bool triedToSpecialize_;
|
||||
bool isIterator_;
|
||||
bool canProduceFloat32_;
|
||||
bool canConsumeFloat32_;
|
||||
|
||||
#if DEBUG
|
||||
bool specialized_;
|
||||
@ -3854,7 +3949,9 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
||||
: slot_(slot),
|
||||
hasBackedgeType_(false),
|
||||
triedToSpecialize_(false),
|
||||
isIterator_(false)
|
||||
isIterator_(false),
|
||||
canProduceFloat32_(false),
|
||||
canConsumeFloat32_(false)
|
||||
#if DEBUG
|
||||
, specialized_(false)
|
||||
, capacity_(0)
|
||||
@ -3952,6 +4049,22 @@ class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
bool canProduceFloat32() const {
|
||||
return canProduceFloat32_;
|
||||
}
|
||||
|
||||
void setCanProduceFloat32(bool can) {
|
||||
canProduceFloat32_ = can;
|
||||
}
|
||||
|
||||
bool canConsumeFloat32() const {
|
||||
return canConsumeFloat32_;
|
||||
}
|
||||
|
||||
void setCanConsumeFloat32(bool can) {
|
||||
canConsumeFloat32_ = can;
|
||||
}
|
||||
};
|
||||
|
||||
// The goal of a Beta node is to split a def at a conditionally taken
|
||||
@ -4956,7 +5069,7 @@ class MStoreElementCommon
|
||||
class MStoreElement
|
||||
: public MAryInstruction<3>,
|
||||
public MStoreElementCommon,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<2> >
|
||||
{
|
||||
bool needsHoleCheck_;
|
||||
|
||||
@ -5006,7 +5119,7 @@ class MStoreElement
|
||||
class MStoreElementHole
|
||||
: public MAryInstruction<4>,
|
||||
public MStoreElementCommon,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3> >
|
||||
{
|
||||
MStoreElementHole(MDefinition *object, MDefinition *elements,
|
||||
MDefinition *index, MDefinition *value) {
|
||||
@ -5206,6 +5319,8 @@ class MLoadTypedArrayElement
|
||||
}
|
||||
|
||||
void computeRange();
|
||||
|
||||
bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Load a value from a typed array. Out-of-bounds accesses are handled using
|
||||
@ -5254,6 +5369,7 @@ class MLoadTypedArrayElementHole
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::Load(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool canProduceFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Load a value fallibly or infallibly from a statically known typed array.
|
||||
@ -5265,7 +5381,9 @@ class MLoadTypedArrayElementStatic
|
||||
: MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true)
|
||||
{
|
||||
int type = typedArray_->type();
|
||||
if (type == ScalarTypeRepresentation::TYPE_FLOAT32 || type == ScalarTypeRepresentation::TYPE_FLOAT64)
|
||||
if (type == ScalarTypeRepresentation::TYPE_FLOAT32)
|
||||
setResultType(MIRType_Float32);
|
||||
else if (type == ScalarTypeRepresentation::TYPE_FLOAT64)
|
||||
setResultType(MIRType_Double);
|
||||
else
|
||||
setResultType(MIRType_Int32);
|
||||
@ -5304,6 +5422,7 @@ class MLoadTypedArrayElementStatic
|
||||
|
||||
void computeRange();
|
||||
bool truncate();
|
||||
bool canProduceFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
class MStoreTypedArrayElement
|
||||
@ -5367,6 +5486,8 @@ class MStoreTypedArrayElement
|
||||
racy_ = true;
|
||||
}
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
class MStoreTypedArrayElementHole
|
||||
@ -5430,6 +5551,8 @@ class MStoreTypedArrayElementHole
|
||||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return arrayType_ == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Store a value infallibly to a statically known typed array.
|
||||
@ -5471,6 +5594,8 @@ class MStoreTypedArrayElementStatic :
|
||||
return AliasSet::Store(AliasSet::TypedArrayElement);
|
||||
}
|
||||
bool isOperandTruncated(size_t index) const;
|
||||
|
||||
bool canConsumeFloat32() const { return typedArray_->type() == ScalarTypeRepresentation::TYPE_FLOAT32; }
|
||||
};
|
||||
|
||||
// Compute an "effective address", i.e., a compound computation of the form:
|
||||
@ -5590,7 +5715,7 @@ class MLoadFixedSlot
|
||||
|
||||
class MStoreFixedSlot
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
|
||||
{
|
||||
bool needsBarrier_;
|
||||
size_t slot_;
|
||||
@ -6411,7 +6536,7 @@ class MForkJoinSlice
|
||||
// Store to vp[slot] (slots that are not inline in an object).
|
||||
class MStoreSlot
|
||||
: public MBinaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >
|
||||
{
|
||||
uint32_t slot_;
|
||||
MIRType slotType_;
|
||||
@ -6711,7 +6836,7 @@ class MCallSetProperty
|
||||
|
||||
class MSetPropertyCache
|
||||
: public MSetPropertyInstruction,
|
||||
public SingleObjectPolicy
|
||||
public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >
|
||||
{
|
||||
bool needsTypeBarrier_;
|
||||
|
||||
|
@ -76,6 +76,7 @@ namespace jit {
|
||||
_(GuardString) \
|
||||
_(AssertRange) \
|
||||
_(ToDouble) \
|
||||
_(ToFloat32) \
|
||||
_(ToInt32) \
|
||||
_(TruncateToInt32) \
|
||||
_(ToString) \
|
||||
|
@ -163,6 +163,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
||||
SAFE_OP(Unbox)
|
||||
SAFE_OP(GuardObject)
|
||||
SAFE_OP(ToDouble)
|
||||
SAFE_OP(ToFloat32)
|
||||
SAFE_OP(ToInt32)
|
||||
SAFE_OP(TruncateToInt32)
|
||||
SAFE_OP(MaybeToDoubleElement)
|
||||
|
@ -1105,6 +1105,12 @@ MToDouble::computeRange()
|
||||
setRange(new Range(getOperand(0)));
|
||||
}
|
||||
|
||||
void
|
||||
MToFloat32::computeRange()
|
||||
{
|
||||
setRange(new Range(getOperand(0)));
|
||||
}
|
||||
|
||||
void
|
||||
MTruncateToInt32::computeRange()
|
||||
{
|
||||
@ -1829,6 +1835,20 @@ MToDouble::truncate()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MToFloat32::truncate()
|
||||
{
|
||||
JS_ASSERT(type() == MIRType_Float32);
|
||||
|
||||
// We use the return type to flag that this MToFloat32 sould be replaced by a
|
||||
// MTruncateToInt32 when modifying the graph.
|
||||
setResultType(MIRType_Int32);
|
||||
if (range())
|
||||
range()->wrapAroundToInt32();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MLoadTypedArrayElementStatic::truncate()
|
||||
{
|
||||
@ -1880,6 +1900,14 @@ MToDouble::isOperandTruncated(size_t index) const
|
||||
return type() == MIRType_Int32;
|
||||
}
|
||||
|
||||
bool
|
||||
MToFloat32::isOperandTruncated(size_t index) const
|
||||
{
|
||||
// The return type is used to flag that we are replacing this Float32 by a
|
||||
// Truncate of its operand if needed.
|
||||
return type() == MIRType_Int32;
|
||||
}
|
||||
|
||||
bool
|
||||
MStoreTypedArrayElement::isOperandTruncated(size_t index) const
|
||||
{
|
||||
|
@ -57,6 +57,8 @@ class SnapshotReader
|
||||
{
|
||||
CONSTANT, // An index into the constant pool.
|
||||
DOUBLE_REG, // Type is double, payload is in a register.
|
||||
FLOAT32_REG, // Type is float32, payload is in a register.
|
||||
FLOAT32_STACK, // Type is float32, payload is on the stack.
|
||||
TYPED_REG, // Type is constant, payload is in a register.
|
||||
TYPED_STACK, // Type is constant, payload is on the stack.
|
||||
UNTYPED, // Type is not known.
|
||||
@ -135,6 +137,18 @@ class SnapshotReader
|
||||
{
|
||||
fpu_ = reg.code();
|
||||
}
|
||||
Slot(SlotMode mode, const FloatRegister ®)
|
||||
: mode_(mode)
|
||||
{
|
||||
JS_ASSERT(mode == FLOAT32_REG);
|
||||
fpu_ = reg.code();
|
||||
}
|
||||
Slot(SlotMode mode, const Location &loc)
|
||||
: mode_(mode)
|
||||
{
|
||||
JS_ASSERT(mode == FLOAT32_STACK);
|
||||
known_type_.payload = loc;
|
||||
}
|
||||
Slot(SlotMode mode)
|
||||
: mode_(mode)
|
||||
{ }
|
||||
@ -166,11 +180,11 @@ class SnapshotReader
|
||||
return known_type_.payload.reg();
|
||||
}
|
||||
FloatRegister floatReg() const {
|
||||
JS_ASSERT(mode() == DOUBLE_REG);
|
||||
JS_ASSERT(mode() == DOUBLE_REG || mode() == FLOAT32_REG);
|
||||
return FloatRegister::FromCode(fpu_);
|
||||
}
|
||||
int32_t stackSlot() const {
|
||||
JS_ASSERT(mode() == TYPED_STACK);
|
||||
JS_ASSERT(mode() == TYPED_STACK || mode() == FLOAT32_STACK);
|
||||
return known_type_.payload.stackSlot();
|
||||
}
|
||||
#if defined(JS_NUNBOX32)
|
||||
|
@ -46,6 +46,8 @@ class SnapshotWriter
|
||||
void addUndefinedSlot();
|
||||
void addNullSlot();
|
||||
void addInt32Slot(int32_t value);
|
||||
void addFloat32Slot(const FloatRegister ®);
|
||||
void addFloat32Slot(int32_t stackIndex);
|
||||
void addConstantPoolSlot(uint32_t index);
|
||||
#if defined(JS_NUNBOX32)
|
||||
void addSlot(const Register &type, const Register &payload);
|
||||
|
@ -56,13 +56,16 @@ using namespace js::jit;
|
||||
//
|
||||
// JSVAL_TYPE_NULL:
|
||||
// Reg value:
|
||||
// 0-29: Constant integer; Int32Value(n)
|
||||
// 0-27: Constant integer; Int32Value(n)
|
||||
// 28: Variable float32; Float register code
|
||||
// 29: Variable float32; Stack index
|
||||
// 30: NullValue()
|
||||
// 31: Constant integer; Int32Value([vws])
|
||||
//
|
||||
// JSVAL_TYPE_UNDEFINED:
|
||||
// Reg value:
|
||||
// 0-29: Constant value, index n into ionScript->constants()
|
||||
// 0-27: Constant value, index n into ionScript->constants()
|
||||
// 28-29: unused
|
||||
// 30: UndefinedValue()
|
||||
// 31: Constant value, index [vwu] into
|
||||
// ionScript->constants()
|
||||
@ -172,10 +175,12 @@ static const uint32_t NUNBOX32_REG_REG = 3;
|
||||
|
||||
static const uint32_t MAX_TYPE_FIELD_VALUE = 7;
|
||||
|
||||
static const uint32_t MAX_REG_FIELD_VALUE = 31;
|
||||
static const uint32_t ESC_REG_FIELD_INDEX = 31;
|
||||
static const uint32_t ESC_REG_FIELD_CONST = 30;
|
||||
static const uint32_t MIN_REG_FIELD_ESC = 30;
|
||||
static const uint32_t MAX_REG_FIELD_VALUE = 31;
|
||||
static const uint32_t ESC_REG_FIELD_INDEX = 31;
|
||||
static const uint32_t ESC_REG_FIELD_CONST = 30;
|
||||
static const uint32_t ESC_REG_FIELD_FLOAT32_STACK = 29;
|
||||
static const uint32_t ESC_REG_FIELD_FLOAT32_REG = 28;
|
||||
static const uint32_t MIN_REG_FIELD_ESC = 28;
|
||||
|
||||
SnapshotReader::Slot
|
||||
SnapshotReader::readSlot()
|
||||
@ -210,6 +215,10 @@ SnapshotReader::readSlot()
|
||||
return Slot(JS_NULL);
|
||||
if (code == ESC_REG_FIELD_INDEX)
|
||||
return Slot(JS_INT32, reader_.readSigned());
|
||||
if (code == ESC_REG_FIELD_FLOAT32_REG)
|
||||
return Slot(FLOAT32_REG, FloatRegister::FromCode(reader_.readUnsigned()));
|
||||
if (code == ESC_REG_FIELD_FLOAT32_STACK)
|
||||
return Slot(FLOAT32_STACK, Location::From(reader_.readSigned()));
|
||||
return Slot(JS_INT32, code);
|
||||
|
||||
case JSVAL_TYPE_UNDEFINED:
|
||||
@ -353,6 +362,7 @@ SnapshotWriter::writeSlotHeader(JSValueType type, uint32_t regCode)
|
||||
void
|
||||
SnapshotWriter::addSlot(const FloatRegister ®)
|
||||
{
|
||||
JS_ASSERT(reg.code() < MIN_REG_FIELD_ESC);
|
||||
IonSpew(IonSpew_Snapshots, " slot %u: double (reg %s)", slotsWritten_, reg.name());
|
||||
|
||||
writeSlotHeader(JSVAL_TYPE_DOUBLE, reg.code());
|
||||
@ -506,6 +516,23 @@ SnapshotWriter::addInt32Slot(int32_t value)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SnapshotWriter::addFloat32Slot(const FloatRegister ®)
|
||||
{
|
||||
JS_ASSERT(reg.code() < MIN_REG_FIELD_ESC);
|
||||
IonSpew(IonSpew_Snapshots, " slot %u: float32 (reg %s)", slotsWritten_, reg.name());
|
||||
writeSlotHeader(JSVAL_TYPE_NULL, ESC_REG_FIELD_FLOAT32_REG);
|
||||
writer_.writeUnsigned(reg.code());
|
||||
}
|
||||
|
||||
void
|
||||
SnapshotWriter::addFloat32Slot(int32_t stackIndex)
|
||||
{
|
||||
IonSpew(IonSpew_Snapshots, " slot %u: float32 (stack %d)", slotsWritten_, stackIndex);
|
||||
writeSlotHeader(JSVAL_TYPE_NULL, ESC_REG_FIELD_FLOAT32_STACK);
|
||||
writer_.writeSigned(stackIndex);
|
||||
}
|
||||
|
||||
void
|
||||
SnapshotWriter::addConstantPoolSlot(uint32_t index)
|
||||
{
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "jit/TypePolicy.h"
|
||||
|
||||
#include "jit/Lowering.h"
|
||||
#include "jit/MIR.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
@ -17,7 +18,20 @@ BoxInputsPolicy::boxAt(MInstruction *at, MDefinition *operand)
|
||||
{
|
||||
if (operand->isUnbox())
|
||||
return operand->toUnbox()->input();
|
||||
MBox *box = MBox::New(operand);
|
||||
return alwaysBoxAt(at, operand);
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
BoxInputsPolicy::alwaysBoxAt(MInstruction *at, MDefinition *operand)
|
||||
{
|
||||
MDefinition *boxedOperand = operand;
|
||||
// Replace Float32 by double
|
||||
if (operand->type() == MIRType_Float32) {
|
||||
MInstruction *replace = MToDouble::New(operand);
|
||||
operand->block()->insertBefore(at, replace);
|
||||
boxedOperand = replace;
|
||||
}
|
||||
MBox *box = MBox::New(boxedOperand);
|
||||
at->block()->insertBefore(at, box);
|
||||
return box;
|
||||
}
|
||||
@ -40,7 +54,7 @@ ArithPolicy::adjustInputs(MInstruction *ins)
|
||||
if (specialization_ == MIRType_None)
|
||||
return BoxInputsPolicy::adjustInputs(ins);
|
||||
|
||||
JS_ASSERT(ins->type() == MIRType_Double || ins->type() == MIRType_Int32);
|
||||
JS_ASSERT(ins->type() == MIRType_Double || ins->type() == MIRType_Int32 || ins->type() == MIRType_Float32);
|
||||
|
||||
for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
|
||||
MDefinition *in = ins->getOperand(i);
|
||||
@ -59,6 +73,8 @@ ArithPolicy::adjustInputs(MInstruction *ins)
|
||||
|
||||
if (ins->type() == MIRType_Double)
|
||||
replace = MToDouble::New(in);
|
||||
else if (ins->type() == MIRType_Float32)
|
||||
replace = MToFloat32::New(in);
|
||||
else
|
||||
replace = MToInt32::New(in);
|
||||
|
||||
@ -99,6 +115,16 @@ ComparePolicy::adjustInputs(MInstruction *def)
|
||||
JS_ASSERT(def->isCompare());
|
||||
MCompare *compare = def->toCompare();
|
||||
|
||||
// Convert Float32 operands to doubles
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
MDefinition *in = def->getOperand(i);
|
||||
if (in->type() == MIRType_Float32) {
|
||||
MInstruction *replace = MToDouble::New(in);
|
||||
def->block()->insertBefore(def, replace);
|
||||
def->replaceOperand(i, replace);
|
||||
}
|
||||
}
|
||||
|
||||
// Box inputs to get value
|
||||
if (compare->compareType() == MCompare::Compare_Unknown ||
|
||||
compare->compareType() == MCompare::Compare_Value)
|
||||
@ -313,6 +339,13 @@ BitwisePolicy::adjustInputs(MInstruction *ins)
|
||||
if (in->type() == MIRType_Object || in->type() == MIRType_String)
|
||||
in = boxAt(ins, in);
|
||||
|
||||
if (in->type() == MIRType_Float32) {
|
||||
MToDouble *replace = MToDouble::New(in);
|
||||
ins->block()->insertBefore(ins, replace);
|
||||
ins->replaceOperand(i, replace);
|
||||
in = replace;
|
||||
}
|
||||
|
||||
MInstruction *replace = MTruncateToInt32::New(in);
|
||||
ins->block()->insertBefore(ins, replace);
|
||||
ins->replaceOperand(i, replace);
|
||||
@ -410,6 +443,61 @@ DoublePolicy<Op>::staticAdjustInputs(MInstruction *def)
|
||||
template bool DoublePolicy<0>::staticAdjustInputs(MInstruction *def);
|
||||
template bool DoublePolicy<1>::staticAdjustInputs(MInstruction *def);
|
||||
|
||||
template <unsigned Op>
|
||||
bool
|
||||
Float32Policy<Op>::staticAdjustInputs(MInstruction *def)
|
||||
{
|
||||
MDefinition *in = def->getOperand(Op);
|
||||
if (in->type() == MIRType_Float32)
|
||||
return true;
|
||||
|
||||
// Force a bailout. Objects may be effectful; strings are currently unhandled.
|
||||
if (in->type() == MIRType_Object || in->type() == MIRType_String) {
|
||||
MToDouble *toDouble = MToDouble::New(in);
|
||||
def->block()->insertBefore(def, toDouble);
|
||||
|
||||
MBox *box = MBox::New(toDouble);
|
||||
def->block()->insertBefore(def, box);
|
||||
|
||||
MUnbox *unbox = MUnbox::New(box, MIRType_Double, MUnbox::Fallible);
|
||||
def->block()->insertBefore(def, unbox);
|
||||
|
||||
MToFloat32 *toFloat32 = MToFloat32::New(unbox);
|
||||
def->block()->insertBefore(def, toFloat32);
|
||||
|
||||
def->replaceOperand(Op, unbox);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MToFloat32 *replace = MToFloat32::New(in);
|
||||
def->block()->insertBefore(def, replace);
|
||||
def->replaceOperand(Op, replace);
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool Float32Policy<0>::staticAdjustInputs(MInstruction *def);
|
||||
template bool Float32Policy<1>::staticAdjustInputs(MInstruction *def);
|
||||
template bool Float32Policy<2>::staticAdjustInputs(MInstruction *def);
|
||||
|
||||
template <unsigned Op>
|
||||
bool
|
||||
NoFloatPolicy<Op>::staticAdjustInputs(MInstruction *def)
|
||||
{
|
||||
MDefinition *in = def->getOperand(Op);
|
||||
if (in->type() == MIRType_Float32) {
|
||||
MToDouble *replace = MToDouble::New(in);
|
||||
def->block()->insertBefore(def, replace);
|
||||
def->replaceOperand(Op, replace);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template bool NoFloatPolicy<0>::staticAdjustInputs(MInstruction *def);
|
||||
template bool NoFloatPolicy<1>::staticAdjustInputs(MInstruction *def);
|
||||
template bool NoFloatPolicy<2>::staticAdjustInputs(MInstruction *def);
|
||||
template bool NoFloatPolicy<3>::staticAdjustInputs(MInstruction *def);
|
||||
|
||||
template <unsigned Op>
|
||||
bool
|
||||
BoxPolicy<Op>::staticAdjustInputs(MInstruction *ins)
|
||||
@ -468,6 +556,18 @@ CallPolicy::adjustInputs(MInstruction *ins)
|
||||
{
|
||||
MCall *call = ins->toCall();
|
||||
|
||||
// External calls shouldn't have Float32 parameters
|
||||
for (uint32_t i = 0, numArgs = call->numActualArgs(); i < numArgs; i++) {
|
||||
MDefinition *arg = call->getArg(i+1); // arg 0 is |this|
|
||||
if (arg->type() == MIRType_Float32) {
|
||||
JS_ASSERT(arg->isPassArg()); // can't be a type barrier, as Float32 doesn't rely on type inference
|
||||
MPassArg *passArg = arg->toPassArg();
|
||||
MInstruction *replace = MToDouble::New(passArg->getArgument());
|
||||
passArg->replaceOperand(0, replace);
|
||||
call->block()->insertBefore(passArg, replace);
|
||||
}
|
||||
}
|
||||
|
||||
MDefinition *func = call->getFunction();
|
||||
if (func->type() == MIRType_Object)
|
||||
return true;
|
||||
@ -521,6 +621,7 @@ StoreTypedArrayPolicy::adjustValueInput(MInstruction *ins, int arrayType,
|
||||
switch (value->type()) {
|
||||
case MIRType_Int32:
|
||||
case MIRType_Double:
|
||||
case MIRType_Float32:
|
||||
case MIRType_Boolean:
|
||||
case MIRType_Value:
|
||||
break;
|
||||
@ -550,6 +651,7 @@ StoreTypedArrayPolicy::adjustValueInput(MInstruction *ins, int arrayType,
|
||||
JS_ASSERT(value->type() == MIRType_Int32 ||
|
||||
value->type() == MIRType_Boolean ||
|
||||
value->type() == MIRType_Double ||
|
||||
value->type() == MIRType_Float32 ||
|
||||
value->type() == MIRType_Value);
|
||||
|
||||
switch (arrayType) {
|
||||
@ -569,6 +671,15 @@ StoreTypedArrayPolicy::adjustValueInput(MInstruction *ins, int arrayType,
|
||||
JS_ASSERT(value->type() == MIRType_Int32);
|
||||
break;
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT32:
|
||||
if (LIRGenerator::allowFloat32Optimizations()) {
|
||||
if (value->type() != MIRType_Float32) {
|
||||
value = MToFloat32::New(value);
|
||||
ins->block()->insertBefore(ins, value->toInstruction());
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Fallthrough: if the LIRGenerator cannot directly store Float32, it will expect the
|
||||
// stored value to be a double.
|
||||
case ScalarTypeRepresentation::TYPE_FLOAT64:
|
||||
if (value->type() != MIRType_Double) {
|
||||
value = MToDouble::New(value);
|
||||
|
@ -35,6 +35,7 @@ class BoxInputsPolicy : public TypePolicy
|
||||
static MDefinition *boxAt(MInstruction *at, MDefinition *operand);
|
||||
|
||||
public:
|
||||
static MDefinition *alwaysBoxAt(MInstruction *at, MDefinition *operand);
|
||||
virtual bool adjustInputs(MInstruction *def);
|
||||
};
|
||||
|
||||
@ -144,6 +145,45 @@ class DoublePolicy : public BoxInputsPolicy
|
||||
}
|
||||
};
|
||||
|
||||
// Expect a float32 for operand Op. If the input is a Value, it is unboxed.
|
||||
template <unsigned Op>
|
||||
class Float32Policy : public BoxInputsPolicy
|
||||
{
|
||||
public:
|
||||
static bool staticAdjustInputs(MInstruction *def);
|
||||
bool adjustInputs(MInstruction *def) {
|
||||
return staticAdjustInputs(def);
|
||||
}
|
||||
};
|
||||
|
||||
// Expect a float32 OR a double for operand Op, but will prioritize Float32
|
||||
// if the result type is set as such. If the input is a Value, it is unboxed.
|
||||
template <unsigned Op>
|
||||
class RuntimePolicy : public TypePolicy
|
||||
{
|
||||
MIRType policyType_;
|
||||
|
||||
public:
|
||||
bool adjustInputs(MInstruction *def) {
|
||||
if (policyType_ == MIRType_Double)
|
||||
return DoublePolicy<Op>::staticAdjustInputs(def);
|
||||
return Float32Policy<Op>::staticAdjustInputs(def);
|
||||
}
|
||||
void setPolicyType(MIRType type) {
|
||||
policyType_ = type;
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned Op>
|
||||
class NoFloatPolicy
|
||||
{
|
||||
public:
|
||||
static bool staticAdjustInputs(MInstruction *def);
|
||||
bool adjustInputs(MInstruction *def) {
|
||||
return staticAdjustInputs(def);
|
||||
}
|
||||
};
|
||||
|
||||
// Box objects or strings as an input to a ToDouble instruction.
|
||||
class ToDoublePolicy : public BoxInputsPolicy
|
||||
{
|
||||
@ -251,7 +291,7 @@ class ClampPolicy : public BoxInputsPolicy
|
||||
static inline bool
|
||||
CoercesToDouble(MIRType type)
|
||||
{
|
||||
if (type == MIRType_Undefined || type == MIRType_Double)
|
||||
if (type == MIRType_Undefined || IsFloatingPointType(type))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -67,6 +67,13 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
||||
LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
|
||||
LGetElementCacheT *newLGetElementCacheT(MGetElementCache *ins);
|
||||
|
||||
bool lowerConstantFloat32(float d, MInstruction *ins) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
bool lowerTruncateFToInt32(MTruncateToInt32 *ins) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
|
||||
public:
|
||||
bool visitConstant(MConstant *ins);
|
||||
bool visitBox(MBox *box);
|
||||
|
@ -54,6 +54,19 @@ class MacroAssemblerARM : public Assembler
|
||||
void convertDoubleToInt32(const FloatRegister &src, const Register &dest, Label *fail,
|
||||
bool negativeZeroCheck = true);
|
||||
|
||||
void convertFloatToDouble(const FloatRegister &src, const FloatRegister &dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
void convertInt32ToFloat32(const Register &src, const FloatRegister &dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
void convertInt32ToFloat32(const Address &src, FloatRegister dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
|
||||
void addDouble(FloatRegister src, FloatRegister dest);
|
||||
void subDouble(FloatRegister src, FloatRegister dest);
|
||||
void mulDouble(FloatRegister src, FloatRegister dest);
|
||||
@ -748,6 +761,16 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
Condition testDoubleTruthy(bool truthy, const FloatRegister ®);
|
||||
Condition testStringTruthy(bool truthy, const ValueOperand &value);
|
||||
|
||||
void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
void loadStaticFloat32(const float *dp, const FloatRegister &dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void branchTestInt32(Condition cond, const T & t, Label *label) {
|
||||
Condition c = testInt32(cond, t);
|
||||
@ -1187,6 +1210,13 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void loadFloatAsDouble(const Address &addr, const FloatRegister &dest);
|
||||
void loadFloatAsDouble(const BaseIndex &src, const FloatRegister &dest);
|
||||
|
||||
void loadFloat(const Address &addr, const FloatRegister &dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
void loadFloat(const BaseIndex &src, const FloatRegister &dest) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
|
||||
void store8(const Register &src, const Address &address);
|
||||
void store8(const Imm32 &imm, const Address &address);
|
||||
void store8(const Register &src, const BaseIndex &address);
|
||||
@ -1269,6 +1299,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void branchDouble(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs,
|
||||
Label *label);
|
||||
|
||||
void branchFloat(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs,
|
||||
Label *label) {
|
||||
MOZ_ASSUME_UNREACHABLE("NYI");
|
||||
}
|
||||
|
||||
void checkStackAlignment();
|
||||
|
||||
void rshiftPtr(Imm32 imm, Register dest) {
|
||||
|
@ -227,6 +227,10 @@ class AssemblerX86Shared
|
||||
label->bind(masm.size());
|
||||
masm.doubleConstant(d);
|
||||
}
|
||||
void writeFloatConstant(float f, Label *label) {
|
||||
label->bind(masm.size());
|
||||
masm.floatConstant(f);
|
||||
}
|
||||
void movl(const Imm32 &imm32, const Register &dest) {
|
||||
masm.movl_i32r(imm32.value, dest.code());
|
||||
}
|
||||
@ -332,6 +336,9 @@ class AssemblerX86Shared
|
||||
void movss(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
case Operand::FPREG:
|
||||
masm.movss_rr(src.fpu(), dest.code());
|
||||
break;
|
||||
case Operand::REG_DISP:
|
||||
masm.movss_mr(src.disp(), src.base(), dest.code());
|
||||
break;
|
||||
@ -345,6 +352,9 @@ class AssemblerX86Shared
|
||||
void movss(const FloatRegister &src, const Operand &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (dest.kind()) {
|
||||
case Operand::FPREG:
|
||||
masm.movss_rr(src.code(), dest.fpu());
|
||||
break;
|
||||
case Operand::REG_DISP:
|
||||
masm.movss_rm(src.code(), dest.disp(), dest.base());
|
||||
break;
|
||||
@ -1141,6 +1151,30 @@ class AssemblerX86Shared
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.cvttsd2si_rr(src.code(), dest.code());
|
||||
}
|
||||
void cvttss2si(const FloatRegister &src, const Register &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.cvttss2si_rr(src.code(), dest.code());
|
||||
}
|
||||
void cvtsi2ss(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
case Operand::REG:
|
||||
masm.cvtsi2ss_rr(src.reg(), dest.code());
|
||||
break;
|
||||
case Operand::REG_DISP:
|
||||
masm.cvtsi2ss_mr(src.disp(), src.base(), dest.code());
|
||||
break;
|
||||
case Operand::SCALE:
|
||||
masm.cvtsi2ss_mr(src.disp(), src.base(), src.index(), src.scale(), dest.code());
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void cvtsi2ss(const Register &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.cvtsi2ss_rr(src.code(), dest.code());
|
||||
}
|
||||
void cvtsi2sd(const Register &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.cvtsi2sd_rr(src.code(), dest.code());
|
||||
@ -1157,6 +1191,10 @@ class AssemblerX86Shared
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.ucomisd_rr(rhs.code(), lhs.code());
|
||||
}
|
||||
void ucomiss(const FloatRegister &lhs, const FloatRegister &rhs) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.ucomiss_rr(rhs.code(), lhs.code());
|
||||
}
|
||||
void pcmpeqw(const FloatRegister &lhs, const FloatRegister &rhs) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.pcmpeqw_rr(rhs.code(), lhs.code());
|
||||
@ -1173,6 +1211,10 @@ class AssemblerX86Shared
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.addsd_rr(src.code(), dest.code());
|
||||
}
|
||||
void addss(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.addss_rr(src.code(), dest.code());
|
||||
}
|
||||
void addsd(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
@ -1186,6 +1228,24 @@ class AssemblerX86Shared
|
||||
case Operand::ADDRESS:
|
||||
masm.addsd_mr(src.address(), dest.code());
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void addss(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
case Operand::FPREG:
|
||||
masm.addss_rr(src.fpu(), dest.code());
|
||||
break;
|
||||
case Operand::REG_DISP:
|
||||
masm.addss_mr(src.disp(), src.base(), dest.code());
|
||||
break;
|
||||
#ifdef JS_CPU_X86
|
||||
case Operand::ADDRESS:
|
||||
masm.addss_mr(src.address(), dest.code());
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
@ -1195,6 +1255,10 @@ class AssemblerX86Shared
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.subsd_rr(src.code(), dest.code());
|
||||
}
|
||||
void subss(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.subss_rr(src.code(), dest.code());
|
||||
}
|
||||
void subsd(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
@ -1208,6 +1272,19 @@ class AssemblerX86Shared
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void subss(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
case Operand::FPREG:
|
||||
masm.subss_rr(src.fpu(), dest.code());
|
||||
break;
|
||||
case Operand::REG_DISP:
|
||||
masm.subss_mr(src.disp(), src.base(), dest.code());
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void mulsd(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.mulsd_rr(src.code(), dest.code());
|
||||
@ -1225,10 +1302,31 @@ class AssemblerX86Shared
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void mulss(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
case Operand::FPREG:
|
||||
masm.mulss_rr(src.fpu(), dest.code());
|
||||
break;
|
||||
case Operand::REG_DISP:
|
||||
masm.mulss_mr(src.disp(), src.base(), dest.code());
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void mulss(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.mulss_rr(src.code(), dest.code());
|
||||
}
|
||||
void divsd(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.divsd_rr(src.code(), dest.code());
|
||||
}
|
||||
void divss(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.divss_rr(src.code(), dest.code());
|
||||
}
|
||||
void divsd(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
@ -1242,10 +1340,27 @@ class AssemblerX86Shared
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void divss(const Operand &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
case Operand::FPREG:
|
||||
masm.divss_rr(src.fpu(), dest.code());
|
||||
break;
|
||||
case Operand::REG_DISP:
|
||||
masm.divss_mr(src.disp(), src.base(), dest.code());
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void xorpd(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.xorpd_rr(src.code(), dest.code());
|
||||
}
|
||||
void xorps(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.xorps_rr(src.code(), dest.code());
|
||||
}
|
||||
void orpd(const FloatRegister &src, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.orpd_rr(src.code(), dest.code());
|
||||
|
@ -162,15 +162,23 @@ CodeGeneratorShared::encodeSlots(LSnapshot *snapshot, MResumePoint *resumePoint,
|
||||
case MIRType_Object:
|
||||
case MIRType_Boolean:
|
||||
case MIRType_Double:
|
||||
case MIRType_Float32:
|
||||
{
|
||||
LAllocation *payload = snapshot->payloadOfSlot(i);
|
||||
JSValueType type = ValueTypeFromMIRType(mir->type());
|
||||
JSValueType valueType = ValueTypeFromMIRType(type);
|
||||
if (payload->isMemory()) {
|
||||
snapshots_.addSlot(type, ToStackIndex(payload));
|
||||
if (type == MIRType_Float32)
|
||||
snapshots_.addFloat32Slot(ToStackIndex(payload));
|
||||
else
|
||||
snapshots_.addSlot(valueType, ToStackIndex(payload));
|
||||
} else if (payload->isGeneralReg()) {
|
||||
snapshots_.addSlot(type, ToRegister(payload));
|
||||
snapshots_.addSlot(valueType, ToRegister(payload));
|
||||
} else if (payload->isFloatReg()) {
|
||||
snapshots_.addSlot(ToFloatRegister(payload));
|
||||
FloatRegister reg = ToFloatRegister(payload);
|
||||
if (type == MIRType_Float32)
|
||||
snapshots_.addFloat32Slot(reg);
|
||||
else
|
||||
snapshots_.addSlot(reg);
|
||||
} else {
|
||||
MConstant *constant = mir->toConstant();
|
||||
const Value &v = constant->value();
|
||||
|
@ -89,6 +89,14 @@ CodeGeneratorX86Shared::visitDouble(LDouble *ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitFloat32(LFloat32 *ins)
|
||||
{
|
||||
const LDefinition *out = ins->getDef(0);
|
||||
masm.loadConstantFloat32(ins->getFloat(), ToFloatRegister(out));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitTestIAndBranch(LTestIAndBranch *test)
|
||||
{
|
||||
@ -1276,6 +1284,34 @@ CodeGeneratorX86Shared::visitMathD(LMathD *math)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitMathF(LMathF *math)
|
||||
{
|
||||
FloatRegister lhs = ToFloatRegister(math->lhs());
|
||||
Operand rhs = ToOperand(math->rhs());
|
||||
|
||||
JS_ASSERT(ToFloatRegister(math->output()) == lhs);
|
||||
|
||||
switch (math->jsop()) {
|
||||
case JSOP_ADD:
|
||||
masm.addss(rhs, lhs);
|
||||
break;
|
||||
case JSOP_SUB:
|
||||
masm.subss(rhs, lhs);
|
||||
break;
|
||||
case JSOP_MUL:
|
||||
masm.mulss(rhs, lhs);
|
||||
break;
|
||||
case JSOP_DIV:
|
||||
masm.divss(rhs, lhs);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected opcode");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitFloor(LFloor *lir)
|
||||
{
|
||||
@ -1527,6 +1563,16 @@ CodeGeneratorX86Shared::visitNegD(LNegD *ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitNegF(LNegF *ins)
|
||||
{
|
||||
FloatRegister input = ToFloatRegister(ins->input());
|
||||
JS_ASSERT(input == ToFloatRegister(ins->output()));
|
||||
|
||||
masm.negateFloat(input);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
@ -79,6 +79,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
public:
|
||||
// Instruction visitors.
|
||||
virtual bool visitDouble(LDouble *ins);
|
||||
virtual bool visitFloat32(LFloat32 *ins);
|
||||
virtual bool visitMinMaxD(LMinMaxD *ins);
|
||||
virtual bool visitAbsD(LAbsD *ins);
|
||||
virtual bool visitSqrtD(LSqrtD *ins);
|
||||
@ -105,6 +106,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
virtual bool visitNotI(LNotI *comp);
|
||||
virtual bool visitNotD(LNotD *comp);
|
||||
virtual bool visitMathD(LMathD *math);
|
||||
virtual bool visitMathF(LMathF *math);
|
||||
virtual bool visitFloor(LFloor *lir);
|
||||
virtual bool visitRound(LRound *lir);
|
||||
virtual bool visitGuardShape(LGuardShape *guard);
|
||||
@ -116,6 +118,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
|
||||
bool visitNegI(LNegI *lir);
|
||||
bool visitNegD(LNegD *lir);
|
||||
bool visitNegF(LNegF *lir);
|
||||
|
||||
// Out of line visitors.
|
||||
bool visitOutOfLineBailout(OutOfLineBailout *ool);
|
||||
|
@ -140,6 +140,7 @@ LIRGeneratorShared::defineReturn(LInstruction *lir, MDefinition *mir)
|
||||
lir->setDef(0, LDefinition(vreg, LDefinition::BOX, LGeneralReg(JSReturnReg)));
|
||||
#endif
|
||||
break;
|
||||
case MIRType_Float32:
|
||||
case MIRType_Double:
|
||||
lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnFloatReg)));
|
||||
break;
|
||||
@ -274,7 +275,7 @@ LIRGeneratorShared::useRegisterOrNonNegativeConstantAtStart(MDefinition *mir)
|
||||
LAllocation
|
||||
LIRGeneratorShared::useRegisterOrNonDoubleConstant(MDefinition *mir)
|
||||
{
|
||||
if (mir->isConstant() && mir->type() != MIRType_Double)
|
||||
if (mir->isConstant() && mir->type() != MIRType_Double && mir->type() != MIRType_Float32)
|
||||
return LAllocation(mir->toConstant()->vp());
|
||||
return useRegister(mir);
|
||||
}
|
||||
@ -413,7 +414,7 @@ VirtualRegisterOfPayload(MDefinition *mir)
|
||||
{
|
||||
if (mir->isBox()) {
|
||||
MDefinition *inner = mir->toBox()->getOperand(0);
|
||||
if (!inner->isConstant() && inner->type() != MIRType_Double)
|
||||
if (!inner->isConstant() && inner->type() != MIRType_Double && inner->type() != MIRType_Float32)
|
||||
return inner->virtualRegister();
|
||||
}
|
||||
if (mir->isTypeBarrier())
|
||||
|
@ -181,6 +181,11 @@ class LIRGeneratorShared : public MInstructionVisitorWithDefaults
|
||||
static bool allowStaticTypedArrayAccesses() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whether we can emit Float32 specific optimizations.
|
||||
static bool allowFloat32Optimizations() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
|
@ -264,12 +264,21 @@ LIRGeneratorX86Shared::lowerConstantDouble(double d, MInstruction *mir)
|
||||
return define(new LDouble(d), mir);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorX86Shared::lowerConstantFloat32(float f, MInstruction *mir)
|
||||
{
|
||||
return define(new LFloat32(f), mir);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGeneratorX86Shared::visitConstant(MConstant *ins)
|
||||
{
|
||||
if (ins->type() == MIRType_Double)
|
||||
return lowerConstantDouble(ins->value().toDouble(), ins);
|
||||
|
||||
if (ins->type() == MIRType_Float32)
|
||||
return lowerConstantFloat32(ins->value().toDouble(), ins);
|
||||
|
||||
// Emit non-double constants at their uses.
|
||||
if (ins->canEmitAtUses())
|
||||
return emitAtUses(ins);
|
||||
|
@ -46,6 +46,7 @@ class LIRGeneratorX86Shared : public LIRGeneratorShared
|
||||
bool lowerUMod(MInstruction *mod);
|
||||
bool lowerUrshD(MUrsh *mir);
|
||||
bool lowerConstantDouble(double d, MInstruction *ins);
|
||||
bool lowerConstantFloat32(float d, MInstruction *ins);
|
||||
bool lowerTruncateDToInt32(MTruncateToInt32 *ins);
|
||||
};
|
||||
|
||||
|
@ -62,6 +62,34 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
j(ConditionFromDoubleCondition(cond), label);
|
||||
}
|
||||
|
||||
void compareFloat(DoubleCondition cond, const FloatRegister &lhs, const FloatRegister &rhs) {
|
||||
if (cond & DoubleConditionBitInvert)
|
||||
ucomiss(rhs, lhs);
|
||||
else
|
||||
ucomiss(lhs, rhs);
|
||||
}
|
||||
void branchFloat(DoubleCondition cond, const FloatRegister &lhs,
|
||||
const FloatRegister &rhs, Label *label)
|
||||
{
|
||||
compareFloat(cond, lhs, rhs);
|
||||
|
||||
if (cond == DoubleEqual) {
|
||||
Label unordered;
|
||||
j(Parity, &unordered);
|
||||
j(Equal, label);
|
||||
bind(&unordered);
|
||||
return;
|
||||
}
|
||||
if (cond == DoubleNotEqualOrUnordered) {
|
||||
j(NotEqual, label);
|
||||
j(Parity, label);
|
||||
return;
|
||||
}
|
||||
|
||||
JS_ASSERT(!(cond & DoubleConditionBitSpecial));
|
||||
j(ConditionFromDoubleCondition(cond), label);
|
||||
}
|
||||
|
||||
void move32(const Imm32 &imm, const Register &dest) {
|
||||
if (imm.value == 0)
|
||||
xorl(dest, dest);
|
||||
@ -211,6 +239,12 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
void convertInt32ToDouble(const Address &src, FloatRegister dest) {
|
||||
cvtsi2sd(Operand(src), dest);
|
||||
}
|
||||
void convertInt32ToFloat32(const Register &src, const FloatRegister &dest) {
|
||||
cvtsi2ss(src, dest);
|
||||
}
|
||||
void convertInt32ToFloat32(const Address &src, FloatRegister dest) {
|
||||
cvtsi2ss(Operand(src), dest);
|
||||
}
|
||||
Condition testDoubleTruthy(bool truthy, const FloatRegister ®) {
|
||||
xorpd(ScratchFloatReg, ScratchFloatReg);
|
||||
ucomisd(ScratchFloatReg, reg);
|
||||
@ -293,6 +327,13 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
// XOR the float in a float register with -0.0.
|
||||
xorpd(ScratchFloatReg, reg); // s ^ 0x80000000000000
|
||||
}
|
||||
void negateFloat(FloatRegister reg) {
|
||||
pcmpeqw(ScratchFloatReg, ScratchFloatReg);
|
||||
psllq(Imm32(31), ScratchFloatReg);
|
||||
|
||||
// XOR the float in a float register with -0.0.
|
||||
xorps(ScratchFloatReg, reg); // s ^ 0x80000000
|
||||
}
|
||||
void addDouble(FloatRegister src, FloatRegister dest) {
|
||||
addsd(src, dest);
|
||||
}
|
||||
@ -305,6 +346,9 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
void divDouble(FloatRegister src, FloatRegister dest) {
|
||||
divsd(src, dest);
|
||||
}
|
||||
void convertFloatToDouble(const FloatRegister &src, const FloatRegister &dest) {
|
||||
cvtss2sd(src, dest);
|
||||
}
|
||||
void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest) {
|
||||
cvtsd2ss(src, dest);
|
||||
}
|
||||
@ -324,6 +368,18 @@ class MacroAssemblerX86Shared : public Assembler
|
||||
movss(src, dest);
|
||||
cvtss2sd(dest, dest);
|
||||
}
|
||||
void loadFloat(const Register &src, FloatRegister dest) {
|
||||
movss(Operand(src), dest);
|
||||
}
|
||||
void loadFloat(const Address &src, FloatRegister dest) {
|
||||
movss(Operand(src), dest);
|
||||
}
|
||||
void loadFloat(const BaseIndex &src, FloatRegister dest) {
|
||||
movss(Operand(src), dest);
|
||||
}
|
||||
void loadFloat(const Operand &src, FloatRegister dest) {
|
||||
movss(src, dest);
|
||||
}
|
||||
void storeFloat(FloatRegister src, const Address &dest) {
|
||||
movss(src, Operand(dest));
|
||||
}
|
||||
|
@ -104,10 +104,12 @@ LIRGeneratorX64::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
|
||||
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
|
||||
if (ins->isFloatArray())
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Double);
|
||||
else
|
||||
if (ins->isFloatArray()) {
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
|
||||
} else {
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Int32);
|
||||
}
|
||||
|
||||
LUse elements = useRegister(ins->elements());
|
||||
LAllocation index = useRegisterOrConstant(ins->index());
|
||||
@ -122,10 +124,12 @@ LIRGeneratorX64::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *in
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
JS_ASSERT(ins->length()->type() == MIRType_Int32);
|
||||
|
||||
if (ins->isFloatArray())
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Double);
|
||||
else
|
||||
if (ins->isFloatArray()) {
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
|
||||
} else {
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Int32);
|
||||
}
|
||||
|
||||
LUse elements = useRegister(ins->elements());
|
||||
LAllocation length = useAnyOrConstant(ins->length());
|
||||
|
@ -44,6 +44,10 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
|
||||
bool visitAsmJSStoreHeap(MAsmJSStoreHeap *ins);
|
||||
bool visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins);
|
||||
bool visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic *ins);
|
||||
|
||||
static bool allowFloat32Optimizations() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
typedef LIRGeneratorX64 LIRGeneratorSpecific;
|
||||
|
@ -962,10 +962,29 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
cvtsi2sd(operand.valueReg(), dest);
|
||||
}
|
||||
|
||||
void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
cvtsi2ss(operand.valueReg(), dest);
|
||||
}
|
||||
void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
cvtsi2ss(operand.valueReg(), dest);
|
||||
}
|
||||
|
||||
void loadConstantDouble(double d, const FloatRegister &dest);
|
||||
void loadConstantFloat32(float f, const FloatRegister &dest) {
|
||||
union FloatPun {
|
||||
uint32_t u;
|
||||
float f;
|
||||
} pun;
|
||||
pun.f = f;
|
||||
mov(ImmWord(pun.u), ScratchReg);
|
||||
movq(ScratchReg, dest);
|
||||
}
|
||||
void loadStaticDouble(const double *dp, const FloatRegister &dest) {
|
||||
loadConstantDouble(*dp, dest);
|
||||
}
|
||||
void loadStaticFloat32(const float *fp, const FloatRegister &dest) {
|
||||
loadConstantFloat32(*fp, dest);
|
||||
}
|
||||
|
||||
void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail) {
|
||||
const uint64_t IndefiniteIntegerValue = 0x8000000000000000;
|
||||
|
@ -25,6 +25,7 @@ ABIArgGenerator::next(MIRType type)
|
||||
case MIRType_Pointer:
|
||||
stackOffset_ += sizeof(uint32_t);
|
||||
break;
|
||||
case MIRType_Float32: // Float32 moves are actually double moves
|
||||
case MIRType_Double:
|
||||
stackOffset_ += sizeof(uint64_t);
|
||||
break;
|
||||
|
@ -251,6 +251,7 @@ class Assembler : public AssemblerX86Shared
|
||||
using AssemblerX86Shared::j;
|
||||
using AssemblerX86Shared::jmp;
|
||||
using AssemblerX86Shared::movsd;
|
||||
using AssemblerX86Shared::movss;
|
||||
using AssemblerX86Shared::retarget;
|
||||
using AssemblerX86Shared::cmpl;
|
||||
using AssemblerX86Shared::call;
|
||||
@ -444,6 +445,11 @@ class Assembler : public AssemblerX86Shared
|
||||
masm.movsd_mr((const void *)dp, dest.code());
|
||||
}
|
||||
|
||||
void movss(const float *dp, const FloatRegister &dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.movss_mr((const void *)dp, dest.code());
|
||||
}
|
||||
|
||||
// Move a 32-bit immediate into a register where the immediate can be
|
||||
// patched.
|
||||
CodeOffsetLabel movlWithPatch(Imm32 imm, Register dest) {
|
||||
@ -461,6 +467,11 @@ class Assembler : public AssemblerX86Shared
|
||||
masm.movsd_mr(addr, dest.code());
|
||||
return masm.currentOffset();
|
||||
}
|
||||
CodeOffsetLabel movssWithPatch(void *addr, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.movss_mr(addr, dest.code());
|
||||
return masm.currentOffset();
|
||||
}
|
||||
|
||||
// Store to *addr where addr can be patched
|
||||
CodeOffsetLabel movlWithPatch(Register src, void *addr) {
|
||||
@ -472,6 +483,11 @@ class Assembler : public AssemblerX86Shared
|
||||
masm.movsd_rm(dest.code(), addr);
|
||||
return masm.currentOffset();
|
||||
}
|
||||
CodeOffsetLabel movssWithPatch(FloatRegister dest, void *addr) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.movss_rm(dest.code(), addr);
|
||||
return masm.currentOffset();
|
||||
}
|
||||
|
||||
// Load from *(base + disp32) where disp32 can be patched.
|
||||
CodeOffsetLabel movsblWithPatch(Address src, Register dest) {
|
||||
|
@ -464,10 +464,10 @@ CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic
|
||||
|
||||
Address srcAddr(ptr, (int32_t) mir->base());
|
||||
if (vt == ArrayBufferView::TYPE_FLOAT32) {
|
||||
JS_ASSERT(mir->type() == MIRType_Float32);
|
||||
FloatRegister dest = ToFloatRegister(out);
|
||||
masm.movssWithPatch(srcAddr, dest);
|
||||
masm.cvtss2sd(dest, dest);
|
||||
masm.canonicalizeDouble(dest);
|
||||
masm.canonicalizeFloat(dest);
|
||||
if (ool)
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
@ -562,8 +562,8 @@ CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllo
|
||||
const T &dstAddr)
|
||||
{
|
||||
if (vt == ArrayBufferView::TYPE_FLOAT32) {
|
||||
masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
|
||||
uint32_t before = masm.size();
|
||||
masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
|
||||
masm.movssWithPatch(ScratchFloatReg, dstAddr);
|
||||
uint32_t after = masm.size();
|
||||
return gen->noteHeapAccess(AsmJSHeapAccess(before, after));
|
||||
@ -589,8 +589,8 @@ CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati
|
||||
|
||||
Address dstAddr(ptr, (int32_t) mir->base());
|
||||
if (vt == ArrayBufferView::TYPE_FLOAT32) {
|
||||
masm.convertDoubleToFloat(ToFloatRegister(value), ScratchFloatReg);
|
||||
masm.movssWithPatch(ScratchFloatReg, dstAddr);
|
||||
JS_ASSERT(mir->value()->type() == MIRType_Float32);
|
||||
masm.movssWithPatch(ToFloatRegister(value), dstAddr);
|
||||
masm.bind(&rejoin);
|
||||
return true;
|
||||
}
|
||||
|
@ -166,10 +166,12 @@ LIRGeneratorX86::visitStoreTypedArrayElement(MStoreTypedArrayElement *ins)
|
||||
JS_ASSERT(ins->elements()->type() == MIRType_Elements);
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
|
||||
if (ins->isFloatArray())
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Double);
|
||||
else
|
||||
if (ins->isFloatArray()) {
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
|
||||
} else {
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Int32);
|
||||
}
|
||||
|
||||
LUse elements = useRegister(ins->elements());
|
||||
LAllocation index = useRegisterOrConstant(ins->index());
|
||||
@ -190,10 +192,12 @@ LIRGeneratorX86::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *in
|
||||
JS_ASSERT(ins->index()->type() == MIRType_Int32);
|
||||
JS_ASSERT(ins->length()->type() == MIRType_Int32);
|
||||
|
||||
if (ins->isFloatArray())
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Double);
|
||||
else
|
||||
if (ins->isFloatArray()) {
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT64, ins->value()->type() == MIRType_Double);
|
||||
JS_ASSERT_IF(ins->arrayType() == ScalarTypeRepresentation::TYPE_FLOAT32, ins->value()->type() == MIRType_Float32);
|
||||
} else {
|
||||
JS_ASSERT(ins->value()->type() == MIRType_Int32);
|
||||
}
|
||||
|
||||
LUse elements = useRegister(ins->elements());
|
||||
LAllocation length = useAnyOrConstant(ins->length());
|
||||
|
@ -56,6 +56,10 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
|
||||
static bool allowStaticTypedArrayAccesses() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool allowFloat32Optimizations() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
typedef LIRGeneratorX86 LIRGeneratorSpecific;
|
||||
|
@ -47,6 +47,34 @@ MacroAssemblerX86::loadConstantDouble(double d, const FloatRegister &dest)
|
||||
dbl.uses.setPrev(masm.size());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86::loadConstantFloat32(float f, const FloatRegister &dest)
|
||||
{
|
||||
// Contrarily to loadConstantDouble, this one doesn't have any maybeInlineFloat,
|
||||
// but that might be interesting to do it in the future.
|
||||
if (!floatMap_.initialized()) {
|
||||
enoughMemory_ &= floatMap_.init();
|
||||
if (!enoughMemory_)
|
||||
return;
|
||||
}
|
||||
size_t floatIndex;
|
||||
FloatMap::AddPtr p = floatMap_.lookupForAdd(f);
|
||||
if (p) {
|
||||
floatIndex = p->value;
|
||||
} else {
|
||||
floatIndex = floats_.length();
|
||||
enoughMemory_ &= floats_.append(Float(f));
|
||||
enoughMemory_ &= floatMap_.add(p, f, floatIndex);
|
||||
if (!enoughMemory_)
|
||||
return;
|
||||
}
|
||||
Float &flt = floats_[floatIndex];
|
||||
JS_ASSERT(!flt.uses.bound());
|
||||
|
||||
masm.movss_mr(reinterpret_cast<const void *>(flt.uses.prev()), dest.code());
|
||||
flt.uses.setPrev(masm.size());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86::loadStaticDouble(const double *dp, const FloatRegister &dest) {
|
||||
if (maybeInlineDouble(*dp, dest))
|
||||
@ -56,10 +84,16 @@ MacroAssemblerX86::loadStaticDouble(const double *dp, const FloatRegister &dest)
|
||||
movsd(dp, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86::loadStaticFloat32(const float *fp, const FloatRegister &dest) {
|
||||
// x86 can just load from any old immediate address.
|
||||
movss(fp, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86::finish()
|
||||
{
|
||||
if (doubles_.empty())
|
||||
if (doubles_.empty() && floats_.empty())
|
||||
return;
|
||||
|
||||
masm.align(sizeof(double));
|
||||
@ -70,6 +104,13 @@ MacroAssemblerX86::finish()
|
||||
if (!enoughMemory_)
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < floats_.length(); i++) {
|
||||
CodeLabel cl(floats_[i].uses);
|
||||
writeFloatConstant(floats_[i].value, cl.src());
|
||||
enoughMemory_ &= addCodeLabel(cl);
|
||||
if (!enoughMemory_)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -33,9 +33,17 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
Double(double value) : value(value) {}
|
||||
};
|
||||
Vector<Double, 0, SystemAllocPolicy> doubles_;
|
||||
struct Float {
|
||||
float value;
|
||||
AbsoluteLabel uses;
|
||||
Float(float value) : value(value) {}
|
||||
};
|
||||
Vector<Float, 0, SystemAllocPolicy> floats_;
|
||||
|
||||
typedef HashMap<double, size_t, DefaultHasher<double>, SystemAllocPolicy> DoubleMap;
|
||||
DoubleMap doubleMap_;
|
||||
typedef HashMap<float, size_t, DefaultHasher<float>, SystemAllocPolicy> FloatMap;
|
||||
FloatMap floatMap_;
|
||||
|
||||
protected:
|
||||
MoveResolver moveResolver_;
|
||||
@ -816,12 +824,20 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
void boolValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
cvtsi2sd(operand.payloadReg(), dest);
|
||||
}
|
||||
void boolValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
cvtsi2ss(operand.payloadReg(), dest);
|
||||
}
|
||||
void int32ValueToDouble(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
cvtsi2sd(operand.payloadReg(), dest);
|
||||
}
|
||||
void int32ValueToFloat32(const ValueOperand &operand, const FloatRegister &dest) {
|
||||
cvtsi2ss(operand.payloadReg(), dest);
|
||||
}
|
||||
|
||||
void loadConstantDouble(double d, const FloatRegister &dest);
|
||||
void loadConstantFloat32(float f, const FloatRegister &dest);
|
||||
void loadStaticDouble(const double *dp, const FloatRegister &dest);
|
||||
void loadStaticFloat32(const float *dp, const FloatRegister &dest);
|
||||
|
||||
void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail) {
|
||||
const uint32_t IndefiniteIntegerValue = 0x80000000;
|
||||
@ -829,6 +845,12 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
cmpl(dest, Imm32(IndefiniteIntegerValue));
|
||||
j(Assembler::Equal, fail);
|
||||
}
|
||||
void branchTruncateFloat32(const FloatRegister &src, const Register &dest, Label *fail) {
|
||||
const uint32_t IndefiniteIntegerValue = 0x80000000;
|
||||
cvttss2si(src, dest);
|
||||
cmpl(dest, Imm32(IndefiniteIntegerValue));
|
||||
j(Assembler::Equal, fail);
|
||||
}
|
||||
|
||||
Condition testInt32Truthy(bool truthy, const ValueOperand &operand) {
|
||||
testl(operand.payloadReg(), operand.payloadReg());
|
||||
@ -909,6 +931,20 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
addsd(Operand(&NegativeOne), dest);
|
||||
}
|
||||
|
||||
// Note: this function clobbers the source register.
|
||||
void convertUInt32ToFloat32(const Register &src, const FloatRegister &dest) {
|
||||
// src is [0, 2^32-1]
|
||||
subl(Imm32(0x80000000), src);
|
||||
|
||||
// Do it the GCC way
|
||||
cvtsi2ss(src, dest);
|
||||
|
||||
// dest is now a double with the int range.
|
||||
// correct the double value by adding 0x80000000.
|
||||
static const float NegativeOne = 2147483648.f;
|
||||
addss(Operand(&NegativeOne), dest);
|
||||
}
|
||||
|
||||
void inc64(AbsoluteAddress dest) {
|
||||
addl(Imm32(1), Operand(dest));
|
||||
Label noOverflow;
|
||||
|
Loading…
Reference in New Issue
Block a user