зеркало из https://github.com/mozilla/gecko-dev.git
Bug 984018 - sincos optimization. r=nbp
This commit is contained in:
Родитель
e19e1ac0e0
Коммит
4d5d75f626
|
@ -2402,6 +2402,7 @@ fi
|
|||
dnl Checks for math functions.
|
||||
dnl ========================================================
|
||||
AC_CHECK_LIB(m, sin)
|
||||
AC_CHECK_LIB(m, __sincos, AC_DEFINE(HAVE_SINCOS))
|
||||
AC_CHECK_FUNCS([log2 log1p expm1 sqrt1pm1 acosh asinh atanh cbrt])
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
var x = 0;
|
||||
function test() {
|
||||
function sincos1(a, b) {
|
||||
var sa = Math.sin(a);
|
||||
var sb = Math.sin(b);
|
||||
var ca = Math.cos(a);
|
||||
var cb = Math.cos(b);
|
||||
var ra = sa * sa + ca * ca;
|
||||
var rb = sb * sb + cb * cb;
|
||||
var dec = 100000;
|
||||
|
||||
assertEq(Math.round(ra * dec) / dec, Math.round(rb * dec) / dec);
|
||||
|
||||
ca = Math.cos(a);
|
||||
cb = Math.cos(b);
|
||||
sa = Math.sin(a);
|
||||
sb = Math.sin(b);
|
||||
|
||||
var ra = sa * sa + ca * ca;
|
||||
var rb = sb * sb + cb * cb;
|
||||
|
||||
assertEq(Math.round(ra * dec) / dec, Math.round(rb * dec) / dec);
|
||||
return ra;
|
||||
}
|
||||
|
||||
function sincos2(x) {
|
||||
var s1 = Math.sin(x);
|
||||
var c1 = Math.cos(x);
|
||||
|
||||
var c2 = Math.cos(x);
|
||||
var s2 = Math.sin(x);
|
||||
|
||||
return (s1 * s1 + c1 * c1) - (s2 * s2 + c2 * c2);
|
||||
}
|
||||
|
||||
function bailoutHere() { bailout(); }
|
||||
|
||||
function sincos3(x) {
|
||||
var s = Math.sin(x);
|
||||
bailoutHere();
|
||||
var c = Math.cos(x);
|
||||
assertEq(Math.round(s * s + c * c), 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
function sincos4(x) {
|
||||
if (x < 2500)
|
||||
x = Math.sin(x);
|
||||
else
|
||||
x = Math.cos(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
for (var i=0; i<5000; i++) {
|
||||
x += sincos1(x, x + 1);
|
||||
x += sincos2(x);
|
||||
x += sincos3(x);
|
||||
x += sincos4(i);
|
||||
}
|
||||
}
|
||||
x += 1;
|
||||
test();
|
|
@ -6400,6 +6400,42 @@ CodeGenerator::visitFromCharCode(LFromCharCode* lir)
|
|||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitSinCos(LSinCos *lir)
|
||||
{
|
||||
Register temp = ToRegister(lir->temp());
|
||||
Register params = ToRegister(lir->temp2());
|
||||
FloatRegister input = ToFloatRegister(lir->input());
|
||||
FloatRegister outputSin = ToFloatRegister(lir->outputSin());
|
||||
FloatRegister outputCos = ToFloatRegister(lir->outputCos());
|
||||
|
||||
masm.reserveStack(sizeof(double) * 2);
|
||||
masm.movePtr(masm.getStackPointer(), params);
|
||||
|
||||
const MathCache* mathCache = lir->mir()->cache();
|
||||
|
||||
masm.setupUnalignedABICall(temp);
|
||||
if (mathCache) {
|
||||
masm.movePtr(ImmPtr(mathCache), temp);
|
||||
masm.passABIArg(temp);
|
||||
}
|
||||
|
||||
#define MAYBE_CACHED_(fcn) (mathCache ? (void*)fcn ## _impl : (void*)fcn ## _uncached)
|
||||
|
||||
masm.passABIArg(input, MoveOp::DOUBLE);
|
||||
masm.passABIArg(MoveOperand(params, sizeof(double), MoveOperand::EFFECTIVE_ADDRESS),
|
||||
MoveOp::GENERAL);
|
||||
masm.passABIArg(MoveOperand(params, 0, MoveOperand::EFFECTIVE_ADDRESS),
|
||||
MoveOp::GENERAL);
|
||||
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, MAYBE_CACHED_(js::math_sincos)));
|
||||
#undef MAYBE_CACHED_
|
||||
|
||||
masm.loadDouble(Address(masm.getStackPointer(), 0), outputCos);
|
||||
masm.loadDouble(Address(masm.getStackPointer(), sizeof(double)), outputSin);
|
||||
masm.freeStack(sizeof(double) * 2);
|
||||
}
|
||||
|
||||
typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString);
|
||||
static const VMFunction StringSplitInfo = FunctionInfo<StringSplitFn>(js::str_split_string);
|
||||
|
||||
|
|
|
@ -242,6 +242,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
void visitConcat(LConcat* lir);
|
||||
void visitCharCodeAt(LCharCodeAt* lir);
|
||||
void visitFromCharCode(LFromCharCode* lir);
|
||||
void visitSinCos(LSinCos *lir);
|
||||
void visitStringSplit(LStringSplit* lir);
|
||||
void visitFunctionEnvironment(LFunctionEnvironment* lir);
|
||||
void visitCallGetProperty(LCallGetProperty* lir);
|
||||
|
|
|
@ -1378,6 +1378,111 @@ jit::ToggleBarriers(JS::Zone* zone, bool needs)
|
|||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
static void
|
||||
OptimizeSinCos(MIRGenerator *mir, MIRGraph &graph)
|
||||
{
|
||||
// Now, we are looking for:
|
||||
// var y = sin(x);
|
||||
// var z = cos(x);
|
||||
// Graph before:
|
||||
// - 1 op
|
||||
// - 6 mathfunction op1 Sin
|
||||
// - 7 mathfunction op1 Cos
|
||||
// Graph will look like:
|
||||
// - 1 op
|
||||
// - 5 sincos op1
|
||||
// - 6 mathfunction sincos5 Sin
|
||||
// - 7 mathfunction sincos5 Cos
|
||||
for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
|
||||
for (MInstructionIterator iter(block->begin()), end(block->end()); iter != end; ) {
|
||||
MInstruction *ins = *iter++;
|
||||
if (!ins->isMathFunction() || ins->isRecoveredOnBailout())
|
||||
continue;
|
||||
|
||||
MMathFunction *insFunc = ins->toMathFunction();
|
||||
if (insFunc->function() != MMathFunction::Sin && insFunc->function() != MMathFunction::Cos)
|
||||
continue;
|
||||
|
||||
// Check if sin/cos is already optimized.
|
||||
if (insFunc->getOperand(0)->type() == MIRType_SinCosDouble)
|
||||
continue;
|
||||
|
||||
// insFunc is either a |sin(x)| or |cos(x)| instruction. The
|
||||
// following loop iterates over the uses of |x| to check if both
|
||||
// |sin(x)| and |cos(x)| instructions exist.
|
||||
bool hasSin = false;
|
||||
bool hasCos = false;
|
||||
for (MUseDefIterator uses(insFunc->input()); uses; uses++)
|
||||
{
|
||||
if (!uses.def()->isInstruction())
|
||||
continue;
|
||||
|
||||
// We should replacing the argument of the sin/cos just when it
|
||||
// is dominated by the |block|.
|
||||
if (!block->dominates(uses.def()->block()))
|
||||
continue;
|
||||
|
||||
MInstruction *insUse = uses.def()->toInstruction();
|
||||
if (!insUse->isMathFunction() || insUse->isRecoveredOnBailout())
|
||||
continue;
|
||||
|
||||
MMathFunction *mathIns = insUse->toMathFunction();
|
||||
if (!hasSin && mathIns->function() == MMathFunction::Sin) {
|
||||
hasSin = true;
|
||||
JitSpew(JitSpew_Sincos, "Found sin in block %d.", mathIns->block()->id());
|
||||
}
|
||||
else if (!hasCos && mathIns->function() == MMathFunction::Cos) {
|
||||
hasCos = true;
|
||||
JitSpew(JitSpew_Sincos, "Found cos in block %d.", mathIns->block()->id());
|
||||
}
|
||||
|
||||
if (hasCos && hasSin)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasCos || !hasSin) {
|
||||
JitSpew(JitSpew_Sincos, "No sin/cos pair found.");
|
||||
continue;
|
||||
}
|
||||
|
||||
JitSpew(JitSpew_Sincos, "Found, at least, a pair sin/cos. Adding sincos in block %d",
|
||||
block->id());
|
||||
// Adding the MSinCos and replacing the parameters of the
|
||||
// sin(x)/cos(x) to sin(sincos(x))/cos(sincos(x)).
|
||||
MSinCos *insSinCos = MSinCos::New(graph.alloc(),
|
||||
insFunc->input(),
|
||||
insFunc->toMathFunction()->cache());
|
||||
insSinCos->setImplicitlyUsedUnchecked();
|
||||
block->insertBefore(insFunc, insSinCos);
|
||||
for (MUseDefIterator uses(insFunc->input()); uses; )
|
||||
{
|
||||
MDefinition* def = uses.def();
|
||||
uses++;
|
||||
if (!def->isInstruction())
|
||||
continue;
|
||||
|
||||
// We should replacing the argument of the sin/cos just when it
|
||||
// is dominated by the |block|.
|
||||
if (!block->dominates(def->block()))
|
||||
continue;
|
||||
|
||||
MInstruction *insUse = def->toInstruction();
|
||||
if (!insUse->isMathFunction() || insUse->isRecoveredOnBailout())
|
||||
continue;
|
||||
|
||||
MMathFunction *mathIns = insUse->toMathFunction();
|
||||
if (mathIns->function() != MMathFunction::Sin && mathIns->function() != MMathFunction::Cos)
|
||||
continue;
|
||||
|
||||
mathIns->replaceOperand(0, insSinCos);
|
||||
JitSpew(JitSpew_Sincos, "Replacing %s by sincos in block %d",
|
||||
mathIns->function() == MMathFunction::Sin ? "sin" : "cos",
|
||||
mathIns->block()->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
OptimizeMIR(MIRGenerator* mir)
|
||||
{
|
||||
|
@ -1653,6 +1758,16 @@ OptimizeMIR(MIRGenerator* mir)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mir->optimizationInfo().sincosEnabled()) {
|
||||
AutoTraceLog log(logger, TraceLogger_Sincos);
|
||||
OptimizeSinCos(mir, graph);
|
||||
gs.spewPass("Sincos optimization");
|
||||
AssertExtendedGraphCoherency(graph);
|
||||
|
||||
if (mir->shouldCancel("Sincos optimization"))
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
AutoTraceLog log(logger, TraceLogger_EliminateDeadCode);
|
||||
if (!EliminateDeadCode(mir, graph))
|
||||
|
|
|
@ -2094,6 +2094,7 @@ IsResumableMIRType(MIRType type)
|
|||
case MIRType_Shape:
|
||||
case MIRType_ObjectGroup:
|
||||
case MIRType_Doublex2: // NYI, see also RSimdBox::recover
|
||||
case MIRType_SinCosDouble:
|
||||
return false;
|
||||
}
|
||||
MOZ_CRASH("Unknown MIRType.");
|
||||
|
|
|
@ -35,6 +35,7 @@ OptimizationInfo::initNormalOptimizationInfo()
|
|||
loopUnrolling_ = true;
|
||||
reordering_ = true;
|
||||
autoTruncate_ = true;
|
||||
sincos_ = true;
|
||||
sink_ = true;
|
||||
registerAllocator_ = RegisterAllocator_Backtracking;
|
||||
|
||||
|
@ -66,6 +67,7 @@ OptimizationInfo::initAsmjsOptimizationInfo()
|
|||
edgeCaseAnalysis_ = false;
|
||||
eliminateRedundantChecks_ = false;
|
||||
autoTruncate_ = false;
|
||||
sincos_ = false;
|
||||
sink_ = false;
|
||||
registerAllocator_ = RegisterAllocator_Backtracking;
|
||||
scalarReplacement_ = false; // AsmJS has no objects.
|
||||
|
|
|
@ -85,6 +85,9 @@ class OptimizationInfo
|
|||
// Toggles whether Truncation based on Range Analysis is used.
|
||||
bool autoTruncate_;
|
||||
|
||||
// Toggles whether sincos is used.
|
||||
bool sincos_;
|
||||
|
||||
// Toggles whether sink is used.
|
||||
bool sink_;
|
||||
|
||||
|
@ -186,6 +189,10 @@ class OptimizationInfo
|
|||
return autoTruncate_ && rangeAnalysisEnabled();
|
||||
}
|
||||
|
||||
bool sincosEnabled() const {
|
||||
return sincos_ && !js_JitOptions.disableSincos;
|
||||
}
|
||||
|
||||
bool sinkEnabled() const {
|
||||
return sink_ && !js_JitOptions.disableSink;
|
||||
}
|
||||
|
|
|
@ -379,6 +379,7 @@ enum MIRType
|
|||
MIRType_MagicIsConstructing, // JS_IS_CONSTRUCTING magic value.
|
||||
MIRType_MagicUninitializedLexical, // JS_UNINITIALIZED_LEXICAL magic value.
|
||||
MIRType_Value,
|
||||
MIRType_SinCosDouble, // Optimizing a sin/cos to sincos.
|
||||
MIRType_ObjectOrNull,
|
||||
MIRType_None, // Invalid, used as a placeholder.
|
||||
MIRType_Slots, // A slots vector
|
||||
|
@ -492,6 +493,8 @@ StringFromMIRType(MIRType type)
|
|||
return "MagicUninitializedLexical";
|
||||
case MIRType_Value:
|
||||
return "Value";
|
||||
case MIRType_SinCosDouble:
|
||||
return "SinCosDouble";
|
||||
case MIRType_ObjectOrNull:
|
||||
return "ObjectOrNull";
|
||||
case MIRType_None:
|
||||
|
|
|
@ -110,6 +110,14 @@ JitOptions::JitOptions()
|
|||
// Toggles whether shared stubs are used in Ionmonkey.
|
||||
SET_DEFAULT(disableSharedStubs, true);
|
||||
|
||||
// Toggles whether sincos optimization is globally disabled.
|
||||
// See bug984018: The MacOS is the only one that has the sincos fast.
|
||||
#if defined(XP_MACOSX)
|
||||
SET_DEFAULT(disableSincos, false);
|
||||
#else
|
||||
SET_DEFAULT(disableSincos, true);
|
||||
#endif
|
||||
|
||||
// Toggles whether sink code motion is globally disabled.
|
||||
SET_DEFAULT(disableSink, true);
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ struct JitOptions
|
|||
bool disableRangeAnalysis;
|
||||
bool disableScalarReplacement;
|
||||
bool disableSharedStubs;
|
||||
bool disableSincos;
|
||||
bool disableSink;
|
||||
bool eagerCompilation;
|
||||
bool forceInlineCaches;
|
||||
|
|
|
@ -404,6 +404,7 @@ jit::CheckLogging()
|
|||
" alias Alias analysis\n"
|
||||
" gvn Global Value Numbering\n"
|
||||
" licm Loop invariant code motion\n"
|
||||
" sincos Replace sin/cos by sincos\n"
|
||||
" sink Sink transformation\n"
|
||||
" regalloc Register allocation\n"
|
||||
" inline Inlining\n"
|
||||
|
@ -455,6 +456,8 @@ jit::CheckLogging()
|
|||
EnableChannel(JitSpew_Unrolling);
|
||||
if (ContainsFlag(env, "licm"))
|
||||
EnableChannel(JitSpew_LICM);
|
||||
if (ContainsFlag(env, "sincos"))
|
||||
EnableChannel(JitSpew_Sincos);
|
||||
if (ContainsFlag(env, "sink"))
|
||||
EnableChannel(JitSpew_Sink);
|
||||
if (ContainsFlag(env, "regalloc"))
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace jit {
|
|||
_(Alias) \
|
||||
/* Information during GVN */ \
|
||||
_(GVN) \
|
||||
/* Information during sincos */ \
|
||||
_(Sincos) \
|
||||
/* Information during sinking */ \
|
||||
_(Sink) \
|
||||
/* Information during Range analysis */ \
|
||||
|
|
|
@ -350,6 +350,7 @@ static const char * const TypeChars[] =
|
|||
"d", // DOUBLE
|
||||
"i32x4", // INT32X4
|
||||
"f32x4", // FLOAT32X4
|
||||
"sincos", // SINCOS
|
||||
#ifdef JS_NUNBOX32
|
||||
"t", // TYPE
|
||||
"p" // PAYLOAD
|
||||
|
|
|
@ -422,6 +422,7 @@ class LDefinition
|
|||
DOUBLE, // 64-bit floating-point value (FPU).
|
||||
INT32X4, // SIMD data containing four 32-bit integers (FPU).
|
||||
FLOAT32X4, // SIMD data containing four 32-bit floats (FPU).
|
||||
SINCOS,
|
||||
#ifdef JS_NUNBOX32
|
||||
// A type virtual register must be followed by a payload virtual
|
||||
// register, as both will be tracked as a single gcthing.
|
||||
|
@ -562,6 +563,8 @@ class LDefinition
|
|||
case MIRType_Value:
|
||||
return LDefinition::BOX;
|
||||
#endif
|
||||
case MIRType_SinCosDouble:
|
||||
return LDefinition::SINCOS;
|
||||
case MIRType_Slots:
|
||||
case MIRType_Elements:
|
||||
return LDefinition::SLOTS;
|
||||
|
|
|
@ -1393,7 +1393,14 @@ void
|
|||
LIRGenerator::visitMathFunction(MMathFunction* ins)
|
||||
{
|
||||
MOZ_ASSERT(IsFloatingPointType(ins->type()));
|
||||
MOZ_ASSERT(ins->type() == ins->input()->type());
|
||||
MOZ_ASSERT_IF(ins->input()->type() != MIRType_SinCosDouble,
|
||||
ins->type() == ins->input()->type());
|
||||
|
||||
if (ins->input()->type() == MIRType_SinCosDouble) {
|
||||
MOZ_ASSERT(ins->type() == MIRType_Double);
|
||||
redefine(ins, ins->input(), ins->function());
|
||||
return;
|
||||
}
|
||||
|
||||
LInstruction* lir;
|
||||
if (ins->type() == MIRType_Double) {
|
||||
|
@ -2981,6 +2988,20 @@ LIRGenerator::visitArrayJoin(MArrayJoin* ins)
|
|||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitSinCos(MSinCos *ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->type() == MIRType_SinCosDouble);
|
||||
MOZ_ASSERT(ins->input()->type() == MIRType_Double ||
|
||||
ins->input()->type() == MIRType_Float32 ||
|
||||
ins->input()->type() == MIRType_Int32);
|
||||
|
||||
LSinCos *lir = new (alloc()) LSinCos(useRegisterAtStart(ins->input()),
|
||||
tempFixed(CallTempReg0),
|
||||
temp());
|
||||
defineSinCos(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitStringSplit(MStringSplit* ins)
|
||||
{
|
||||
|
|
|
@ -141,6 +141,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitConcat(MConcat* ins);
|
||||
void visitCharCodeAt(MCharCodeAt* ins);
|
||||
void visitFromCharCode(MFromCharCode* ins);
|
||||
void visitSinCos(MSinCos *ins);
|
||||
void visitStringSplit(MStringSplit* ins);
|
||||
void visitStart(MStart* start);
|
||||
void visitOsrEntry(MOsrEntry* entry);
|
||||
|
|
|
@ -6119,6 +6119,8 @@ class MMathFunction
|
|||
void computeRange(TempAllocator& alloc) override;
|
||||
bool writeRecoverData(CompactBufferWriter& writer) const override;
|
||||
bool canRecoverOnBailout() const override {
|
||||
if (input()->type() == MIRType_SinCosDouble)
|
||||
return false;
|
||||
switch(function_) {
|
||||
case Sin:
|
||||
case Log:
|
||||
|
@ -6626,6 +6628,40 @@ class MFromCharCode
|
|||
ALLOW_CLONE(MFromCharCode)
|
||||
};
|
||||
|
||||
class MSinCos
|
||||
: public MUnaryInstruction,
|
||||
public FloatingPointPolicy<0>::Data
|
||||
{
|
||||
const MathCache* cache_;
|
||||
|
||||
MSinCos(MDefinition *input, const MathCache *cache) : MUnaryInstruction(input), cache_(cache)
|
||||
{
|
||||
setResultType(MIRType_SinCosDouble);
|
||||
specialization_ = MIRType_Double;
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SinCos)
|
||||
|
||||
static MSinCos *New(TempAllocator &alloc, MDefinition *input, const MathCache *cache)
|
||||
{
|
||||
return new (alloc) MSinCos(input, cache);
|
||||
}
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
bool congruentTo(const MDefinition *ins) const override {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
bool possiblyCalls() const override {
|
||||
return true;
|
||||
}
|
||||
const MathCache* cache() const {
|
||||
return cache_;
|
||||
}
|
||||
};
|
||||
|
||||
class MStringSplit
|
||||
: public MTernaryInstruction,
|
||||
public MixPolicy<StringPolicy<0>, StringPolicy<1> >::Data
|
||||
|
|
|
@ -99,6 +99,7 @@ namespace jit {
|
|||
_(Concat) \
|
||||
_(CharCodeAt) \
|
||||
_(FromCharCode) \
|
||||
_(SinCos) \
|
||||
_(StringSplit) \
|
||||
_(Substr) \
|
||||
_(Return) \
|
||||
|
|
|
@ -97,6 +97,7 @@ class StackSlotAllocator
|
|||
case LDefinition::PAYLOAD:
|
||||
#endif
|
||||
case LDefinition::DOUBLE: return 8;
|
||||
case LDefinition::SINCOS:
|
||||
case LDefinition::FLOAT32X4:
|
||||
case LDefinition::INT32X4: return 16;
|
||||
}
|
||||
|
|
|
@ -493,7 +493,7 @@ bool
|
|||
DoublePolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def)
|
||||
{
|
||||
MDefinition* in = def->getOperand(Op);
|
||||
if (in->type() == MIRType_Double)
|
||||
if (in->type() == MIRType_Double || in->type() == MIRType_SinCosDouble)
|
||||
return true;
|
||||
|
||||
MToDouble* replace = MToDouble::New(alloc, in);
|
||||
|
|
|
@ -3311,6 +3311,38 @@ class LFromCharCode : public LInstructionHelper<1, 1, 0>
|
|||
}
|
||||
};
|
||||
|
||||
// Calculates sincos(x) and returns two values (sin/cos).
|
||||
class LSinCos : public LCallInstructionHelper<2, 1, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SinCos)
|
||||
|
||||
LSinCos(const LAllocation &input, const LDefinition &temp, const LDefinition &temp2)
|
||||
{
|
||||
setOperand(0, input);
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LAllocation *input() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LDefinition *outputSin() {
|
||||
return getDef(0);
|
||||
}
|
||||
const LDefinition *outputCos() {
|
||||
return getDef(1);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition *temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const MSinCos *mir() const {
|
||||
return mir_->toSinCos();
|
||||
}
|
||||
};
|
||||
|
||||
class LStringSplit : public LCallInstructionHelper<1, 2, 0>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
_(Concat) \
|
||||
_(CharCodeAt) \
|
||||
_(FromCharCode) \
|
||||
_(SinCos) \
|
||||
_(StringSplit) \
|
||||
_(Int32ToDouble) \
|
||||
_(Float32ToDouble) \
|
||||
|
|
|
@ -176,6 +176,35 @@ LIRGeneratorShared::defineReturn(LInstruction* lir, MDefinition* mir)
|
|||
add(lir);
|
||||
}
|
||||
|
||||
template <size_t Ops, size_t Temps> void
|
||||
LIRGeneratorShared::defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefinition *mir,
|
||||
LDefinition::Policy policy)
|
||||
{
|
||||
MOZ_ASSERT(lir->isCall());
|
||||
|
||||
uint32_t vreg = getVirtualRegister();
|
||||
lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnDoubleReg)));
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
|
||||
lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, LFloatReg(d1)));
|
||||
#elif defined(JS_CODEGEN_MIPS)
|
||||
lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, LFloatReg(f2)));
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
MOZ_CRASH();
|
||||
#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
lir->setDef(1, LDefinition(vreg + VREG_INCREMENT, LDefinition::DOUBLE, LFloatReg(xmm1)));
|
||||
#else
|
||||
#error "Unsupported architecture for SinCos"
|
||||
#endif
|
||||
|
||||
getVirtualRegister();
|
||||
|
||||
lir->setMir(mir);
|
||||
mir->setVirtualRegister(vreg);
|
||||
add(lir);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// In LIR, we treat booleans and integers as the same low-level type (INTEGER).
|
||||
// When snapshotting, we recover the actual JS type from MIR. This function
|
||||
// checks that when making redefinitions, we don't accidentally coerce two
|
||||
|
@ -195,6 +224,30 @@ IsCompatibleLIRCoercion(MIRType to, MIRType from)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
// We can redefine the sin(x) and cos(x) function to return the sincos result.
|
||||
void
|
||||
LIRGeneratorShared::redefine(MDefinition* def, MDefinition* as, MMathFunction::Function func)
|
||||
{
|
||||
MOZ_ASSERT(def->isMathFunction());
|
||||
MOZ_ASSERT(def->type() == MIRType_Double && as->type() == MIRType_SinCosDouble);
|
||||
MOZ_ASSERT(MMathFunction::Sin == func || MMathFunction::Cos == func);
|
||||
|
||||
ensureDefined(as);
|
||||
MMathFunction *math = def->toMathFunction();
|
||||
|
||||
MOZ_ASSERT(math->function() == MMathFunction::Cos ||
|
||||
math->function() == MMathFunction::Sin);
|
||||
|
||||
// The sincos returns two values:
|
||||
// - VREG: it returns the sin's value of the sincos;
|
||||
// - VREG + VREG_INCREMENT: it returns the cos' value of the sincos.
|
||||
if (math->function() == MMathFunction::Sin)
|
||||
def->setVirtualRegister(as->virtualRegister());
|
||||
else
|
||||
def->setVirtualRegister(as->virtualRegister() + VREG_INCREMENT);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorShared::redefine(MDefinition* def, MDefinition* as)
|
||||
{
|
||||
|
|
|
@ -142,6 +142,10 @@ class LIRGeneratorShared : public MDefinitionVisitor
|
|||
inline void defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
|
||||
LDefinition::Policy policy = LDefinition::REGISTER);
|
||||
|
||||
template <size_t Ops, size_t Temps>
|
||||
inline void defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefinition *mir,
|
||||
LDefinition::Policy policy = LDefinition::REGISTER);
|
||||
|
||||
inline void defineSharedStubReturn(LInstruction* lir, MDefinition* mir);
|
||||
inline void defineReturn(LInstruction* lir, MDefinition* mir);
|
||||
|
||||
|
@ -163,6 +167,9 @@ class LIRGeneratorShared : public MDefinitionVisitor
|
|||
// virtual register as |as|.
|
||||
inline void redefine(MDefinition* ins, MDefinition* as);
|
||||
|
||||
// Redefine a sin/cos call to sincos.
|
||||
inline void redefine(MDefinition* def, MDefinition* as, MMathFunction::Function func);
|
||||
|
||||
TempAllocator& alloc() const {
|
||||
return graph.alloc();
|
||||
}
|
||||
|
|
|
@ -953,6 +953,40 @@ js::math_sin(JSContext* cx, unsigned argc, Value* vp)
|
|||
return math_sin_handle(cx, args[0], args.rval());
|
||||
}
|
||||
|
||||
void
|
||||
js::math_sincos_uncached(double x, double *sin, double *cos)
|
||||
{
|
||||
#if defined(__GLIBC__)
|
||||
sincos(x, sin, cos);
|
||||
#elif defined(HAVE_SINCOS)
|
||||
__sincos(x, sin, cos);
|
||||
#else
|
||||
*sin = js::math_sin_uncached(x);
|
||||
*cos = js::math_cos_uncached(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
js::math_sincos_impl(MathCache* mathCache, double x, double *sin, double *cos)
|
||||
{
|
||||
unsigned indexSin;
|
||||
unsigned indexCos;
|
||||
bool hasSin = mathCache->isCached(x, MathCache::Sin, sin, &indexSin);
|
||||
bool hasCos = mathCache->isCached(x, MathCache::Cos, cos, &indexCos);
|
||||
if (!(hasSin || hasCos)) {
|
||||
js::math_sincos_uncached(x, sin, cos);
|
||||
mathCache->store(MathCache::Sin, x, *sin, indexSin);
|
||||
mathCache->store(MathCache::Cos, x, *cos, indexCos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasSin)
|
||||
*sin = js::math_sin_impl(mathCache, x);
|
||||
|
||||
if (!hasCos)
|
||||
*cos = js::math_cos_impl(mathCache, x);
|
||||
}
|
||||
|
||||
bool
|
||||
js::math_sqrt_handle(JSContext* cx, HandleValue number, MutableHandleValue result)
|
||||
{
|
||||
|
|
|
@ -80,6 +80,25 @@ class MathCache
|
|||
return e.out = f(x);
|
||||
}
|
||||
|
||||
bool isCached(double x, MathFuncId id, double *r, unsigned *index) {
|
||||
*index = hash(x, id);
|
||||
Entry& e = table[*index];
|
||||
if (e.in == x && e.id == id) {
|
||||
*r = e.out;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void store(MathFuncId id, double x, double v, unsigned index) {
|
||||
Entry &e = table[index];
|
||||
if (e.in == x && e.id == id)
|
||||
return;
|
||||
e.in = x;
|
||||
e.id = id;
|
||||
e.out = v;
|
||||
}
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
};
|
||||
|
||||
|
@ -149,6 +168,12 @@ extern bool
|
|||
minmax_impl(JSContext* cx, bool max, js::HandleValue a, js::HandleValue b,
|
||||
js::MutableHandleValue res);
|
||||
|
||||
extern void
|
||||
math_sincos_uncached(double x, double *sin, double *cos);
|
||||
|
||||
extern void
|
||||
math_sincos_impl(MathCache* mathCache, double x, double *sin, double *cos);
|
||||
|
||||
extern bool
|
||||
math_sqrt_handle(JSContext* cx, js::HandleValue number, js::MutableHandleValue result);
|
||||
|
||||
|
|
|
@ -5918,6 +5918,15 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op)
|
|||
return OptionFailure("ion-range-analysis", str);
|
||||
}
|
||||
|
||||
if (const char *str = op.getStringOption("ion-sincos")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableSincos = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::js_JitOptions.disableSincos = true;
|
||||
else
|
||||
return OptionFailure("ion-sincos", str);
|
||||
}
|
||||
|
||||
if (const char* str = op.getStringOption("ion-sink")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::js_JitOptions.disableSink = false;
|
||||
|
@ -6256,6 +6265,13 @@ main(int argc, char** argv, char** envp)
|
|||
"Find edge cases where Ion can avoid bailouts (default: on, off to disable)")
|
||||
|| !op.addStringOption('\0', "ion-range-analysis", "on/off",
|
||||
"Range analysis (default: on, off to disable)")
|
||||
#if defined(__APPLE__)
|
||||
|| !op.addStringOption('\0', "ion-sincos", "on/off",
|
||||
"Replace sin(x)/cos(x) to sincos(x) (default: on, off to disable)")
|
||||
#else
|
||||
|| !op.addStringOption('\0', "ion-sincos", "on/off",
|
||||
"Replace sin(x)/cos(x) to sincos(x) (default: off, on to enable)")
|
||||
#endif
|
||||
|| !op.addStringOption('\0', "ion-sink", "on/off",
|
||||
"Sink code motion (default: off, on to enable)")
|
||||
|| !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
_(AliasAnalysis) \
|
||||
_(GVN) \
|
||||
_(LICM) \
|
||||
_(Sincos) \
|
||||
_(RangeAnalysis) \
|
||||
_(LoopUnrolling) \
|
||||
_(EffectiveAddressAnalysis) \
|
||||
|
|
Загрузка…
Ссылка в новой задаче