Bug 888109: Float32 general optimizations for IonMonkey: framework and arithmetic operations; r=sstangl,nbp

This commit is contained in:
Benjamin Bouvier 2013-07-18 15:13:15 -07:00
parent 9e93b3a656
commit 69698e03f0
49 changed files with 1747 additions and 100 deletions

View File

@ -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)
{

View File

@ -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));

View File

@ -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;

View File

@ -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)
{

View File

@ -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() {

View File

@ -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())

View File

@ -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;

View File

@ -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()));

View File

@ -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);

View File

@ -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(&notNaN);
}
void canonicalizeFloat(FloatRegister reg) {
static float js_NaN_float = js_NaN;
Label notNaN;
branchFloat(DoubleOrdered, reg, reg, &notNaN);
loadStaticFloat32(&js_NaN_float, reg);
bind(&notNaN);
}
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);

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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)

View File

@ -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))

View File

@ -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);

View File

@ -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();

View File

@ -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)
{

View File

@ -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_;

View File

@ -76,6 +76,7 @@ namespace jit {
_(GuardString) \
_(AssertRange) \
_(ToDouble) \
_(ToFloat32) \
_(ToInt32) \
_(TruncateToInt32) \
_(ToString) \

View File

@ -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)

View File

@ -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
{

View File

@ -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 &reg)
: 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)

View File

@ -46,6 +46,8 @@ class SnapshotWriter
void addUndefinedSlot();
void addNullSlot();
void addInt32Slot(int32_t value);
void addFloat32Slot(const FloatRegister &reg);
void addFloat32Slot(int32_t stackIndex);
void addConstantPoolSlot(uint32_t index);
#if defined(JS_NUNBOX32)
void addSlot(const Register &type, const Register &payload);

View File

@ -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 &reg)
{
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 &reg)
{
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)
{

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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 &reg);
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) {

View File

@ -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());

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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())

View File

@ -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

View File

@ -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);

View File

@ -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);
};

View File

@ -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 &reg) {
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));
}

View File

@ -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());

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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());

View File

@ -56,6 +56,10 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
static bool allowStaticTypedArrayAccesses() {
return true;
}
static bool allowFloat32Optimizations() {
return true;
}
};
typedef LIRGeneratorX86 LIRGeneratorSpecific;

View File

@ -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

View File

@ -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;