mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 930414 - Instantiate module function defintions earlier as per the spec r=shu (c0f7f01c16) - Bug 819125 - Reset return value before executing catch/finally block. r=jandem (005fc7dc26) - Bug 1216623 - Part 2: In for (let ...) loops, evaluate initializers in the scope of the variables being initialized. r=Waldo. (a09e74569a) - Bug 1219877 - Allow let token with TOK_NAME in strict mode in Parser::shouldParseLetDeclaration. r=shu (d76dbab3d4) - Bug 932517 - Followup: remove let-block logic in sloppy mode ES6 let declaration check. (rs=evilpie) (c6e0c8661a) - Bug 1212305: Also mark lazy link frames, r=jandem (cd3601052c) - Bug 1216140 - Load the global lexical scope for the BINDGNAME Baseline IC when the script doesn't have a non-syntactic scope chain. (r=jandem) (370cccbd8a) - Bug 1144630 - Install class methods as non-enumerable on instances. (r=evilpie) (1158b1ffdb) - Bug 1218639 - IonMonkey: MIPS64: Add support into jit. r=nbp (f521201ec3) - Bug 1213740 - IonMonkey: MIPS64: Import Bailouts-mips64. r=nbp f=rankov (5e810a94d9) - Bug 1213741 - IonMonkey: MIPS64: Import BaselineCompiler-mips64. r=nbp f=rankov (c6eb2d72b1) - Bug 1213742 - IonMonkey: MIPS64: Import BaselineIC-mips64. r=nbp f=rankov (67b78d1345) - Bug 1213745 - IonMonkey: MIPS64: Import Lowering-mips64. r=nbp f=rankov (d1e60521f2) - Bug 1213749 - IonMonkey: MIPS64: Import SharedIC-mips64. r=nbp f=rankov (4a2184d535) - Bug 1213750 - IonMonkey: MIPS64: Import Trampoline-mips64. r=nbp f=rankov (382237c7db) - Bug 1213743 - IonMonkey: MIPS64: Import CodeGenerator-mips64. r=lth f=nbp,rankov (4f7d1720d6) - Bug 1213747 - IonMonkey: MIPS64: Import MoveEmitter-mips64. r=nbp f=rankov (63a53396e6) - Bug 1213751 - IonMonkey: MIPS64: Import Simulator-mips64. r=jandem (ac2f67fd43) - Bug 1218652 - IonMonkey: MIPS: Add mips-shared in check macroassembler style. r=arai (cf6f2bf5db) - Bug 1205134 - IonMonkey: MIPS: Split shareable code to mips-shared in MacroAssembler-mips32. r=lth f=nbp (3608ef227a) - Bug 1218652 - IonMonkey: MIPS: Move shared functions to mips-shared. r=arai (d716fa56bc) - Bug 1219137 - IonMonkey: MIPS: Fix PushRegsInMask and PopRegsInMaskIgnore can not shared. r=arai (13e5811e19) - Bug 1213746 - IonMonkey: MIPS64: Import MacroAssembler-mips64. r=lthf=rankov (3a7cb6fbd6) - Bug 1196545 - unboxed array snafus. r=jandem (31bbe773b2) - Bug 1224411 - Speed up FileRegistry._partial_paths by memoizing on the basis of directory. r=nalexander (d202d614a9) - Bug 1153790 - mac unification doesn't work with packaged addons. r=glandium (269cc3e37c) - Bug 1208320 - Support configuring zlib compression level; r=glandium (502de5e78a) - Bug 1163077 - Never elfhack if --disable-compile-environment is set. r=glandium (a484cb9a2f) - Bug 1213418 - Part 1: Use mozregression to download in |mach artifact|. r=me (93e1cb3aa2) - Bug 1213418 - Part 2: Use Fennec APKs in |mach artifact|; use mozversion to extract build IDs. r=me (27efbe8404) - Bug 1191051 - Use full 40 character hashes in |mach artifact|. r=gps (43992efbee) - Bug 1192064 - Add 'android-x86' job to |mach artifact|. r=jonalmeida (c0e7d6c8f8) - No bug - Don't write artifacts twice. r=me (e5eff7d6de) - Bug 1208808 - Move time zone adjustment information out of JSRuntime, into global state protected by a spinlock. r=till (20c6e76cc7) - Bug 1181612: Rename AsmJSFunctionLabels to make them easier to understand; r=luke (c6d88f944b) - Bug 1181612: Remove FunctionCompiler's RetType; r=luke (2f7483bfe2) - Bug 1214467 - Eliminate redundant loop header phis while emitting MIR for asm.js. r=luke (6c50f9b407) - Bug 1205390 - make Odin accept TypedArray constructors for shared memory. r=luke (400f892775) - Bug 1181612: Make asm.js internal calls thread-local; r=luke (aed88de095) - Bug 1219143 - IonMonkey: MIPS: Move callAndPushReturnAddress to mips-shared. r=lth (b9cd97f491) - Bug 1217326 - fork remaining atomics. r=h4writer. (65cc2a2b07)
This commit is contained in:
@@ -30,8 +30,8 @@ import sys
|
||||
from check_utils import get_all_toplevel_filenames
|
||||
|
||||
architecture_independent = set([ 'generic' ])
|
||||
all_architecture_names = set([ 'x86', 'x64', 'arm', 'arm64', 'mips32' ])
|
||||
all_shared_architecture_names = set([ 'x86_shared', 'arm', 'arm64', 'mips32' ])
|
||||
all_architecture_names = set([ 'x86', 'x64', 'arm', 'arm64', 'mips32', 'mips64' ])
|
||||
all_shared_architecture_names = set([ 'x86_shared', 'mips_shared', 'arm', 'arm64' ])
|
||||
|
||||
reBeforeArg = "(?<=[(,\s])"
|
||||
reArgType = "(?P<type>[\w\s:*&]+)"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "DateCacheCleaner.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "js/Date.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Hal.h"
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
void Notify(const SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo)
|
||||
{
|
||||
mozilla::AutoSafeJSContext cx;
|
||||
JS_ClearDateCaches(cx);
|
||||
JS::ResetTimeZone();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/*
|
||||
* InitializeDateCacheCleaner registers DateCacheCleaner to
|
||||
* SystemTimeChangeObserver. When time zone is changed, DateCacheCleaner calls
|
||||
* JS_ClearDateCaches to update the time zone information.
|
||||
* JS::ResetTimeZone to update the time zone information.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -39,6 +39,22 @@ struct JSContext;
|
||||
|
||||
namespace JS {
|
||||
|
||||
/**
|
||||
* Re-query the system to determine the current time zone adjustment from UTC,
|
||||
* including any component due to DST. If the time zone has changed, this will
|
||||
* cause all Date object non-UTC methods and formatting functions to produce
|
||||
* appropriately adjusted results.
|
||||
*
|
||||
* Left to its own devices, SpiderMonkey itself may occasionally call this
|
||||
* method to attempt to keep up with system time changes. However, no
|
||||
* particular frequency of checking is guaranteed. Embedders unable to accept
|
||||
* occasional inaccuracies should call this method in response to system time
|
||||
* changes, or immediately before operations requiring instantaneous
|
||||
* correctness, to guarantee correct behavior.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
ResetTimeZone();
|
||||
|
||||
class ClippedTime;
|
||||
inline ClippedTime TimeClip(double time);
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* SpiderMonkey initialization and shutdown APIs. */
|
||||
|
||||
#ifndef js_Initialization_h
|
||||
#define js_Initialization_h
|
||||
|
||||
#include "jstypes.h"
|
||||
|
||||
namespace JS {
|
||||
namespace detail {
|
||||
|
||||
enum class InitState { Uninitialized = 0, Running, ShutDown };
|
||||
|
||||
/**
|
||||
* SpiderMonkey's initialization status is tracked here, and it controls things
|
||||
* that should happen only once across all runtimes. It's an API requirement
|
||||
* that JS_Init (and JS_ShutDown, if called) be called in a thread-aware
|
||||
* manner, so this (internal -- embedders, don't use!) variable doesn't need to
|
||||
* be atomic.
|
||||
*/
|
||||
extern JS_PUBLIC_DATA(InitState)
|
||||
libraryInitState;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace JS
|
||||
|
||||
// These are equivalent to ICU's |UMemAllocFn|, |UMemReallocFn|, and
|
||||
// |UMemFreeFn| types. The first argument (called |context| in the ICU docs)
|
||||
// will always be nullptr and should be ignored.
|
||||
typedef void* (*JS_ICUAllocFn)(const void*, size_t size);
|
||||
typedef void* (*JS_ICUReallocFn)(const void*, void* p, size_t size);
|
||||
typedef void (*JS_ICUFreeFn)(const void*, void* p);
|
||||
|
||||
/**
|
||||
* This function can be used to track memory used by ICU. If it is called, it
|
||||
* *must* be called before JS_Init. Don't use it unless you know what you're
|
||||
* doing!
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
|
||||
JS_ICUReallocFn reallocFn,
|
||||
JS_ICUFreeFn freeFn);
|
||||
|
||||
/**
|
||||
* Initialize SpiderMonkey, returning true only if initialization succeeded.
|
||||
* Once this method has succeeded, it is safe to call JS_NewRuntime and other
|
||||
* JSAPI methods.
|
||||
*
|
||||
* This method must be called before any other JSAPI method is used on any
|
||||
* thread. Once it has been used, it is safe to call any JSAPI method, and it
|
||||
* remains safe to do so until JS_ShutDown is correctly called.
|
||||
*
|
||||
* It is currently not possible to initialize SpiderMonkey multiple times (that
|
||||
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
|
||||
* again). This restriction may eventually be lifted.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_Init(void);
|
||||
|
||||
/**
|
||||
* Destroy free-standing resources allocated by SpiderMonkey, not associated
|
||||
* with any runtime, context, or other structure.
|
||||
*
|
||||
* This method should be called after all other JSAPI data has been properly
|
||||
* cleaned up: every new runtime must have been destroyed, every new context
|
||||
* must have been destroyed, and so on. Calling this method before all other
|
||||
* resources have been destroyed has undefined behavior.
|
||||
*
|
||||
* Failure to call this method, at present, has no adverse effects other than
|
||||
* leaking memory. This may not always be the case; it's recommended that all
|
||||
* embedders call this method when all other JSAPI operations have completed.
|
||||
*
|
||||
* It is currently not possible to initialize SpiderMonkey multiple times (that
|
||||
* is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
|
||||
* again). This restriction may eventually be lifted.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_ShutDown(void);
|
||||
|
||||
#endif /* js_Initialization_h */
|
||||
@@ -115,51 +115,6 @@ class ModuleCompiler
|
||||
|
||||
/***************************************************** Mutable interface */
|
||||
|
||||
bool getOrCreateFunctionEntry(uint32_t funcIndex, Label** label)
|
||||
{
|
||||
return compileResults_->getOrCreateFunctionEntry(funcIndex, label);
|
||||
}
|
||||
|
||||
bool finishGeneratingFunction(AsmFunction& func, CodeGenerator& codegen,
|
||||
const AsmJSFunctionLabels& labels)
|
||||
{
|
||||
// If we have hit OOM then invariants which we assert below may not
|
||||
// hold, so abort now.
|
||||
if (masm().oom())
|
||||
return false;
|
||||
|
||||
// Code range
|
||||
unsigned line = func.lineno();
|
||||
unsigned column = func.column();
|
||||
PropertyName* funcName = func.name();
|
||||
if (!compileResults_->addCodeRange(AsmJSModule::FunctionCodeRange(funcName, line, labels)))
|
||||
return false;
|
||||
|
||||
// Script counts
|
||||
jit::IonScriptCounts* counts = codegen.extractScriptCounts();
|
||||
if (counts && !compileResults_->addFunctionCounts(counts)) {
|
||||
js_delete(counts);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Slow functions
|
||||
if (func.compileTime() >= 250) {
|
||||
ModuleCompileResults::SlowFunction sf(funcName, func.compileTime(), line, column);
|
||||
if (!compileResults_->slowFunctions().append(Move(sf)))
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
// Perf and profiling information
|
||||
unsigned begin = labels.begin.offset();
|
||||
unsigned end = labels.end.offset();
|
||||
AsmJSModule::ProfiledFunction profiledFunc(funcName, begin, end, line, column);
|
||||
if (!compileResults_->addProfiledFunction(profiledFunc))
|
||||
return false;
|
||||
#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
return true;
|
||||
}
|
||||
|
||||
void finish(ScopedJSDeletePtr<ModuleCompileResults>* results) {
|
||||
*results = compileResults_.forget();
|
||||
}
|
||||
@@ -191,7 +146,6 @@ class FunctionCompiler
|
||||
|
||||
ModuleCompiler & m_;
|
||||
LifoAlloc & lifo_;
|
||||
RetType retType_;
|
||||
|
||||
const AsmFunction & func_;
|
||||
size_t pc_;
|
||||
@@ -217,7 +171,6 @@ class FunctionCompiler
|
||||
FunctionCompiler(ModuleCompiler& m, const AsmFunction& func, LifoAlloc& lifo)
|
||||
: m_(m),
|
||||
lifo_(lifo),
|
||||
retType_(func.returnedType()),
|
||||
func_(func),
|
||||
pc_(0),
|
||||
alloc_(nullptr),
|
||||
@@ -227,10 +180,10 @@ class FunctionCompiler
|
||||
curBlock_(nullptr)
|
||||
{}
|
||||
|
||||
ModuleCompiler & m() const { return m_; }
|
||||
TempAllocator & alloc() const { return *alloc_; }
|
||||
LifoAlloc & lifo() const { return lifo_; }
|
||||
RetType returnedType() const { return retType_; }
|
||||
ModuleCompiler & m() const { return m_; }
|
||||
TempAllocator & alloc() const { return *alloc_; }
|
||||
LifoAlloc & lifo() const { return lifo_; }
|
||||
RetType returnedType() const { return func_.returnedType(); }
|
||||
|
||||
bool init()
|
||||
{
|
||||
@@ -803,7 +756,7 @@ class FunctionCompiler
|
||||
return true;
|
||||
}
|
||||
|
||||
CallSiteDesc::Kind kind = CallSiteDesc::Kind(-1); // initialize to silence GCC warning
|
||||
CallSiteDesc::Kind kind = CallSiteDesc::Kind(-1);
|
||||
switch (callee.which()) {
|
||||
case MAsmJSCall::Callee::Internal: kind = CallSiteDesc::Relative; break;
|
||||
case MAsmJSCall::Callee::Dynamic: kind = CallSiteDesc::Register; break;
|
||||
@@ -821,10 +774,10 @@ class FunctionCompiler
|
||||
}
|
||||
|
||||
public:
|
||||
bool internalCall(const Signature& sig, Label* entry, const Call& call, MDefinition** def)
|
||||
bool internalCall(const Signature& sig, uint32_t funcIndex, const Call& call, MDefinition** def)
|
||||
{
|
||||
MIRType returnType = sig.retType().toMIRType();
|
||||
return callPrivate(MAsmJSCall::Callee(entry), call, returnType, def);
|
||||
return callPrivate(MAsmJSCall::Callee(AsmJSInternalCallee(funcIndex)), call, returnType, def);
|
||||
}
|
||||
|
||||
bool funcPtrCall(const Signature& sig, uint32_t maskLit, uint32_t globalDataOffset, MDefinition* index,
|
||||
@@ -1038,6 +991,61 @@ class FunctionCompiler
|
||||
return pos;
|
||||
}
|
||||
|
||||
void fixupRedundantPhis(MBasicBlock* b)
|
||||
{
|
||||
for (size_t i = 0, depth = b->stackDepth(); i < depth; i++) {
|
||||
MDefinition* def = b->getSlot(i);
|
||||
if (def->isUnused())
|
||||
b->setSlot(i, def->toPhi()->getOperand(0));
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void fixupRedundantPhis(MBasicBlock* loopEntry, T& map)
|
||||
{
|
||||
if (!map.initialized())
|
||||
return;
|
||||
for (typename T::Enum e(map); !e.empty(); e.popFront()) {
|
||||
BlockVector& blocks = e.front().value();
|
||||
for (size_t i = 0; i < blocks.length(); i++) {
|
||||
if (blocks[i]->loopDepth() >= loopEntry->loopDepth())
|
||||
fixupRedundantPhis(blocks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool setLoopBackedge(MBasicBlock* loopEntry, MBasicBlock* backedge, MBasicBlock* afterLoop)
|
||||
{
|
||||
if (!loopEntry->setBackedgeAsmJS(backedge))
|
||||
return false;
|
||||
|
||||
// Flag all redundant phis as unused.
|
||||
for (MPhiIterator phi = loopEntry->phisBegin(); phi != loopEntry->phisEnd(); phi++) {
|
||||
MOZ_ASSERT(phi->numOperands() == 2);
|
||||
if (phi->getOperand(0) == phi->getOperand(1))
|
||||
phi->setUnused();
|
||||
}
|
||||
|
||||
// Fix up phis stored in the slots Vector of pending blocks.
|
||||
if (afterLoop)
|
||||
fixupRedundantPhis(afterLoop);
|
||||
fixupRedundantPhis(loopEntry, labeledContinues_);
|
||||
fixupRedundantPhis(loopEntry, labeledBreaks_);
|
||||
fixupRedundantPhis(loopEntry, unlabeledContinues_);
|
||||
fixupRedundantPhis(loopEntry, unlabeledBreaks_);
|
||||
|
||||
// Discard redundant phis and add to the free list.
|
||||
for (MPhiIterator phi = loopEntry->phisBegin(); phi != loopEntry->phisEnd(); ) {
|
||||
MPhi* entryDef = *phi++;
|
||||
if (!entryDef->isUnused())
|
||||
continue;
|
||||
|
||||
entryDef->justReplaceAllUsesWith(entryDef->getOperand(0));
|
||||
loopEntry->discardPhi(entryDef);
|
||||
mirGraph().addPhiToFreeList(entryDef);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
bool closeLoop(MBasicBlock* loopEntry, MBasicBlock* afterLoop)
|
||||
{
|
||||
@@ -1053,7 +1061,7 @@ class FunctionCompiler
|
||||
if (curBlock_) {
|
||||
MOZ_ASSERT(curBlock_->loopDepth() == loopStack_.length() + 1);
|
||||
curBlock_->end(MGoto::New(alloc(), loopEntry));
|
||||
if (!loopEntry->setBackedgeAsmJS(curBlock_))
|
||||
if (!setLoopBackedge(loopEntry, curBlock_, afterLoop))
|
||||
return false;
|
||||
}
|
||||
curBlock_ = afterLoop;
|
||||
@@ -1076,7 +1084,7 @@ class FunctionCompiler
|
||||
if (cond->isConstant()) {
|
||||
if (cond->toConstant()->valueToBoolean()) {
|
||||
curBlock_->end(MGoto::New(alloc(), loopEntry));
|
||||
if (!loopEntry->setBackedgeAsmJS(curBlock_))
|
||||
if (!setLoopBackedge(loopEntry, curBlock_, nullptr))
|
||||
return false;
|
||||
curBlock_ = nullptr;
|
||||
} else {
|
||||
@@ -1091,7 +1099,7 @@ class FunctionCompiler
|
||||
if (!newBlock(curBlock_, &afterLoop))
|
||||
return false;
|
||||
curBlock_->end(MTest::New(alloc(), cond, loopEntry, afterLoop));
|
||||
if (!loopEntry->setBackedgeAsmJS(curBlock_))
|
||||
if (!setLoopBackedge(loopEntry, curBlock_, afterLoop))
|
||||
return false;
|
||||
curBlock_ = afterLoop;
|
||||
}
|
||||
@@ -1696,13 +1704,7 @@ static bool
|
||||
EmitInternalCall(FunctionCompiler& f, RetType retType, MDefinition** def)
|
||||
{
|
||||
uint32_t funcIndex = f.readU32();
|
||||
|
||||
Label* entry;
|
||||
if (!f.m().getOrCreateFunctionEntry(funcIndex, &entry))
|
||||
return false;
|
||||
|
||||
const Signature& sig = *f.readSignature();
|
||||
|
||||
MOZ_ASSERT_IF(sig.retType() != RetType::Void, sig.retType() == retType);
|
||||
|
||||
uint32_t lineno, column;
|
||||
@@ -1712,7 +1714,7 @@ EmitInternalCall(FunctionCompiler& f, RetType retType, MDefinition** def)
|
||||
if (!EmitCallArgs(f, sig, &call))
|
||||
return false;
|
||||
|
||||
return f.internalCall(sig, entry, call, def);
|
||||
return f.internalCall(sig, funcIndex, call, def);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -3123,7 +3125,8 @@ js::GenerateAsmFunctionMIR(ModuleCompiler& m, LifoAlloc& lifo, AsmFunction& func
|
||||
}
|
||||
|
||||
bool
|
||||
js::GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, MIRGenerator& mir, LIRGraph& lir)
|
||||
js::GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, MIRGenerator& mir, LIRGraph& lir,
|
||||
FunctionCompileResults* results)
|
||||
{
|
||||
JitContext jitContext(m.runtime(), /* CompileCompartment = */ nullptr, &mir.alloc());
|
||||
|
||||
@@ -3140,18 +3143,19 @@ js::GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, MIRGenerator&
|
||||
if (!codegen)
|
||||
return false;
|
||||
|
||||
Label* funcEntry;
|
||||
if (!m.getOrCreateFunctionEntry(func.funcIndex(), &funcEntry))
|
||||
return false;
|
||||
|
||||
AsmJSFunctionLabels labels(*funcEntry, m.stackOverflowLabel());
|
||||
Label entry;
|
||||
AsmJSFunctionLabels labels(entry, m.stackOverflowLabel());
|
||||
if (!codegen->generateAsmJS(&labels))
|
||||
return false;
|
||||
|
||||
func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
|
||||
|
||||
if (!m.finishGeneratingFunction(func, *codegen, labels))
|
||||
return false;
|
||||
PropertyName* funcName = func.name();
|
||||
unsigned line = func.lineno();
|
||||
|
||||
// Fill in the results of the function's compilation
|
||||
AsmJSModule::FunctionCodeRange codeRange(funcName, line, labels);
|
||||
results->finishCodegen(func, codeRange, *codegen->extractScriptCounts());
|
||||
|
||||
// Unlike regular IonMonkey, which links and generates a new JitCode for
|
||||
// every function, we accumulate all the functions in the module in a
|
||||
|
||||
@@ -27,6 +27,7 @@ class AsmFunction;
|
||||
class LifoAlloc;
|
||||
class ModuleCompiler;
|
||||
class ModuleCompileResults;
|
||||
class FunctionCompileResults;
|
||||
|
||||
namespace jit {
|
||||
class LIRGraph;
|
||||
@@ -76,7 +77,8 @@ class MOZ_RAII AsmModuleCompilerScope
|
||||
|
||||
bool CreateAsmModuleCompiler(ModuleCompileInputs mci, AsmModuleCompilerScope* scope);
|
||||
bool GenerateAsmFunctionMIR(ModuleCompiler& m, LifoAlloc& lifo, AsmFunction& func, jit::MIRGenerator** mir);
|
||||
bool GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, jit::MIRGenerator& mir, jit::LIRGraph& lir);
|
||||
bool GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, jit::MIRGenerator& mir,
|
||||
jit::LIRGraph& lir, FunctionCompileResults* results);
|
||||
void FinishAsmModuleCompilation(ModuleCompiler& m, ScopedJSDeletePtr<ModuleCompileResults>* results);
|
||||
|
||||
} // namespace js
|
||||
|
||||
@@ -280,13 +280,13 @@ js::GenerateAsmJSFunctionPrologue(MacroAssembler& masm, unsigned framePushed,
|
||||
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
|
||||
GenerateProfilingPrologue(masm, framePushed, AsmJSExit::None, &labels->begin);
|
||||
GenerateProfilingPrologue(masm, framePushed, AsmJSExit::None, &labels->profilingEntry);
|
||||
Label body;
|
||||
masm.jump(&body);
|
||||
|
||||
// Generate normal prologue:
|
||||
masm.haltingAlign(CodeAlignment);
|
||||
masm.bind(&labels->entry);
|
||||
masm.bind(&labels->nonProfilingEntry);
|
||||
PushRetAddr(masm);
|
||||
masm.subFromStackPtr(Imm32(framePushed + AsmJSFrameBytesAfterReturnAddress));
|
||||
|
||||
|
||||
+26
-66
@@ -1046,43 +1046,39 @@ class AsmFunction
|
||||
}
|
||||
};
|
||||
|
||||
const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
|
||||
class FunctionCompileResults
|
||||
{
|
||||
const AsmFunction* func_;
|
||||
jit::IonScriptCounts* counts_;
|
||||
AsmJSModule::FunctionCodeRange codeRange_;
|
||||
|
||||
public:
|
||||
FunctionCompileResults()
|
||||
: func_(nullptr),
|
||||
counts_(nullptr),
|
||||
codeRange_()
|
||||
{}
|
||||
|
||||
const AsmFunction& func() const { MOZ_ASSERT(func_); return *func_; }
|
||||
const AsmJSModule::FunctionCodeRange& codeRange() const { return codeRange_; }
|
||||
jit::IonScriptCounts* counts() const { return counts_; }
|
||||
|
||||
void finishCodegen(AsmFunction& func, AsmJSModule::FunctionCodeRange codeRange,
|
||||
jit::IonScriptCounts& counts)
|
||||
{
|
||||
func_ = &func;
|
||||
codeRange_ = codeRange;
|
||||
counts_ = &counts;
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleCompileResults
|
||||
{
|
||||
public:
|
||||
struct SlowFunction
|
||||
{
|
||||
SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
|
||||
: name(name), ms(ms), line(line), column(column)
|
||||
{}
|
||||
|
||||
PropertyName* name;
|
||||
unsigned ms;
|
||||
unsigned line;
|
||||
unsigned column;
|
||||
};
|
||||
|
||||
typedef Vector<SlowFunction , 0, SystemAllocPolicy> SlowFunctionVector;
|
||||
typedef Vector<jit::Label* , 8, SystemAllocPolicy> LabelVector;
|
||||
typedef Vector<AsmJSModule::FunctionCodeRange, 8, SystemAllocPolicy> FunctionCodeRangeVector;
|
||||
typedef Vector<jit::IonScriptCounts* , 0, SystemAllocPolicy> ScriptCountVector;
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
typedef Vector<AsmJSModule::ProfiledFunction , 0, SystemAllocPolicy> ProfiledFunctionVector;
|
||||
#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
|
||||
private:
|
||||
LifoAlloc lifo_;
|
||||
jit::MacroAssembler masm_;
|
||||
|
||||
SlowFunctionVector slowFunctions_;
|
||||
LabelVector functionEntries_;
|
||||
FunctionCodeRangeVector codeRanges_;
|
||||
ScriptCountVector functionCounts_;
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
ProfiledFunctionVector profiledFunctions_;
|
||||
#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
|
||||
jit::NonAssertingLabel stackOverflowLabel_;
|
||||
jit::NonAssertingLabel asyncInterruptLabel_;
|
||||
jit::NonAssertingLabel syncInterruptLabel_;
|
||||
@@ -1093,8 +1089,7 @@ class ModuleCompileResults
|
||||
|
||||
public:
|
||||
ModuleCompileResults()
|
||||
: lifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
|
||||
masm_(jit::MacroAssembler::AsmJSToken()),
|
||||
: masm_(jit::MacroAssembler::AsmJSToken()),
|
||||
usecBefore_(PRMJ_Now())
|
||||
{}
|
||||
|
||||
@@ -1106,41 +1101,6 @@ class ModuleCompileResults
|
||||
jit::Label& onDetachedLabel() { return onDetachedLabel_; }
|
||||
jit::Label& onConversionErrorLabel() { return onConversionErrorLabel_; }
|
||||
int64_t usecBefore() { return usecBefore_; }
|
||||
|
||||
SlowFunctionVector& slowFunctions() { return slowFunctions_; }
|
||||
|
||||
size_t numFunctionEntries() const { return functionEntries_.length(); }
|
||||
jit::Label* functionEntry(unsigned i) { return functionEntries_[i]; }
|
||||
|
||||
bool getOrCreateFunctionEntry(unsigned i, jit::Label** label) {
|
||||
if (i == UINT32_MAX)
|
||||
return false;
|
||||
while (functionEntries_.length() <= i) {
|
||||
jit::Label* newEntry = lifo_.new_<jit::Label>();
|
||||
if (!newEntry || !functionEntries_.append(newEntry))
|
||||
return false;
|
||||
}
|
||||
*label = functionEntries_[i];
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t numCodeRanges() const { return codeRanges_.length(); }
|
||||
bool addCodeRange(AsmJSModule::FunctionCodeRange range) { return codeRanges_.append(range); }
|
||||
AsmJSModule::FunctionCodeRange& codeRange(unsigned i) { return codeRanges_[i]; }
|
||||
|
||||
size_t numFunctionCounts() const { return functionCounts_.length(); }
|
||||
bool addFunctionCounts(jit::IonScriptCounts* counts) { return functionCounts_.append(counts); }
|
||||
jit::IonScriptCounts* functionCount(unsigned i) { return functionCounts_[i]; }
|
||||
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
size_t numProfiledFunctions() const { return profiledFunctions_.length(); }
|
||||
bool addProfiledFunction(AsmJSModule::ProfiledFunction func) {
|
||||
return profiledFunctions_.append(func);
|
||||
}
|
||||
AsmJSModule::ProfiledFunction& profiledFunction(unsigned i) {
|
||||
return profiledFunctions_[i];
|
||||
}
|
||||
#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
@@ -230,7 +230,7 @@ ValidateArrayView(JSContext* cx, AsmJSModule::Global& global, HandleValue global
|
||||
|
||||
bool tac = IsTypedArrayConstructor(v, global.viewType());
|
||||
bool stac = IsSharedTypedArrayConstructor(v, global.viewType());
|
||||
if (!((tac || stac) && stac == isShared))
|
||||
if (!(tac || (stac && isShared)))
|
||||
return LinkFail(cx, "bad typed array constructor");
|
||||
|
||||
return true;
|
||||
|
||||
@@ -322,7 +322,9 @@ AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembl
|
||||
heapAccesses_ = masm.extractAsmJSHeapAccesses();
|
||||
|
||||
// Call-site metadata used for stack unwinding.
|
||||
callSites_ = masm.extractCallSites();
|
||||
const CallSiteAndTargetVector& callSites = masm.callSites();
|
||||
if (!callSites_.appendAll(callSites))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0);
|
||||
|
||||
@@ -1330,19 +1332,19 @@ AsmJSModule::CodeRange::CodeRange(uint32_t nameIndex, uint32_t lineNumber,
|
||||
const AsmJSFunctionLabels& l)
|
||||
: nameIndex_(nameIndex),
|
||||
lineNumber_(lineNumber),
|
||||
begin_(l.begin.offset()),
|
||||
begin_(l.profilingEntry.offset()),
|
||||
profilingReturn_(l.profilingReturn.offset()),
|
||||
end_(l.end.offset())
|
||||
end_(l.endAfterOOL.offset())
|
||||
{
|
||||
PodZero(&u); // zero padding for Valgrind
|
||||
u.kind_ = Function;
|
||||
setDeltas(l.entry.offset(), l.profilingJump.offset(), l.profilingEpilogue.offset());
|
||||
setDeltas(l.nonProfilingEntry.offset(), l.profilingJump.offset(), l.profilingEpilogue.offset());
|
||||
|
||||
MOZ_ASSERT(l.begin.offset() < l.entry.offset());
|
||||
MOZ_ASSERT(l.entry.offset() < l.profilingJump.offset());
|
||||
MOZ_ASSERT(l.profilingEntry.offset() < l.nonProfilingEntry.offset());
|
||||
MOZ_ASSERT(l.nonProfilingEntry.offset() < l.profilingJump.offset());
|
||||
MOZ_ASSERT(l.profilingJump.offset() < l.profilingEpilogue.offset());
|
||||
MOZ_ASSERT(l.profilingEpilogue.offset() < l.profilingReturn.offset());
|
||||
MOZ_ASSERT(l.profilingReturn.offset() < l.end.offset());
|
||||
MOZ_ASSERT(l.profilingReturn.offset() < l.endAfterOOL.offset());
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -102,16 +102,20 @@ enum AsmJSSimdOperation
|
||||
struct MOZ_STACK_CLASS AsmJSFunctionLabels
|
||||
{
|
||||
AsmJSFunctionLabels(jit::Label& entry, jit::Label& overflowExit)
|
||||
: entry(entry), overflowExit(overflowExit) {}
|
||||
: nonProfilingEntry(entry), overflowExit(overflowExit) {}
|
||||
|
||||
jit::Label begin;
|
||||
jit::Label& entry;
|
||||
jit::Label profilingJump;
|
||||
jit::Label profilingEpilogue;
|
||||
jit::Label profilingReturn;
|
||||
jit::Label end;
|
||||
jit::Label profilingEntry;
|
||||
jit::Label& nonProfilingEntry;
|
||||
jit::Label profilingJump;
|
||||
jit::Label profilingEpilogue;
|
||||
jit::Label profilingReturn;
|
||||
jit::Label endAfterOOL;
|
||||
mozilla::Maybe<jit::Label> overflowThunk;
|
||||
jit::Label& overflowExit;
|
||||
|
||||
private:
|
||||
AsmJSFunctionLabels(const AsmJSFunctionLabels&) = delete;
|
||||
AsmJSFunctionLabels& operator=(const AsmJSFunctionLabels&) = delete;
|
||||
};
|
||||
|
||||
// Represents the type and value of an asm.js numeric literal.
|
||||
@@ -314,6 +318,10 @@ class AsmJSModule
|
||||
MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == SharedArrayView || pod.which_ == ArrayViewCtor);
|
||||
return pod.u.viewType_;
|
||||
}
|
||||
void makeViewShared() {
|
||||
MOZ_ASSERT(pod.which_ == ArrayView);
|
||||
pod.which_ = SharedArrayView;
|
||||
}
|
||||
PropertyName* mathName() const {
|
||||
MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
|
||||
return name_;
|
||||
@@ -627,6 +635,10 @@ class AsmJSModule
|
||||
PropertyName* name_;
|
||||
|
||||
public:
|
||||
FunctionCodeRange()
|
||||
: CodeRange(), name_(nullptr)
|
||||
{}
|
||||
|
||||
FunctionCodeRange(PropertyName* name, uint32_t lineNumber, const AsmJSFunctionLabels& l)
|
||||
: CodeRange(UINT32_MAX, lineNumber, l), name_(name)
|
||||
{}
|
||||
@@ -1097,6 +1109,15 @@ class AsmJSModule
|
||||
return pod.isSharedView_ == shared;
|
||||
return !pod.isSharedView_ || shared;
|
||||
}
|
||||
void setViewsAreShared() {
|
||||
if (pod.hasArrayView_)
|
||||
pod.isSharedView_ = true;
|
||||
for (size_t i=0 ; i < globals_.length() ; i++) {
|
||||
Global& g = globals_[i];
|
||||
if (g.which() == Global::ArrayView)
|
||||
g.makeViewShared();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -1146,14 +1167,14 @@ class AsmJSModule
|
||||
bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t pret, uint32_t end) {
|
||||
return codeRanges_.append(CodeRange(kind, begin, pret, end));
|
||||
}
|
||||
bool addFunctionCodeRange(PropertyName* name, FunctionCodeRange&& codeRange)
|
||||
bool addFunctionCodeRange(PropertyName* name, FunctionCodeRange codeRange)
|
||||
{
|
||||
MOZ_ASSERT(!isFinished());
|
||||
MOZ_ASSERT(name->isTenured());
|
||||
if (names_.length() >= UINT32_MAX)
|
||||
return false;
|
||||
codeRange.initNameIndex(names_.length());
|
||||
return names_.append(name) && codeRanges_.append(Move(codeRange));
|
||||
return names_.append(name) && codeRanges_.append(codeRange);
|
||||
}
|
||||
bool addBuiltinThunkCodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
|
||||
uint32_t profilingReturn, uint32_t end)
|
||||
|
||||
+102
-33
@@ -584,6 +584,8 @@ TypedArrayLoadType(Scalar::Type viewType)
|
||||
MOZ_CRASH("Unexpected array type");
|
||||
}
|
||||
|
||||
const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
|
||||
|
||||
namespace {
|
||||
|
||||
// The ModuleValidator encapsulates the entire validation of an asm.js module.
|
||||
@@ -773,6 +775,10 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
MOZ_ASSERT(isAnyArrayView());
|
||||
return u.viewInfo.isSharedView_;
|
||||
}
|
||||
void setViewIsSharedView() {
|
||||
MOZ_ASSERT(isAnyArrayView());
|
||||
u.viewInfo.isSharedView_ = true;
|
||||
}
|
||||
bool isMathFunction() const {
|
||||
return which_ == MathBuiltinFunction;
|
||||
}
|
||||
@@ -881,12 +887,25 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
}
|
||||
};
|
||||
|
||||
struct SlowFunction
|
||||
{
|
||||
SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
|
||||
: name(name), ms(ms), line(line), column(column)
|
||||
{}
|
||||
|
||||
PropertyName* name;
|
||||
unsigned ms;
|
||||
unsigned line;
|
||||
unsigned column;
|
||||
};
|
||||
|
||||
private:
|
||||
typedef HashMap<PropertyName*, Global*> GlobalMap;
|
||||
typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
|
||||
typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
|
||||
typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap;
|
||||
typedef Vector<ArrayView> ArrayViewVector;
|
||||
typedef Vector<SlowFunction> SlowFunctionVector;
|
||||
|
||||
public:
|
||||
typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
|
||||
@@ -918,6 +937,10 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
bool canValidateChangeHeap_;
|
||||
bool hasChangeHeap_;
|
||||
bool supportsSimd_;
|
||||
bool atomicsPresent_;
|
||||
|
||||
Vector<uint32_t> functionEntryOffsets_;
|
||||
SlowFunctionVector slowFunctions_;
|
||||
|
||||
ScopedJSDeletePtr<ModuleCompileResults> compileResults_;
|
||||
DebugOnly<bool> finishedFunctionBodies_;
|
||||
@@ -943,6 +966,9 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
canValidateChangeHeap_(false),
|
||||
hasChangeHeap_(false),
|
||||
supportsSimd_(cx->jitSupportsSimd()),
|
||||
atomicsPresent_(false),
|
||||
functionEntryOffsets_(cx),
|
||||
slowFunctions_(cx),
|
||||
compileResults_(nullptr),
|
||||
finishedFunctionBodies_(false)
|
||||
{
|
||||
@@ -1155,6 +1181,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
Global* global = moduleLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
|
||||
if (!global)
|
||||
return false;
|
||||
atomicsPresent_ = true;
|
||||
global->u.atomicsBuiltinFunc_ = func;
|
||||
return globals_.putNew(varName, global);
|
||||
}
|
||||
@@ -1392,6 +1419,9 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
FuncPtrTable& funcPtrTable(unsigned i) const {
|
||||
return *funcPtrTables_[i];
|
||||
}
|
||||
uint32_t functionEntryOffset(unsigned i) {
|
||||
return functionEntryOffsets_[i];
|
||||
}
|
||||
|
||||
const Global* lookupGlobal(PropertyName* name) const {
|
||||
if (GlobalMap::Ptr p = globals_.lookup(name))
|
||||
@@ -1438,9 +1468,43 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
Label& onDetachedLabel() { return compileResults_->onDetachedLabel(); }
|
||||
Label& onOutOfBoundsLabel() { return compileResults_->onOutOfBoundsLabel(); }
|
||||
Label& onConversionErrorLabel() { return compileResults_->onConversionErrorLabel(); }
|
||||
Label* functionEntry(unsigned i) { return compileResults_->functionEntry(i); }
|
||||
ExitMap::Range allExits() const { return exits_.all(); }
|
||||
|
||||
bool finishGeneratingFunction(FunctionCompileResults& results) {
|
||||
const AsmFunction& func = results.func();
|
||||
unsigned i = func.funcIndex();
|
||||
if (functionEntryOffsets_.length() <= i && !functionEntryOffsets_.resize(i + 1))
|
||||
return false;
|
||||
|
||||
AsmJSModule::FunctionCodeRange codeRange = results.codeRange();
|
||||
functionEntryOffsets_[i] = codeRange.entry();
|
||||
|
||||
PropertyName* funcName = func.name();
|
||||
unsigned line = func.lineno();
|
||||
unsigned column = func.column();
|
||||
|
||||
// These must be done before the module is done with function bodies.
|
||||
if (results.counts() && !module().addFunctionCounts(results.counts()))
|
||||
return false;
|
||||
if (!module().addFunctionCodeRange(codeRange.name(), codeRange))
|
||||
return false;
|
||||
|
||||
unsigned compileTime = func.compileTime();
|
||||
if (compileTime >= 250) {
|
||||
if (!slowFunctions_.append(SlowFunction(funcName, compileTime, line, column)))
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
// Perf and profiling information
|
||||
AsmJSModule::ProfiledFunction pf(funcName, codeRange.entry(), codeRange.end(), line, column);
|
||||
if (!module().addProfiledFunction(Move(pf)))
|
||||
return false;
|
||||
#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool finishGeneratingEntry(unsigned exportIndex, Label* begin) {
|
||||
MOZ_ASSERT(finishedFunctionBodies_);
|
||||
module_->exportedFunction(exportIndex).initCodeOffset(begin->offset());
|
||||
@@ -1497,8 +1561,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) {
|
||||
AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::RawPointer);
|
||||
link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*);
|
||||
Label* entry = functionEntry(table.elem(elemIndex).funcIndex());
|
||||
link.targetOffset = entry->offset();
|
||||
link.targetOffset = functionEntryOffset(table.elem(elemIndex).funcIndex());
|
||||
if (!module_->addRelativeLink(link))
|
||||
return false;
|
||||
}
|
||||
@@ -1509,29 +1572,28 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
}
|
||||
|
||||
void startFunctionBodies() {
|
||||
if (atomicsPresent_) {
|
||||
for (GlobalMap::Range r = globals_.all() ; !r.empty() ; r.popFront()) {
|
||||
Global* g = r.front().value();
|
||||
if (g->isAnyArrayView())
|
||||
g->setViewIsSharedView();
|
||||
}
|
||||
module_->setViewsAreShared();
|
||||
}
|
||||
module_->startFunctionBodies();
|
||||
}
|
||||
bool finishFunctionBodies(ScopedJSDeletePtr<ModuleCompileResults>* compileResults) {
|
||||
// Take ownership of compilation results
|
||||
compileResults_ = compileResults->forget();
|
||||
|
||||
// These must be done before the module is done with function bodies.
|
||||
for (size_t i = 0; i < compileResults_->numFunctionCounts(); ++i) {
|
||||
if (!module().addFunctionCounts(compileResults_->functionCount(i)))
|
||||
return false;
|
||||
}
|
||||
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
for (size_t i = 0; i < compileResults_->numProfiledFunctions(); ++i) {
|
||||
if (!module().addProfiledFunction(Move(compileResults_->profiledFunction(i))))
|
||||
return false;
|
||||
}
|
||||
#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
|
||||
|
||||
// Hand in code ranges, script counts and perf profiling data to the AsmJSModule
|
||||
for (size_t i = 0; i < compileResults_->numCodeRanges(); ++i) {
|
||||
AsmJSModule::FunctionCodeRange& codeRange = compileResults_->codeRange(i);
|
||||
if (!module().addFunctionCodeRange(codeRange.name(), Move(codeRange)))
|
||||
return false;
|
||||
// Patch internal calls to their final positions
|
||||
for (auto& cs : masm().callSites()) {
|
||||
if (!cs.isInternal())
|
||||
continue;
|
||||
MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
|
||||
uint32_t callerOffset = cs.returnAddressOffset();
|
||||
uint32_t calleeOffset = functionEntryOffset(cs.targetIndex());
|
||||
masm().patchCall(callerOffset, calleeOffset);
|
||||
}
|
||||
|
||||
// When an interrupt is triggered, all function code is mprotected and,
|
||||
@@ -1553,19 +1615,18 @@ class MOZ_STACK_CLASS ModuleValidator
|
||||
ScopedJSFreePtr<char> slowFuns;
|
||||
int64_t usecAfter = PRMJ_Now();
|
||||
int msTotal = (usecAfter - compileResults_->usecBefore()) / PRMJ_USEC_PER_MSEC;
|
||||
ModuleCompileResults::SlowFunctionVector& slowFunctions = compileResults_->slowFunctions();
|
||||
if (!slowFunctions.empty()) {
|
||||
slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions.length()));
|
||||
if (!slowFunctions_.empty()) {
|
||||
slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
|
||||
if (!slowFuns)
|
||||
return;
|
||||
for (unsigned i = 0; i < slowFunctions.length(); i++) {
|
||||
ModuleCompileResults::SlowFunction& func = slowFunctions[i];
|
||||
for (unsigned i = 0; i < slowFunctions_.length(); i++) {
|
||||
ModuleValidator::SlowFunction& func = slowFunctions_[i];
|
||||
JSAutoByteString name;
|
||||
if (!AtomToPrintableString(cx_, func.name, &name))
|
||||
return;
|
||||
slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(),
|
||||
name.ptr(), func.line, func.column, func.ms,
|
||||
i+1 < slowFunctions.length() ? ", " : ""));
|
||||
i+1 < slowFunctions_.length() ? ", " : ""));
|
||||
if (!slowFuns)
|
||||
return;
|
||||
}
|
||||
@@ -6520,7 +6581,10 @@ CheckFunctionsSequential(ModuleValidator& m, ScopedJSDeletePtr<ModuleCompileResu
|
||||
func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
if (!GenerateAsmFunctionCode(mc, *func, *mir, *lir))
|
||||
FunctionCompileResults results;
|
||||
if (!GenerateAsmFunctionCode(mc, *func, *mir, *lir, &results))
|
||||
return false;
|
||||
if (!m.finishGeneratingFunction(results))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6601,10 +6665,10 @@ GetFinishedCompilation(ModuleCompiler& m, ParallelGroupState& group)
|
||||
}
|
||||
|
||||
static bool
|
||||
GetUsedTask(ModuleCompiler& m, ParallelGroupState& group, AsmJSParallelTask** outTask)
|
||||
GetUsedTask(ModuleValidator& m, ModuleCompiler& mc, ParallelGroupState& group, AsmJSParallelTask** outTask)
|
||||
{
|
||||
// Block until a used LifoAlloc becomes available.
|
||||
AsmJSParallelTask* task = GetFinishedCompilation(m, group);
|
||||
AsmJSParallelTask* task = GetFinishedCompilation(mc, group);
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
@@ -6612,7 +6676,10 @@ GetUsedTask(ModuleCompiler& m, ParallelGroupState& group, AsmJSParallelTask** ou
|
||||
func.accumulateCompileTime(task->compileTime);
|
||||
|
||||
// Perform code generation on the main thread.
|
||||
if (!GenerateAsmFunctionCode(m, func, *task->mir, *task->lir))
|
||||
FunctionCompileResults results;
|
||||
if (!GenerateAsmFunctionCode(mc, func, *task->mir, *task->lir, &results))
|
||||
return false;
|
||||
if (!m.finishGeneratingFunction(results))
|
||||
return false;
|
||||
|
||||
group.compiledJobs++;
|
||||
@@ -6664,7 +6731,7 @@ CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
|
||||
if (tk != TOK_FUNCTION)
|
||||
break;
|
||||
|
||||
if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(mc, group, &task))
|
||||
if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(m, mc, group, &task))
|
||||
return false;
|
||||
|
||||
AsmFunction* func;
|
||||
@@ -6692,7 +6759,7 @@ CheckFunctionsParallel(ModuleValidator& m, ParallelGroupState& group,
|
||||
// Block for all outstanding helpers to complete.
|
||||
while (group.outstandingJobs > 0) {
|
||||
AsmJSParallelTask* ignored = nullptr;
|
||||
if (!GetUsedTask(mc, group, &ignored))
|
||||
if (!GetUsedTask(m, mc, group, &ignored))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7162,7 +7229,9 @@ GenerateEntry(ModuleValidator& m, unsigned exportIndex)
|
||||
|
||||
// Call into the real function.
|
||||
masm.assertStackAlignment(AsmJSStackAlignment);
|
||||
masm.call(CallSiteDesc(CallSiteDesc::Relative), m.functionEntry(func.funcIndex()));
|
||||
Label funcLabel;
|
||||
funcLabel.bind(m.functionEntryOffset(func.funcIndex()));
|
||||
masm.call(CallSiteDesc(CallSiteDesc::Relative), &funcLabel);
|
||||
|
||||
// Recover the stack pointer value before dynamic alignment.
|
||||
masm.loadAsmJSActivation(scratch);
|
||||
|
||||
@@ -234,6 +234,9 @@ function ModuleDeclarationInstantiation()
|
||||
CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 16.iv
|
||||
InstantiateModuleFunctionDeclarations(module);
|
||||
}
|
||||
|
||||
// 15.2.1.16.5 ModuleEvaluation()
|
||||
|
||||
@@ -490,6 +490,19 @@ ModuleNamespaceObject::ProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// FunctionDeclaration
|
||||
|
||||
FunctionDeclaration::FunctionDeclaration(HandleAtom name, HandleFunction fun)
|
||||
: name(name), fun(fun)
|
||||
{}
|
||||
|
||||
void FunctionDeclaration::trace(JSTracer* trc)
|
||||
{
|
||||
TraceEdge(trc, &name, "FunctionDeclaration name");
|
||||
TraceEdge(trc, &fun, "FunctionDeclaration fun");
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// ModuleObject
|
||||
|
||||
@@ -548,6 +561,12 @@ ModuleObject::create(ExclusiveContext* cx, HandleObject enclosingStaticScope)
|
||||
}
|
||||
|
||||
self->initReservedSlot(ImportBindingsSlot, PrivateValue(bindings));
|
||||
|
||||
FunctionDeclarationVector* funDecls = cx->new_<FunctionDeclarationVector>(cx);
|
||||
if (!funDecls)
|
||||
return nullptr;
|
||||
|
||||
self->initReservedSlot(FunctionDeclarationsSlot, PrivateValue(funDecls));
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -559,6 +578,8 @@ ModuleObject::finalize(js::FreeOp* fop, JSObject* obj)
|
||||
fop->delete_(&self->importBindings());
|
||||
if (IndirectBindingMap* bindings = self->namespaceBindings())
|
||||
fop->delete_(bindings);
|
||||
if (FunctionDeclarationVector* funDecls = self->functionDeclarations())
|
||||
fop->delete_(funDecls);
|
||||
}
|
||||
|
||||
ModuleEnvironmentObject*
|
||||
@@ -606,6 +627,16 @@ ModuleObject::namespace_()
|
||||
return &value.toObject().as<ModuleNamespaceObject>();
|
||||
}
|
||||
|
||||
FunctionDeclarationVector*
|
||||
ModuleObject::functionDeclarations()
|
||||
{
|
||||
Value value = getReservedSlot(FunctionDeclarationsSlot);
|
||||
if (value.isUndefined())
|
||||
return nullptr;
|
||||
|
||||
return static_cast<FunctionDeclarationVector*>(value.toPrivate());
|
||||
}
|
||||
|
||||
void
|
||||
ModuleObject::init(HandleScript script)
|
||||
{
|
||||
@@ -691,6 +722,9 @@ ModuleObject::trace(JSTracer* trc, JSObject* obj)
|
||||
TraceBindings(trc, module.importBindings());
|
||||
if (IndirectBindingMap* bindings = module.namespaceBindings())
|
||||
TraceBindings(trc, *bindings);
|
||||
|
||||
if (FunctionDeclarationVector* funDecls = module.functionDeclarations())
|
||||
funDecls->trace(trc);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -703,6 +737,42 @@ ModuleObject::createEnvironment()
|
||||
setReservedSlot(EnvironmentSlot, getReservedSlot(InitialEnvironmentSlot));
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleObject::noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun)
|
||||
{
|
||||
FunctionDeclarationVector* funDecls = functionDeclarations();
|
||||
return funDecls->emplaceBack(name, fun);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self)
|
||||
{
|
||||
FunctionDeclarationVector* funDecls = self->functionDeclarations();
|
||||
if (!funDecls) {
|
||||
JS_ReportError(cx, "Module function declarations have already been instantiated");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedModuleEnvironmentObject env(cx, &self->initialEnvironment());
|
||||
RootedFunction fun(cx);
|
||||
RootedValue value(cx);
|
||||
|
||||
for (const auto& funDecl : *funDecls) {
|
||||
fun = funDecl.fun;
|
||||
RootedObject obj(cx, Lambda(cx, fun, env));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
value = ObjectValue(*fun);
|
||||
if (!SetProperty(cx, env, funDecl.name->asPropertyName(), value))
|
||||
return false;
|
||||
}
|
||||
|
||||
js_delete(funDecls);
|
||||
self->setReservedSlot(FunctionDeclarationsSlot, UndefinedValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ModuleObject::setEvaluated()
|
||||
{
|
||||
|
||||
@@ -145,6 +145,17 @@ class ModuleNamespaceObject : public ProxyObject
|
||||
typedef Rooted<ModuleNamespaceObject*> RootedModuleNamespaceObject;
|
||||
typedef Handle<ModuleNamespaceObject*> HandleModuleNamespaceObject;
|
||||
|
||||
struct FunctionDeclaration
|
||||
{
|
||||
FunctionDeclaration(HandleAtom name, HandleFunction fun);
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
RelocatablePtrAtom name;
|
||||
RelocatablePtrFunction fun;
|
||||
};
|
||||
|
||||
using FunctionDeclarationVector = TraceableVector<FunctionDeclaration>;
|
||||
|
||||
class ModuleObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
@@ -164,6 +175,7 @@ class ModuleObject : public NativeObject
|
||||
ImportBindingsSlot,
|
||||
NamespaceExportsSlot,
|
||||
NamespaceBindingsSlot,
|
||||
FunctionDeclarationsSlot,
|
||||
SlotCount
|
||||
};
|
||||
|
||||
@@ -197,17 +209,21 @@ class ModuleObject : public NativeObject
|
||||
|
||||
void createEnvironment();
|
||||
|
||||
bool noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun);
|
||||
static bool instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self);
|
||||
|
||||
void setEvaluated();
|
||||
static bool evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval);
|
||||
|
||||
static ModuleNamespaceObject* createNamespace(JSContext* cx, HandleModuleObject self,
|
||||
HandleArrayObject exports);
|
||||
HandleArrayObject exports);
|
||||
|
||||
private:
|
||||
static void trace(JSTracer* trc, JSObject* obj);
|
||||
static void finalize(js::FreeOp* fop, JSObject* obj);
|
||||
|
||||
bool hasScript() const;
|
||||
FunctionDeclarationVector* functionDeclarations();
|
||||
};
|
||||
|
||||
// Process a module's parse tree to collate the import and export data used when
|
||||
|
||||
@@ -2491,8 +2491,9 @@ ASTSerializer::forInit(ParseNode* pn, MutableHandleValue dst)
|
||||
return true;
|
||||
}
|
||||
|
||||
return (pn->isKind(PNK_VAR))
|
||||
? variableDeclaration(pn, false, dst)
|
||||
bool lexical = pn->isKind(PNK_LET) || pn->isKind(PNK_CONST);
|
||||
return (lexical || pn->isKind(PNK_VAR))
|
||||
? variableDeclaration(pn, lexical, dst)
|
||||
: expression(pn, dst);
|
||||
}
|
||||
|
||||
@@ -2641,32 +2642,31 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
|
||||
if (!statement(pn->pn_right, &stmt))
|
||||
return false;
|
||||
|
||||
if (head->isKind(PNK_FORIN)) {
|
||||
if (head->isKind(PNK_FORIN) || head->isKind(PNK_FOROF)) {
|
||||
RootedValue var(cx);
|
||||
return (!head->pn_kid1
|
||||
? pattern(head->pn_kid2, &var)
|
||||
: head->pn_kid1->isKind(PNK_LEXICALSCOPE)
|
||||
? variableDeclaration(head->pn_kid1->pn_expr, true, &var)
|
||||
: variableDeclaration(head->pn_kid1, false, &var)) &&
|
||||
forIn(pn, head, var, stmt, dst);
|
||||
}
|
||||
|
||||
if (head->isKind(PNK_FOROF)) {
|
||||
RootedValue var(cx);
|
||||
return (!head->pn_kid1
|
||||
? pattern(head->pn_kid2, &var)
|
||||
: head->pn_kid1->isKind(PNK_LEXICALSCOPE)
|
||||
? variableDeclaration(head->pn_kid1->pn_expr, true, &var)
|
||||
: variableDeclaration(head->pn_kid1, false, &var)) &&
|
||||
forOf(pn, head, var, stmt, dst);
|
||||
if (!head->pn_kid1) {
|
||||
if (!pattern(head->pn_kid2, &var))
|
||||
return false;
|
||||
} else if (head->pn_kid1->isKind(PNK_LEXICALSCOPE)) {
|
||||
if (!variableDeclaration(head->pn_kid1->pn_expr, true, &var))
|
||||
return false;
|
||||
} else {
|
||||
if (!variableDeclaration(head->pn_kid1,
|
||||
head->pn_kid1->isKind(PNK_LET) ||
|
||||
head->pn_kid1->isKind(PNK_CONST),
|
||||
&var))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (head->isKind(PNK_FORIN))
|
||||
return forIn(pn, head, var, stmt, dst);
|
||||
return forOf(pn, head, var, stmt, dst);
|
||||
}
|
||||
|
||||
RootedValue init(cx), test(cx), update(cx);
|
||||
|
||||
return forInit(head->pn_kid1 && !head->pn_kid1->isKind(PNK_FRESHENBLOCK)
|
||||
? head->pn_kid1
|
||||
: nullptr,
|
||||
&init) &&
|
||||
return forInit(head->pn_kid1, &init) &&
|
||||
optExpression(head->pn_kid2, &test) &&
|
||||
optExpression(head->pn_kid3, &update) &&
|
||||
builder.forStatement(init, test, update, stmt, &pn->pn_pos, dst);
|
||||
|
||||
@@ -2323,7 +2323,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
case PNK_FORIN: // by PNK_FOR
|
||||
case PNK_FOROF: // by PNK_FOR
|
||||
case PNK_FORHEAD: // by PNK_FOR
|
||||
case PNK_FRESHENBLOCK: // by PNK_FOR
|
||||
case PNK_CLASSMETHOD: // by PNK_CLASS
|
||||
case PNK_CLASSNAMES: // by PNK_CLASS
|
||||
case PNK_CLASSMETHODLIST: // by PNK_CLASS
|
||||
@@ -4972,6 +4971,15 @@ BytecodeEmitter::emitTry(ParseNode* pn)
|
||||
for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
|
||||
MOZ_ASSERT(this->stackDepth == depth);
|
||||
|
||||
// Clear the frame's return value that might have been set by the
|
||||
// try block:
|
||||
//
|
||||
// eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
|
||||
if (!emit1(JSOP_UNDEFINED))
|
||||
return false;
|
||||
if (!emit1(JSOP_SETRVAL))
|
||||
return false;
|
||||
|
||||
// Emit the lexical scope and catch body.
|
||||
MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
|
||||
if (!emitTree(pn3))
|
||||
@@ -5022,14 +5030,26 @@ BytecodeEmitter::emitTry(ParseNode* pn)
|
||||
stmtInfo.type = StmtType::SUBROUTINE;
|
||||
if (!updateSourceCoordNotes(pn->pn_kid3->pn_pos.begin))
|
||||
return false;
|
||||
if (!emit1(JSOP_FINALLY) ||
|
||||
!emit1(JSOP_GETRVAL) ||
|
||||
!emitTree(pn->pn_kid3) ||
|
||||
!emit1(JSOP_SETRVAL) ||
|
||||
!emit1(JSOP_RETSUB))
|
||||
{
|
||||
if (!emit1(JSOP_FINALLY))
|
||||
return false;
|
||||
if (!emit1(JSOP_GETRVAL))
|
||||
return false;
|
||||
|
||||
// Clear the frame's return value to make break/continue return
|
||||
// correct value even if there's no other statement before them:
|
||||
//
|
||||
// eval("x: try { 1 } finally { break x; }"); // undefined, not 1
|
||||
if (!emit1(JSOP_UNDEFINED))
|
||||
return false;
|
||||
if (!emit1(JSOP_SETRVAL))
|
||||
return false;
|
||||
|
||||
if (!emitTree(pn->pn_kid3))
|
||||
return false;
|
||||
if (!emit1(JSOP_SETRVAL))
|
||||
return false;
|
||||
if (!emit1(JSOP_RETSUB))
|
||||
return false;
|
||||
}
|
||||
hasTryFinally = true;
|
||||
MOZ_ASSERT(this->stackDepth == depth);
|
||||
}
|
||||
@@ -5268,6 +5288,15 @@ BytecodeEmitter::emitIterator()
|
||||
bool
|
||||
BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn, bool* letDecl)
|
||||
{
|
||||
// ES6 specifies that loop variables get a fresh binding in each iteration.
|
||||
// This is currently implemented for C-style for(;;) loops, but not
|
||||
// for-in/of loops, though a similar approach should work. See bug 449811.
|
||||
//
|
||||
// In `for (let x in/of EXPR)`, ES6 specifies that EXPR is evaluated in a
|
||||
// scope containing an uninitialized `x`. If EXPR accesses `x`, we should
|
||||
// get a ReferenceError due to the TDZ violation. This is not yet
|
||||
// implemented. See bug 1069480.
|
||||
|
||||
*letDecl = pn->isKind(PNK_LEXICALSCOPE);
|
||||
MOZ_ASSERT_IF(*letDecl, pn->isLexical());
|
||||
|
||||
@@ -5297,7 +5326,6 @@ BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn, bool* letDecl)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn, ptrdiff_t top)
|
||||
{
|
||||
@@ -5570,8 +5598,9 @@ BytecodeEmitter::emitForIn(ParseNode* pn, ptrdiff_t top)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* C-style `for (init; cond; update) ...` loop. */
|
||||
bool
|
||||
BytecodeEmitter::emitNormalFor(ParseNode* pn, ptrdiff_t top)
|
||||
BytecodeEmitter::emitCStyleFor(ParseNode* pn, ptrdiff_t top)
|
||||
{
|
||||
LoopStmtInfo stmtInfo(cx);
|
||||
pushLoopStatement(&stmtInfo, StmtType::FOR_LOOP, top);
|
||||
@@ -5579,28 +5608,48 @@ BytecodeEmitter::emitNormalFor(ParseNode* pn, ptrdiff_t top)
|
||||
ParseNode* forHead = pn->pn_left;
|
||||
ParseNode* forBody = pn->pn_right;
|
||||
|
||||
/* C-style for (init; cond; update) ... loop. */
|
||||
// If the head of this for-loop declared any lexical variables, the parser
|
||||
// wrapped this PNK_FOR node in a PNK_LEXICALSCOPE representing the
|
||||
// implicit scope of those variables. By the time we get here, we have
|
||||
// already entered that scope. So far, so good.
|
||||
//
|
||||
// ### Scope freshening
|
||||
//
|
||||
// Each iteration of a `for (let V...)` loop creates a fresh loop variable
|
||||
// binding for V, even if the loop is a C-style `for(;;)` loop:
|
||||
//
|
||||
// var funcs = [];
|
||||
// for (let i = 0; i < 2; i++)
|
||||
// funcs.push(function() { return i; });
|
||||
// assertEq(funcs[0](), 0); // the two closures capture...
|
||||
// assertEq(funcs[1](), 1); // ...two different `i` bindings
|
||||
//
|
||||
// This is implemented by "freshening" the implicit block -- changing the
|
||||
// scope chain to a fresh clone of the instantaneous block object -- each
|
||||
// iteration, just before evaluating the "update" in for(;;) loops.
|
||||
//
|
||||
// No freshening occurs in `for (const ...;;)` as there's no point: you
|
||||
// can't reassign consts. This is observable through the Debugger API. (The
|
||||
// ES6 spec also skips cloning the environment in this case.)
|
||||
bool forLoopRequiresFreshening = false;
|
||||
if (ParseNode* init = forHead->pn_kid1) {
|
||||
if (init->isKind(PNK_FRESHENBLOCK)) {
|
||||
// The loop's init declaration was hoisted into an enclosing lexical
|
||||
// scope node. Note that the block scope must be freshened each
|
||||
// iteration.
|
||||
forLoopRequiresFreshening = true;
|
||||
} else {
|
||||
emittingForInit = true;
|
||||
if (!updateSourceCoordNotes(init->pn_pos.begin))
|
||||
return false;
|
||||
if (!emitTree(init))
|
||||
return false;
|
||||
emittingForInit = false;
|
||||
forLoopRequiresFreshening = init->isKind(PNK_LET);
|
||||
|
||||
if (!init->isKind(PNK_VAR) && !init->isKind(PNK_LET) && !init->isKind(PNK_CONST)) {
|
||||
// 'init' is an expression, not a declaration. emitTree left
|
||||
// its value on the stack.
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
}
|
||||
// Emit the `init` clause, whether it's an expression or a variable
|
||||
// declaration. (The loop variables were hoisted into an enclosing
|
||||
// scope, but we still need to emit code for the initializers.)
|
||||
emittingForInit = true;
|
||||
if (!updateSourceCoordNotes(init->pn_pos.begin))
|
||||
return false;
|
||||
if (!emitTree(init))
|
||||
return false;
|
||||
emittingForInit = false;
|
||||
|
||||
if (!init->isKind(PNK_VAR) && !init->isKind(PNK_LET) && !init->isKind(PNK_CONST)) {
|
||||
// 'init' is an expression, not a declaration. emitTree left its
|
||||
// value on the stack.
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5728,7 +5777,7 @@ BytecodeEmitter::emitFor(ParseNode* pn, ptrdiff_t top)
|
||||
return emitForOf(StmtType::FOR_OF_LOOP, pn, top);
|
||||
|
||||
MOZ_ASSERT(pn->pn_left->isKind(PNK_FORHEAD));
|
||||
return emitNormalFor(pn, top);
|
||||
return emitCStyleFor(pn, top);
|
||||
}
|
||||
|
||||
MOZ_NEVER_INLINE bool
|
||||
@@ -5846,13 +5895,16 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
MOZ_ASSERT(!needsProto);
|
||||
|
||||
/*
|
||||
* For a script we emit the code as we parse. Thus the bytecode for
|
||||
* top-level functions should go in the prologue to predefine their
|
||||
* names in the variable object before the already-generated main code
|
||||
* is executed. This extra work for top-level scripts is not necessary
|
||||
* when we emit the code for a function. It is fully parsed prior to
|
||||
* invocation of the emitter and calls to emitTree for function
|
||||
* definitions can be scheduled before generating the rest of code.
|
||||
* For scripts we put the bytecode for top-level functions in the prologue
|
||||
* to predefine their names in the variable object before the main code is
|
||||
* executed.
|
||||
*
|
||||
* Functions are fully parsed prior to invocation of the emitter and calls
|
||||
* to emitTree for function definitions are scheduled before generating
|
||||
* the rest of code.
|
||||
*
|
||||
* For modules, we record the function and instantiate the binding during
|
||||
* ModuleDeclarationInstantiation(), before the script is run.
|
||||
*/
|
||||
if (sc->isGlobalContext()) {
|
||||
MOZ_ASSERT(pn->pn_scopecoord.isFree());
|
||||
@@ -5864,7 +5916,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
if (!updateSourceCoordNotes(pn->pn_pos.begin))
|
||||
return false;
|
||||
switchToMain();
|
||||
} else {
|
||||
} else if (sc->isFunctionBox()) {
|
||||
#ifdef DEBUG
|
||||
BindingIter bi(script);
|
||||
while (bi->name() != fun->atom())
|
||||
@@ -5881,6 +5933,11 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
} else {
|
||||
RootedModuleObject module(cx, sc->asModuleBox()->module());
|
||||
RootedAtom name(cx, fun->atom());
|
||||
if (!module->noteFunctionDeclaration(cx, name, fun))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -7041,12 +7098,25 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Class methods are not enumerable.
|
||||
if (type == ClassBody) {
|
||||
switch (op) {
|
||||
case JSOP_INITPROP: op = JSOP_INITHIDDENPROP; break;
|
||||
case JSOP_INITPROP_GETTER: op = JSOP_INITHIDDENPROP_GETTER; break;
|
||||
case JSOP_INITPROP_SETTER: op = JSOP_INITHIDDENPROP_SETTER; break;
|
||||
default: MOZ_CRASH("Invalid op");
|
||||
}
|
||||
}
|
||||
|
||||
if (isIndex) {
|
||||
objp.set(nullptr);
|
||||
switch (op) {
|
||||
case JSOP_INITPROP: op = JSOP_INITELEM; break;
|
||||
case JSOP_INITPROP_GETTER: op = JSOP_INITELEM_GETTER; break;
|
||||
case JSOP_INITPROP_SETTER: op = JSOP_INITELEM_SETTER; break;
|
||||
case JSOP_INITPROP: op = JSOP_INITELEM; break;
|
||||
case JSOP_INITHIDDENPROP: op = JSOP_INITHIDDENELEM; break;
|
||||
case JSOP_INITPROP_GETTER: op = JSOP_INITELEM_GETTER; break;
|
||||
case JSOP_INITHIDDENPROP_GETTER: op = JSOP_INITHIDDENELEM_GETTER; break;
|
||||
case JSOP_INITPROP_SETTER: op = JSOP_INITELEM_SETTER; break;
|
||||
case JSOP_INITHIDDENPROP_SETTER: op = JSOP_INITHIDDENELEM_SETTER; break;
|
||||
default: MOZ_CRASH("Invalid op");
|
||||
}
|
||||
if (!emit1(op))
|
||||
@@ -7059,6 +7129,8 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp,
|
||||
return false;
|
||||
|
||||
if (objp) {
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
MOZ_ASSERT(!IsHiddenInitOp(op));
|
||||
MOZ_ASSERT(!objp->inDictionaryMode());
|
||||
Rooted<jsid> id(cx, AtomToId(key->pn_atom));
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
|
||||
@@ -586,7 +586,7 @@ struct BytecodeEmitter
|
||||
bool emitFor(ParseNode* pn, ptrdiff_t top);
|
||||
bool emitForIn(ParseNode* pn, ptrdiff_t top);
|
||||
bool emitForInOrOfVariables(ParseNode* pn, bool* letDecl);
|
||||
bool emitNormalFor(ParseNode* pn, ptrdiff_t top);
|
||||
bool emitCStyleFor(ParseNode* pn, ptrdiff_t top);
|
||||
bool emitWhile(ParseNode* pn, ptrdiff_t top);
|
||||
|
||||
bool emitBreak(PropertyName* label);
|
||||
|
||||
@@ -408,7 +408,6 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
||||
case PNK_FORIN:
|
||||
case PNK_FOROF:
|
||||
case PNK_FORHEAD:
|
||||
case PNK_FRESHENBLOCK:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSNAMES:
|
||||
@@ -1710,7 +1709,6 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
case PNK_GENERATOR:
|
||||
case PNK_EXPORT_BATCH_SPEC:
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_FRESHENBLOCK:
|
||||
case PNK_POSHOLDER:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
return true;
|
||||
|
||||
@@ -571,10 +571,6 @@ class FullParseHandler
|
||||
return new_<TernaryNode>(kind, JSOP_NOP, pn1, pn2, pn3, pos);
|
||||
}
|
||||
|
||||
ParseNode* newFreshenBlock(const TokenPos& pos) {
|
||||
return new_<NullaryNode>(PNK_FRESHENBLOCK, pos);
|
||||
}
|
||||
|
||||
ParseNode* newSwitchStatement(uint32_t begin, ParseNode* discriminant, ParseNode* caseList) {
|
||||
TokenPos pos(begin, caseList->pn_pos.end);
|
||||
return new_<BinaryNode>(PNK_SWITCH, JSOP_NOP, pos, discriminant, caseList);
|
||||
|
||||
@@ -374,7 +374,6 @@ class NameResolver
|
||||
case PNK_CONTINUE:
|
||||
case PNK_DEBUGGER:
|
||||
case PNK_EXPORT_BATCH_SPEC:
|
||||
case PNK_FRESHENBLOCK:
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_POSHOLDER:
|
||||
MOZ_ASSERT(cur->isArity(PN_NULLARY));
|
||||
|
||||
@@ -214,7 +214,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||
case PNK_DEBUGGER:
|
||||
case PNK_EXPORT_BATCH_SPEC:
|
||||
case PNK_OBJECT_PROPERTY_NAME:
|
||||
case PNK_FRESHENBLOCK:
|
||||
case PNK_POSHOLDER:
|
||||
MOZ_ASSERT(pn->isArity(PN_NULLARY));
|
||||
MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately");
|
||||
|
||||
@@ -165,7 +165,6 @@ class PackedScopeCoordinate
|
||||
F(FORIN) \
|
||||
F(FOROF) \
|
||||
F(FORHEAD) \
|
||||
F(FRESHENBLOCK) \
|
||||
F(ARGSBODY) \
|
||||
F(SPREAD) \
|
||||
F(MUTATEPROTO) \
|
||||
|
||||
+53
-127
@@ -4080,7 +4080,8 @@ Parser<FullParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, AutoPus
|
||||
|
||||
template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, AutoPushStmtInfoPC& stmt)
|
||||
Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj,
|
||||
AutoPushStmtInfoPC& stmt)
|
||||
{
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
@@ -5203,19 +5204,37 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
|
||||
/*
|
||||
* True if we have 'for (var/let/const ...)'.
|
||||
*/
|
||||
// True if we have 'for (var/let/const ...)'.
|
||||
bool isForDecl = false;
|
||||
|
||||
// The next three variables are used to implement `for (let/const ...)`.
|
||||
//
|
||||
// We generate an implicit block, wrapping the whole loop, to store loop
|
||||
// variables declared this way. Note that if the loop uses `for (var...)`
|
||||
// instead, those variables go on some existing enclosing scope, so no
|
||||
// implicit block scope is created.
|
||||
//
|
||||
// All three variables remain null/none if the loop is any other form.
|
||||
//
|
||||
// blockObj is the static block object for the implicit block scope.
|
||||
RootedStaticBlockObject blockObj(context);
|
||||
|
||||
// letStmt is the BLOCK StmtInfo for the implicit block.
|
||||
//
|
||||
// Caution: `letStmt.emplace()` creates some Rooted objects. Rooteds must
|
||||
// be created/destroyed in FIFO order. Therefore adding a Rooted in this
|
||||
// function, between this point and the .emplace() call below, would trip
|
||||
// assertions.
|
||||
Maybe<AutoPushStmtInfoPC> letStmt;
|
||||
|
||||
// The PNK_LEXICALSCOPE node containing blockObj's ObjectBox.
|
||||
ParseNode* forLetImpliedBlock = nullptr;
|
||||
|
||||
// True if a 'let' token at the head is parsed as an identifier instead of
|
||||
// as starting a declaration.
|
||||
bool letIsIdentifier = false;
|
||||
|
||||
/* Non-null when isForDecl is true for a 'for (let ...)' statement. */
|
||||
RootedStaticBlockObject blockObj(context);
|
||||
|
||||
/* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
|
||||
// Set to 'x' in 'for (x; ...; ...)' or 'for (x in ...)'.
|
||||
ParseNode* pn1;
|
||||
|
||||
TokenStream::Modifier modifier = TokenStream::Operand;
|
||||
@@ -5265,9 +5284,19 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
||||
|
||||
// Initialize the enclosing scope manually for the call to
|
||||
// |variables| below.
|
||||
blockObj = StaticBlockObject::create(context);
|
||||
if (!blockObj)
|
||||
return null();
|
||||
blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
|
||||
letStmt.emplace(*this, StmtType::BLOCK);
|
||||
forLetImpliedBlock = pushLetScope(blockObj, *letStmt);
|
||||
if (!forLetImpliedBlock)
|
||||
return null();
|
||||
(*letStmt)->isForLetBlock = true;
|
||||
|
||||
MOZ_ASSERT(CurrentLexicalStaticBlock(pc) == blockObj);
|
||||
pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
|
||||
nullptr, blockObj, DontHoistVars);
|
||||
nullptr, blockObj, HoistVars);
|
||||
} else {
|
||||
pn1 = expr(InProhibited, yieldHandling, TripledotProhibited);
|
||||
}
|
||||
@@ -5285,60 +5314,10 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST));
|
||||
MOZ_ASSERT(!!blockObj == (isForDecl && (pn1->isOp(JSOP_DEFLET) || pn1->isOp(JSOP_DEFCONST))));
|
||||
|
||||
// If the head of a for-loop declares any lexical variables, we generate an
|
||||
// implicit block to store them. We implement this by desugaring. These:
|
||||
//
|
||||
// for (let/const <bindings>; <test>; <update>) <stmt>
|
||||
// for (let <pattern> in <expr>) <stmt>
|
||||
// for (let <pattern> of <expr>) <stmt>
|
||||
//
|
||||
// transform into roughly the same parse trees as these (using deprecated
|
||||
// let-block syntax):
|
||||
//
|
||||
// let (<bindings>) { for (; <test>; <update>) <stmt> }
|
||||
// let (<pattern>) { for (<pattern> in <expr>) <stmt> }
|
||||
// let (<pattern>) { for (<pattern> of <expr>) <stmt> }
|
||||
//
|
||||
// This desugaring is not ES6 compliant. Initializers in the head of a
|
||||
// let-block are evaluated *outside* the scope of the variables being
|
||||
// initialized. ES6 mandates that they be evaluated in the same scope,
|
||||
// triggering used-before-initialization temporal dead zone errors as
|
||||
// necessary. See bug 1216623 on scoping and bug 1069480 on TDZ.
|
||||
//
|
||||
// Additionally, in ES6, each iteration of a for-loop creates a fresh
|
||||
// binding of the loop variables. For example:
|
||||
//
|
||||
// var funcs = [];
|
||||
// for (let i = 0; i < 2; i++)
|
||||
// funcs.push(function() { return i; });
|
||||
// assertEq(funcs[0](), 0); // the two closures capture...
|
||||
// assertEq(funcs[1](), 1); // ...two different `i` bindings
|
||||
//
|
||||
// These semantics are implemented by "freshening" the implicit block --
|
||||
// changing the scope chain to a fresh clone of the instantaneous block
|
||||
// object -- each iteration, just before evaluating the "update" in
|
||||
// for(;;) loops. We don't implement this freshening for for-in/of loops
|
||||
// yet: bug 449811.
|
||||
//
|
||||
// No freshening occurs in `for (const ...;;)` as there's no point: you
|
||||
// can't reassign consts. This is observable through the Debugger API. (The
|
||||
// ES6 spec also skips cloning the environment in this case.)
|
||||
//
|
||||
// If the for-loop head includes a lexical declaration, then we create an
|
||||
// implicit block scope, and:
|
||||
//
|
||||
// * forLetImpliedBlock is the node for the implicit block scope.
|
||||
// * forLetDecl is the node for the decl 'let/const <pattern>'.
|
||||
//
|
||||
// Otherwise both are null.
|
||||
ParseNode* forLetImpliedBlock = nullptr;
|
||||
ParseNode* forLetDecl = nullptr;
|
||||
MOZ_ASSERT(letStmt.isSome() == (isForDecl && (pn1->isOp(JSOP_DEFLET) || pn1->isOp(JSOP_DEFCONST))));
|
||||
|
||||
// If there's an |in| keyword here, it's a for-in loop, by dint of careful
|
||||
// parsing of |pn1|.
|
||||
Maybe<AutoPushStmtInfoPC> letStmt; /* used if blockObj != nullptr. */
|
||||
ParseNode* pn2; /* forHead->pn_kid2 */
|
||||
ParseNode* pn3; /* forHead->pn_kid3 */
|
||||
ParseNodeKind headKind = PNK_FORHEAD;
|
||||
@@ -5409,7 +5388,7 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
||||
}
|
||||
} else {
|
||||
/* Not a declaration. */
|
||||
MOZ_ASSERT(!blockObj);
|
||||
MOZ_ASSERT(!letStmt);
|
||||
pn2 = pn1;
|
||||
pn1 = nullptr;
|
||||
|
||||
@@ -5424,27 +5403,10 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
||||
return null();
|
||||
modifier = TokenStream::None;
|
||||
|
||||
if (blockObj) {
|
||||
/*
|
||||
* Now that the pn3 has been parsed, push the let scope. To hold
|
||||
* the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node
|
||||
* created by pushLetScope around the for's initializer. This also
|
||||
* serves to indicate the let-decl to the emitter.
|
||||
*/
|
||||
letStmt.emplace(*this, StmtType::BLOCK);
|
||||
ParseNode* block = pushLetScope(blockObj, *letStmt);
|
||||
if (!block)
|
||||
return null();
|
||||
(*letStmt)->isForLetBlock = true;
|
||||
block->pn_expr = pn1;
|
||||
block->pn_pos = pn1->pn_pos;
|
||||
pn1 = block;
|
||||
}
|
||||
|
||||
if (isForDecl) {
|
||||
/*
|
||||
* pn2 is part of a declaration. Make a copy that can be passed to
|
||||
* EmitAssignment. Take care to do this after pushLetScope.
|
||||
* BytecodeEmitter::emitAssignment.
|
||||
*/
|
||||
pn2 = cloneLeftHandSide(pn2);
|
||||
if (!pn2)
|
||||
@@ -5466,47 +5428,13 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
||||
|
||||
MOZ_ASSERT(headKind == PNK_FORHEAD);
|
||||
|
||||
if (blockObj) {
|
||||
if (letStmt) {
|
||||
// Ensure here that the previously-unchecked assignment mandate for
|
||||
// const declarations holds.
|
||||
if (!checkForHeadConstInitializers(pn1)) {
|
||||
report(ParseError, false, nullptr, JSMSG_BAD_CONST_DECL);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Desugar
|
||||
//
|
||||
// for (let INIT; TEST; UPDATE) STMT
|
||||
//
|
||||
// into
|
||||
//
|
||||
// let (INIT) { for (; TEST; UPDATE) STMT }
|
||||
//
|
||||
// to provide a block scope for INIT.
|
||||
letStmt.emplace(*this, StmtType::BLOCK);
|
||||
forLetImpliedBlock = pushLetScope(blockObj, *letStmt);
|
||||
if (!forLetImpliedBlock)
|
||||
return null();
|
||||
(*letStmt)->isForLetBlock = true;
|
||||
|
||||
forLetDecl = pn1;
|
||||
|
||||
// The above transformation isn't enough to implement |INIT|
|
||||
// scoping, because each loop iteration must see separate bindings
|
||||
// of |INIT|. We handle this by replacing the block on the scope
|
||||
// chain with a new block, copying the old one's contents, each
|
||||
// iteration. We supply a special PNK_FRESHENBLOCK node as the
|
||||
// |let INIT| node for |for(let INIT;;)| loop heads to distinguish
|
||||
// such nodes from *actual*, non-desugared use of the above syntax.
|
||||
// (We don't do this for PNK_CONST nodes because the spec says no
|
||||
// freshening happens -- observable with the Debugger API.)
|
||||
if (pn1->isKind(PNK_CONST)) {
|
||||
pn1 = nullptr;
|
||||
} else {
|
||||
pn1 = handler.newFreshenBlock(pn1->pn_pos);
|
||||
if (!pn1)
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the loop condition or null into pn2. */
|
||||
@@ -5558,7 +5486,7 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
|
||||
if (forLetImpliedBlock) {
|
||||
forLetImpliedBlock->pn_expr = forLoop;
|
||||
forLetImpliedBlock->pn_pos = forLoop->pn_pos;
|
||||
return handler.newLetBlock(forLetDecl, forLetImpliedBlock, forLoop->pn_pos);
|
||||
return forLetImpliedBlock;
|
||||
}
|
||||
return forLoop;
|
||||
}
|
||||
@@ -6675,9 +6603,6 @@ template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::shouldParseLetDeclaration(bool* parseDeclOut)
|
||||
{
|
||||
// 'let' is a reserved keyword in strict mode and we shouldn't get here.
|
||||
MOZ_ASSERT(!pc->sc->strict());
|
||||
|
||||
TokenKind tt;
|
||||
*parseDeclOut = false;
|
||||
|
||||
@@ -6692,18 +6617,10 @@ Parser<ParseHandler>::shouldParseLetDeclaration(bool* parseDeclOut)
|
||||
|
||||
case TOK_LC:
|
||||
case TOK_LB:
|
||||
// A following name is always a declaration.
|
||||
//
|
||||
// |let {| and |let [| are destructuring declarations.
|
||||
*parseDeclOut = true;
|
||||
break;
|
||||
|
||||
case TOK_LP:
|
||||
// Only parse let blocks for 1.7 and 1.8. Do not expose deprecated let
|
||||
// blocks to content.
|
||||
*parseDeclOut = versionNumber() == JSVERSION_1_7 || versionNumber() == JSVERSION_1_8;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -6716,6 +6633,9 @@ bool
|
||||
Parser<ParseHandler>::peekShouldParseLetDeclaration(bool* parseDeclOut,
|
||||
TokenStream::Modifier modifier)
|
||||
{
|
||||
// 'let' is a reserved keyword in strict mode and we shouldn't get here.
|
||||
MOZ_ASSERT(!pc->sc->strict());
|
||||
|
||||
*parseDeclOut = false;
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -6802,7 +6722,12 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling, bool canHaveDirecti
|
||||
|
||||
case TOK_NAME: {
|
||||
// 'let' is a contextual keyword in sloppy node. In strict mode, it is
|
||||
// always lexed as TOK_LET.
|
||||
// always lexed as TOK_LET except following case:
|
||||
//
|
||||
// "use strict"
|
||||
// let a = 1;
|
||||
//
|
||||
// There 'let' is lexed as TOK_NAME before parsing directive.
|
||||
if (tokenStream.currentName() == context->names().let) {
|
||||
bool parseDecl;
|
||||
if (!shouldParseLetDeclaration(&parseDecl))
|
||||
@@ -8307,6 +8232,7 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
|
||||
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
|
||||
if (!blockObj)
|
||||
return null();
|
||||
|
||||
// Initialize the enclosing scope manually for the call to |bind|
|
||||
// below, which is before the call to |pushLetScope|.
|
||||
blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "gdb-tests.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/Initialization.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// Transitional test cases, useful while Odin accepts both
|
||||
// "Int32Array" and "SharedInt32Array" to construct a view onto shared
|
||||
// memory but the former only when an atomic operation is referenced,
|
||||
// as per spec. Eventually it will stop accepting "SharedInt32Array",
|
||||
// because that name is going away.
|
||||
//
|
||||
// These should not run with --no-asmjs.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Int8Array can be used on SharedArrayBuffer, if atomics are present
|
||||
|
||||
function m1(stdlib, ffi, heap) {
|
||||
"use asm";
|
||||
|
||||
var i8 = new stdlib.Int8Array(heap);
|
||||
var add = stdlib.Atomics.add;
|
||||
|
||||
function f() {
|
||||
add(i8, 0, 1);
|
||||
return 37;
|
||||
}
|
||||
|
||||
return { f:f }
|
||||
}
|
||||
|
||||
assertEq(isAsmJSModule(m1), true);
|
||||
|
||||
var { f } = m1(this, {}, new SharedArrayBuffer(65536));
|
||||
assertEq(f(), 37);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SharedInt8Array can still be used on SharedArrayBuffer.
|
||||
// (SharedInt8Array will eventually disappear, and this
|
||||
// test case with it.)
|
||||
|
||||
function m2(stdlib, ffi, heap) {
|
||||
"use asm";
|
||||
|
||||
var i8 = new stdlib.SharedInt8Array(heap);
|
||||
var add = stdlib.Atomics.add;
|
||||
|
||||
function g() {
|
||||
add(i8, 0, 1);
|
||||
return 42;
|
||||
}
|
||||
|
||||
return { g:g }
|
||||
}
|
||||
|
||||
assertEq(isAsmJSModule(m2), true);
|
||||
|
||||
var { g } = m2(this, {}, new SharedArrayBuffer(65536));
|
||||
assertEq(g(), 42);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SharedInt8Array still cannot be used on ArrayBuffer, even without
|
||||
// atomics present.
|
||||
// (SharedInt8Array will eventually disappear, and this
|
||||
// test case with it.)
|
||||
|
||||
function m3(stdlib, ffi, heap) {
|
||||
"use asm";
|
||||
|
||||
var i8 = new stdlib.SharedInt8Array(heap);
|
||||
|
||||
function h() {
|
||||
return i8[0]|0;
|
||||
}
|
||||
|
||||
return { h:h }
|
||||
}
|
||||
|
||||
// Running the shell with -w you should see an error here.
|
||||
|
||||
assertEq(isAsmJSModule(m3), true);
|
||||
try {
|
||||
var wasThrown = false;
|
||||
m3(this, {}, new ArrayBuffer(65536));
|
||||
}
|
||||
catch (e) {
|
||||
wasThrown = true;
|
||||
}
|
||||
assertEq(wasThrown, true);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Int8Array cannot be used on SharedArrayBuffer if atomics are not imported.
|
||||
// One argument for the restriction is that there are some optimizations
|
||||
// that are legal if the memory is known not to be shared that are illegal
|
||||
// when it is shared.
|
||||
|
||||
function m4(stdlib, ffi, heap) {
|
||||
"use asm";
|
||||
|
||||
var i8 = new stdlib.Int8Array(heap);
|
||||
|
||||
function i() {
|
||||
return i8[0]|0;
|
||||
}
|
||||
|
||||
return { i:i }
|
||||
}
|
||||
|
||||
assertEq(isAsmJSModule(m4), true);
|
||||
|
||||
// An error is not actually thrown because the link failure drops us
|
||||
// back to JS execution and then the Int8Array constructor will copy data
|
||||
// from the SharedArrayBuffer.
|
||||
//
|
||||
// Running the shell with -w you should see an error here.
|
||||
|
||||
var { i } = m4(this, {}, new SharedArrayBuffer(65536));
|
||||
assertEq(isAsmJSFunction(i), false);
|
||||
@@ -1,5 +1,5 @@
|
||||
// |jit-test| test-also-noasmjs
|
||||
if (!this.SharedArrayBuffer || !this.SharedInt32Array || !this.Atomics)
|
||||
if (!this.SharedArrayBuffer || !this.Atomics)
|
||||
quit();
|
||||
|
||||
// The code duplication below is very far from elegant but provides
|
||||
@@ -8,6 +8,27 @@ if (!this.SharedArrayBuffer || !this.SharedInt32Array || !this.Atomics)
|
||||
load(libdir + "asm.js");
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
// This hack allows the test cases to run with --no-asmjs: the field values
|
||||
// are basically ignored in asm.js mode, and the correct (Firefox-specific)
|
||||
// field values are used in non-asm.js mode. If run in a non-Firefox
|
||||
// browser that does not have the parallel type hierarchy this should also
|
||||
// work.
|
||||
//
|
||||
// This hack will be removed when the parallel type hierarchy is removed
|
||||
// from Firefox, bug 1176214.
|
||||
|
||||
const atomicStdlib = {
|
||||
Atomics: Atomics,
|
||||
Int8Array: this.SharedInt8Array ? SharedInt8Array : Int8Array,
|
||||
Uint8Array: this.SharedUint8Array ? SharedUint8Array : Uint8Array,
|
||||
Int16Array: this.SharedInt16Array ? SharedInt16Array : Int16Array,
|
||||
Uint16Array: this.SharedUint16Array ? SharedUint16Array : Uint16Array,
|
||||
Int32Array: this.SharedInt32Array ? SharedInt32Array : Int32Array,
|
||||
Uint32Array: this.SharedUint32Array ? SharedUint32Array : Uint32Array,
|
||||
Float32Array: this.SharedFloat32Array ? SharedFloat32Array : Float32Array,
|
||||
Float64Array: this.SharedFloat64Array ? SharedFloat64Array : Float64Array
|
||||
};
|
||||
|
||||
var loadModule_int32_code =
|
||||
USE_ASM + `
|
||||
var atomic_fence = stdlib.Atomics.fence;
|
||||
@@ -21,7 +42,7 @@ var loadModule_int32_code =
|
||||
var atomic_or = stdlib.Atomics.or;
|
||||
var atomic_xor = stdlib.Atomics.xor;
|
||||
|
||||
var i32a = new stdlib.SharedInt32Array(heap);
|
||||
var i32a = new stdlib.Int32Array(heap);
|
||||
|
||||
function do_fence() {
|
||||
atomic_fence();
|
||||
@@ -233,7 +254,7 @@ var loadModule_int32 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int32_
|
||||
|
||||
function test_int32(heap) {
|
||||
var i32a = new SharedInt32Array(heap);
|
||||
var i32m = asmLink(loadModule_int32, this, {}, heap);
|
||||
var i32m = asmLink(loadModule_int32, atomicStdlib, {}, heap);
|
||||
|
||||
var size = SharedInt32Array.BYTES_PER_ELEMENT;
|
||||
|
||||
@@ -338,7 +359,7 @@ var loadModule_uint32_code =
|
||||
var atomic_or = stdlib.Atomics.or;
|
||||
var atomic_xor = stdlib.Atomics.xor;
|
||||
|
||||
var i32a = new stdlib.SharedUint32Array(heap);
|
||||
var i32a = new stdlib.Uint32Array(heap);
|
||||
|
||||
// Load element 0
|
||||
function do_load() {
|
||||
@@ -516,7 +537,7 @@ var loadModule_uint32 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint3
|
||||
|
||||
function test_uint32(heap) {
|
||||
var i32a = new SharedUint32Array(heap);
|
||||
var i32m = loadModule_uint32(this, {}, heap);
|
||||
var i32m = loadModule_uint32(atomicStdlib, {}, heap);
|
||||
|
||||
var size = SharedUint32Array.BYTES_PER_ELEMENT;
|
||||
|
||||
@@ -619,7 +640,7 @@ var loadModule_int16_code =
|
||||
var atomic_or = stdlib.Atomics.or;
|
||||
var atomic_xor = stdlib.Atomics.xor;
|
||||
|
||||
var i16a = new stdlib.SharedInt16Array(heap);
|
||||
var i16a = new stdlib.Int16Array(heap);
|
||||
|
||||
function do_fence() {
|
||||
atomic_fence();
|
||||
@@ -802,7 +823,7 @@ var loadModule_int16 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int16_
|
||||
|
||||
function test_int16(heap) {
|
||||
var i16a = new SharedInt16Array(heap);
|
||||
var i16m = loadModule_int16(this, {}, heap);
|
||||
var i16m = loadModule_int16(atomicStdlib, {}, heap);
|
||||
|
||||
var size = SharedInt16Array.BYTES_PER_ELEMENT;
|
||||
|
||||
@@ -914,7 +935,7 @@ var loadModule_uint16_code =
|
||||
var atomic_or = stdlib.Atomics.or;
|
||||
var atomic_xor = stdlib.Atomics.xor;
|
||||
|
||||
var i16a = new stdlib.SharedUint16Array(heap);
|
||||
var i16a = new stdlib.Uint16Array(heap);
|
||||
|
||||
// Load element 0
|
||||
function do_load() {
|
||||
@@ -1092,7 +1113,7 @@ var loadModule_uint16 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint1
|
||||
|
||||
function test_uint16(heap) {
|
||||
var i16a = new SharedUint16Array(heap);
|
||||
var i16m = loadModule_uint16(this, {}, heap);
|
||||
var i16m = loadModule_uint16(atomicStdlib, {}, heap);
|
||||
|
||||
var size = SharedUint16Array.BYTES_PER_ELEMENT;
|
||||
|
||||
@@ -1202,7 +1223,7 @@ var loadModule_int8_code =
|
||||
var atomic_or = stdlib.Atomics.or;
|
||||
var atomic_xor = stdlib.Atomics.xor;
|
||||
|
||||
var i8a = new stdlib.SharedInt8Array(heap);
|
||||
var i8a = new stdlib.Int8Array(heap);
|
||||
|
||||
// Load element 0
|
||||
function do_load() {
|
||||
@@ -1380,7 +1401,7 @@ var loadModule_int8 = asmCompile('stdlib', 'foreign', 'heap', loadModule_int8_co
|
||||
|
||||
function test_int8(heap) {
|
||||
var i8a = new SharedInt8Array(heap);
|
||||
var i8m = loadModule_int8(this, {}, heap);
|
||||
var i8m = loadModule_int8(atomicStdlib, {}, heap);
|
||||
|
||||
for ( var i=0 ; i < i8a.length ; i++ )
|
||||
i8a[i] = 0;
|
||||
@@ -1483,7 +1504,7 @@ var loadModule_uint8_code =
|
||||
var atomic_or = stdlib.Atomics.or;
|
||||
var atomic_xor = stdlib.Atomics.xor;
|
||||
|
||||
var i8a = new stdlib.SharedUint8Array(heap);
|
||||
var i8a = new stdlib.Uint8Array(heap);
|
||||
|
||||
// Load element 0
|
||||
function do_load() {
|
||||
@@ -1661,7 +1682,7 @@ var loadModule_uint8 = asmCompile('stdlib', 'foreign', 'heap', loadModule_uint8_
|
||||
|
||||
function test_uint8(heap) {
|
||||
var i8a = new SharedUint8Array(heap);
|
||||
var i8m = loadModule_uint8(this, {}, heap);
|
||||
var i8m = loadModule_uint8(atomicStdlib, {}, heap);
|
||||
|
||||
for ( var i=0 ; i < i8a.length ; i++ )
|
||||
i8a[i] = 0;
|
||||
@@ -1816,7 +1837,7 @@ var loadModule_misc_code =
|
||||
var loadModule_misc = asmCompile('stdlib', 'foreign', 'heap', loadModule_misc_code);
|
||||
|
||||
function test_misc(heap) {
|
||||
var misc = loadModule_misc(this, {}, heap);
|
||||
var misc = loadModule_misc(atomicStdlib, {}, heap);
|
||||
|
||||
assertEq(misc.ilf1(), 1);
|
||||
assertEq(misc.ilf2(), 1);
|
||||
@@ -1848,7 +1869,7 @@ setARMHwCapFlags('vfp');
|
||||
asmCompile('stdlib', 'ffi', 'heap',
|
||||
USE_ASM + `
|
||||
var atomic_exchange = stdlib.Atomics.exchange;
|
||||
var i8a = new stdlib.SharedInt8Array(heap);
|
||||
var i8a = new stdlib.Int8Array(heap);
|
||||
|
||||
function do_xchg() {
|
||||
var v = 0;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
function newFunc(x) Function(x)()
|
||||
newFunc(`
|
||||
var BUGNUMBER = 8[ anonymous = true ]--;
|
||||
() => BUGNUMBER;
|
||||
`);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
var s, x = 0;
|
||||
var s, v = "NOPE";
|
||||
|
||||
s = '';
|
||||
for (let x = x; x < 3; x++)
|
||||
for (let v = 0, x = v; x < 3; x++)
|
||||
s += x;
|
||||
assertEq(s, '012');
|
||||
|
||||
s = '';
|
||||
for (let x = eval('x'); x < 3; x++)
|
||||
for (let v = 0, x = eval('v'); x < 3; x++)
|
||||
s += x;
|
||||
assertEq(s, '012');
|
||||
|
||||
s = ''
|
||||
for (let x = function () { with ({}) return x; }(); x < 3; x++)
|
||||
for (let v = 0, x = function () { with ({}) return v; }(); x < 3; x++)
|
||||
s += x;
|
||||
assertEq(s, '012');
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
var s = '', x = {a: 1, b: 2, c: 3};
|
||||
// Scoping: `x` in the head of a `for (let x...)` loop refers to the loop variable.
|
||||
|
||||
// For now, this means it evaluates to undefined. It ought to throw a
|
||||
// ReferenceError instead, but the TDZ isn't implemented here (bug 1069480).
|
||||
var s = "", x = {a: 1, b: 2, c: 3};
|
||||
for (let x in eval('x'))
|
||||
s += x;
|
||||
assertEq(s, 'abc');
|
||||
assertEq(s, "");
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// In `for (let x = EXPR; ;)`, if `x` appears within EXPR, it refers to the
|
||||
// loop variable. Actually doing this is typically a TDZ error.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
for (let x = x; null.foo; null.foo++) {}
|
||||
}, ReferenceError);
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
for (let x = eval('x'); null.foo; null.foo++) {}
|
||||
}, ReferenceError);
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
for (let x = function () { with ({}) return x; }(); null.foo; null.foo++) {}
|
||||
}, ReferenceError);
|
||||
@@ -1,5 +0,0 @@
|
||||
var x = "foobar";
|
||||
{ for (let x of x) assertEq(x.length, 1, "second x refers to outer x"); }
|
||||
|
||||
var x = "foobar";
|
||||
{ for (let x in x) assertEq(x.length, 1, "second x refers to outer x"); }
|
||||
@@ -3,7 +3,8 @@ var otherGlobal = newGlobal();
|
||||
function test(str, arg, result)
|
||||
{
|
||||
arg = arg || 'ponies';
|
||||
result = result || 'ponies';
|
||||
if (arguments.length < 3)
|
||||
result = 'ponies';
|
||||
|
||||
var fun = new Function('x', str);
|
||||
|
||||
@@ -136,11 +137,7 @@ test('for (let y = 1;; ++y) {return x;}');
|
||||
test('for (let y = 1; ++y;) {return x;}');
|
||||
test('for (let [[a, [b, c]]] = [[x, []]];;) {return a;}');
|
||||
test('var sum = 0;for (let y = x; y < 4; ++y) {sum += y;}return sum;', 1, 6);
|
||||
test('var sum = 0;for (let x = x, y = 10; x < 4; ++x) {sum += x;}return sum;', 1, 6);
|
||||
test('var sum = 0;for (let x = x; x < 4; ++x) {sum += x;}return x;', 1, 1);
|
||||
test('var sum = 0;for (let x = eval("x"); x < 4; ++x) {sum += x;}return sum;', 1, 6);
|
||||
test('var sum = 0;for (let x = x; eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6);
|
||||
test('var sum = 0;for (let x = eval("x"); eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6);
|
||||
test('var sum = 0;for (let x = 1; eval("x") < 4; ++x) {sum += eval("x");}return sum;', 1, 6);
|
||||
test('for (var y = 1;;) {return x;}');
|
||||
test('for (var y = 1;; ++y) {return x;}');
|
||||
test('for (var y = 1; ++y;) {return x;}');
|
||||
@@ -151,8 +148,6 @@ test('var sum = 0;for (var X = x; X < 4; ++X) {sum += X;}return x;', 1, 1);
|
||||
test('var sum = 0;for (var X = eval("x"); X < 4; ++X) {sum += X;}return sum;', 1, 6);
|
||||
test('var sum = 0;for (var X = x; eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6);
|
||||
test('var sum = 0;for (var X = eval("x"); eval("X") < 4; ++X) {sum += eval("X");}return sum;', 1, 6);
|
||||
test('try {for (let x = eval("throw x");;) {}} catch (e) {return e;}');
|
||||
test('try {for (let x = x + "s"; eval("throw x");) {}} catch (e) {return e;}', 'ponie');
|
||||
test('for (let y = x;;) {let x;return y;}');
|
||||
test('for (let y = x;;) {let y;return x;}');
|
||||
test('for (let y;;) {let y;return x;}');
|
||||
@@ -170,28 +165,31 @@ test('for (let i in x) {return x;}');
|
||||
test('for (let i in x) {let y;return x;}');
|
||||
test('for each (let [a, b] in x) {let y;return x;}');
|
||||
test('for (let i in x) {let i = x;return i;}');
|
||||
test('for each (let [x, y] in x) {return x + y;}', [['ponies', '']]);
|
||||
test('for each (let [{0: x, 1: y}, z] in x) {return x + y + z;}', [[['po','nies'], '']]);
|
||||
test('var s = "";for (let a in x) {for (let b in x) {s += a + b;}}return s;', [1,2], '00011011');
|
||||
test('var res = "";for (let i in x) {res += x[i];}return res;');
|
||||
test('var res = "";for (var i in x) {res += x[i];}return res;');
|
||||
test('for each (let {x: y, y: x} in [{x: x, y: x}]) {return y;}');
|
||||
test('for (let x in eval("x")) {return x;}', {ponies:true});
|
||||
test('for (let x in x) {return eval("x");}', {ponies:true});
|
||||
test('for (let x in eval("x")) {return eval("x");}', {ponies:true});
|
||||
isParseError('for ((let (x = {y: true}) x).y in eval("x")) {return eval("x");}');
|
||||
test('for (let i in x) {break;}return x;');
|
||||
test('for (let i in x) {break;}return eval("x");');
|
||||
test('for (let x in x) {break;}return x;');
|
||||
test('for (let x in x) {break;}return eval("x");');
|
||||
test('a:for (let i in x) {for (let j in x) {break a;}}return x;');
|
||||
test('a:for (let i in x) {for (let j in x) {break a;}}return eval("x");');
|
||||
test('var j;for (let i in x) {j = i;break;}return j;', {ponies:true});
|
||||
test('try {for (let x in eval("throw x")) {}} catch (e) {return e;}');
|
||||
test('try {for each (let x in x) {eval("throw x");}} catch (e) {return e;}', ['ponies']);
|
||||
isParseError('for (let [x, x] in o) {}');
|
||||
isParseError('for (let [x, y, x] in o) {}');
|
||||
isParseError('for (let [x, [y, [x]]] in o) {}');
|
||||
|
||||
// for(let ... in ...) scoping bugs (bug 1069480)
|
||||
test('for each (let [x, y] in x) {return x + y;}', [['ponies', '']], undefined);
|
||||
test('for each (let [{0: x, 1: y}, z] in x) {return x + y + z;}', [[['po','nies'], '']], undefined);
|
||||
test('for (let x in eval("x")) {return x;}', {ponies:true}, undefined);
|
||||
test('for (let x in x) {return eval("x");}', {ponies:true}, undefined);
|
||||
test('for (let x in eval("x")) {return eval("x");}', {ponies:true}, undefined);
|
||||
test('for (let x in x) {break;}return x;');
|
||||
test('for (let x in x) {break;}return eval("x");');
|
||||
test('try {for (let x in eval("throw x")) {}} catch (e) {return e;}', undefined, undefined);
|
||||
test('try {for each (let x in x) {eval("throw x");}} catch (e) {return e;}', ['ponies'], undefined);
|
||||
test('for each (let {x: y, y: x} in [{x: x, y: x}]) {return y;}', undefined, undefined);
|
||||
|
||||
// genexps
|
||||
test('return (i for (i in x)).next();', {ponies:true});
|
||||
test('return (eval("i") for (i in x)).next();', {ponies:true});
|
||||
@@ -222,6 +220,13 @@ isReferenceError('let {x} = {x:x};');
|
||||
isReferenceError('switch (x) {case 3:let x;break;default:if (x === undefined) {return "ponies";}}');
|
||||
isReferenceError('let x = function() {} ? x() : function() {}');
|
||||
isReferenceError('(function() { let x = (function() { return x }()); }())');
|
||||
isReferenceError('var sum = 0;for (let x = x, y = 10; x < 4; ++x) {sum += x;}return sum;');
|
||||
isReferenceError('var sum = 0;for (let x = x; x < 4; ++x) {sum += x;}return x;');
|
||||
isReferenceError('var sum = 0;for (let x = eval("x"); x < 4; ++x) {sum += x;}return sum;');
|
||||
isReferenceError('var sum = 0;for (let x = x; eval("x") < 4; ++x) {sum += eval("x");}return sum;');
|
||||
isReferenceError('var sum = 0;for (let x = eval("x"); eval("x") < 4; ++x) {sum += eval("x");}return sum;');
|
||||
isReferenceError('for (let x = eval("throw x");;) {}');
|
||||
isReferenceError('for (let x = x + "s"; eval("throw x");) {}');
|
||||
|
||||
// redecl with function statements
|
||||
isParseError('let a; function a() {}');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Exercise ModuleDeclarationInstantiation() operation.
|
||||
|
||||
function testModuleEnvironment(module, expected) {
|
||||
var actual = getModuleEnvironmentNames(module);
|
||||
var actual = getModuleEnvironmentNames(module).sort();
|
||||
assertEq(actual.length, expected.length);
|
||||
for (var i = 0; i < actual.length; i++) {
|
||||
assertEq(actual[i], expected[i]);
|
||||
@@ -28,3 +28,17 @@ b.declarationInstantiation();
|
||||
|
||||
testModuleEnvironment(a, ['x']);
|
||||
testModuleEnvironment(b, ['y']);
|
||||
|
||||
// Function bindings are initialized as well as instantiated.
|
||||
let c = parseModule(`function a(x) { return x; }
|
||||
function b(x) { return x + 1; }
|
||||
function c(x) { return x + 2; }
|
||||
function d(x) { return x + 3; }`);
|
||||
const names = ['a', 'b', 'c', 'd'];
|
||||
testModuleEnvironment(c, names);
|
||||
names.forEach((n) => assertEq(typeof getModuleEnvironmentValue(c, n), "undefined"));
|
||||
c.declarationInstantiation();
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
let f = getModuleEnvironmentValue(c, names[i]);
|
||||
assertEq(f(21), 21 + i);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// 'let' after "use strict" directive without semicolon is lexed as TOK_NAME
|
||||
// before parsing the directive. 'let' with TOK_NAME should be handled
|
||||
// correctly in strict mode.
|
||||
|
||||
"use strict"
|
||||
let a = 1;
|
||||
@@ -298,7 +298,7 @@ AtomicOperations::isLockfree(int32_t size)
|
||||
# include "jit/arm/AtomicOperations-arm.h"
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
# include "jit/arm64/AtomicOperations-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips-shared/AtomicOperations-mips-shared.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/AtomicOperations-none.h"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/JitSpewer.h"
|
||||
#include "jit/mips32/Simulator-mips32.h"
|
||||
#include "jit/mips64/Simulator-mips64.h"
|
||||
#include "jit/Recover.h"
|
||||
#include "jit/RematerializedFrame.h"
|
||||
|
||||
@@ -374,7 +375,8 @@ struct BaselineStackBuilder
|
||||
priorOffset -= sizeof(void*);
|
||||
return virtualPointerAtStackOffset(priorOffset);
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
|
||||
defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_MIPS32)
|
||||
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \
|
||||
defined(JS_CODEGEN_X64)
|
||||
// On X64, ARM, ARM64, and MIPS, the frame pointer save location depends on
|
||||
// the caller of the rectifier frame.
|
||||
BufferPointer<RectifierFrameLayout> priorFrame =
|
||||
|
||||
@@ -484,7 +484,7 @@ BaselineCompiler::emitOutOfLinePostBarrierSlot()
|
||||
// On ARM, save the link register before calling. It contains the return
|
||||
// address. The |masm.ret()| later will pop this into |pc| to return.
|
||||
masm.push(lr);
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
masm.push(ra);
|
||||
#endif
|
||||
masm.pushValue(R0);
|
||||
@@ -1964,6 +1964,12 @@ BaselineCompiler::emit_JSOP_INITELEM()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITHIDDENELEM()
|
||||
{
|
||||
return emit_JSOP_INITELEM();
|
||||
}
|
||||
|
||||
typedef bool (*MutateProtoFn)(JSContext* cx, HandlePlainObject obj, HandleValue newProto);
|
||||
static const VMFunction MutateProtoInfo = FunctionInfo<MutateProtoFn>(MutatePrototype);
|
||||
|
||||
@@ -2436,7 +2442,10 @@ BaselineCompiler::emit_JSOP_BINDNAME()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
|
||||
masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
|
||||
if (*pc == JSOP_BINDGNAME && !script->hasNonSyntacticScope())
|
||||
masm.movePtr(ImmGCPtr(&script->global().lexicalScope()), R0.scratchReg());
|
||||
else
|
||||
masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
|
||||
|
||||
// Call IC.
|
||||
ICBindName_Fallback::Compiler stubCompiler(cx);
|
||||
@@ -2566,7 +2575,9 @@ bool
|
||||
BaselineCompiler::emitInitPropGetterSetter()
|
||||
{
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_INITPROP_GETTER ||
|
||||
JSOp(*pc) == JSOP_INITPROP_SETTER);
|
||||
JSOp(*pc) == JSOP_INITHIDDENPROP_GETTER ||
|
||||
JSOp(*pc) == JSOP_INITPROP_SETTER ||
|
||||
JSOp(*pc) == JSOP_INITHIDDENPROP_SETTER);
|
||||
|
||||
// Keep values on the stack for the decompiler.
|
||||
frame.syncStack(0);
|
||||
@@ -2594,12 +2605,24 @@ BaselineCompiler::emit_JSOP_INITPROP_GETTER()
|
||||
return emitInitPropGetterSetter();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITHIDDENPROP_GETTER()
|
||||
{
|
||||
return emitInitPropGetterSetter();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITPROP_SETTER()
|
||||
{
|
||||
return emitInitPropGetterSetter();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITHIDDENPROP_SETTER()
|
||||
{
|
||||
return emitInitPropGetterSetter();
|
||||
}
|
||||
|
||||
typedef bool (*InitElemGetterSetterFn)(JSContext*, jsbytecode*, HandleObject, HandleValue,
|
||||
HandleObject);
|
||||
static const VMFunction InitElemGetterSetterInfo =
|
||||
@@ -2609,7 +2632,9 @@ bool
|
||||
BaselineCompiler::emitInitElemGetterSetter()
|
||||
{
|
||||
MOZ_ASSERT(JSOp(*pc) == JSOP_INITELEM_GETTER ||
|
||||
JSOp(*pc) == JSOP_INITELEM_SETTER);
|
||||
JSOp(*pc) == JSOP_INITHIDDENELEM_GETTER ||
|
||||
JSOp(*pc) == JSOP_INITELEM_SETTER ||
|
||||
JSOp(*pc) == JSOP_INITHIDDENELEM_SETTER);
|
||||
|
||||
// Load index and value in R0 and R1, but keep values on the stack for the
|
||||
// decompiler.
|
||||
@@ -2638,12 +2663,24 @@ BaselineCompiler::emit_JSOP_INITELEM_GETTER()
|
||||
return emitInitElemGetterSetter();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITHIDDENELEM_GETTER()
|
||||
{
|
||||
return emitInitElemGetterSetter();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITELEM_SETTER()
|
||||
{
|
||||
return emitInitElemGetterSetter();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITHIDDENELEM_SETTER()
|
||||
{
|
||||
return emitInitElemGetterSetter();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITELEM_INC()
|
||||
{
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
# include "jit/arm64/BaselineCompiler-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/BaselineCompiler-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/BaselineCompiler-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/BaselineCompiler-none.h"
|
||||
#else
|
||||
@@ -206,7 +208,12 @@ namespace jit {
|
||||
_(JSOP_SUPERCALL) \
|
||||
_(JSOP_SPREADSUPERCALL) \
|
||||
_(JSOP_THROWSETCONST) \
|
||||
_(JSOP_THROWSETALIASEDCONST)
|
||||
_(JSOP_THROWSETALIASEDCONST) \
|
||||
_(JSOP_INITHIDDENPROP_GETTER) \
|
||||
_(JSOP_INITHIDDENPROP_SETTER) \
|
||||
_(JSOP_INITHIDDENELEM) \
|
||||
_(JSOP_INITHIDDENELEM_GETTER) \
|
||||
_(JSOP_INITHIDDENELEM_SETTER)
|
||||
|
||||
class BaselineCompiler : public BaselineCompilerSpecific
|
||||
{
|
||||
|
||||
@@ -3494,6 +3494,7 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||
MOZ_ASSERT(op == JSOP_SETELEM ||
|
||||
op == JSOP_STRICTSETELEM ||
|
||||
op == JSOP_INITELEM ||
|
||||
op == JSOP_INITHIDDENELEM ||
|
||||
op == JSOP_INITELEM_ARRAY ||
|
||||
op == JSOP_INITELEM_INC);
|
||||
|
||||
@@ -3511,8 +3512,8 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||
oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
|
||||
}
|
||||
|
||||
if (op == JSOP_INITELEM) {
|
||||
if (!InitElemOperation(cx, obj, index, rhs))
|
||||
if (op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM) {
|
||||
if (!InitElemOperation(cx, pc, obj, index, rhs))
|
||||
return false;
|
||||
} else if (op == JSOP_INITELEM_ARRAY) {
|
||||
MOZ_ASSERT(uint32_t(index.toInt32()) <= INT32_MAX,
|
||||
@@ -3863,8 +3864,9 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
if (failurePopR0.used()) {
|
||||
// Failure case: restore the value of R0
|
||||
masm.bind(&failurePopR0);
|
||||
masm.Pop(R0);
|
||||
masm.popValue(R0);
|
||||
}
|
||||
|
||||
// Failure case - jump to next stub
|
||||
@@ -4099,8 +4101,9 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm)
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
if (failurePopR0.used()) {
|
||||
// Failure case: restore the value of R0
|
||||
masm.bind(&failurePopR0);
|
||||
masm.Pop(R0);
|
||||
masm.popValue(R0);
|
||||
masm.jump(&failure);
|
||||
}
|
||||
|
||||
|
||||
@@ -1166,6 +1166,11 @@ MarkActiveBaselineScripts(JSRuntime* rt, const JitActivationIterator& activation
|
||||
case JitFrame_BaselineJS:
|
||||
iter.script()->baselineScript()->setActive();
|
||||
break;
|
||||
case JitFrame_LazyLink: {
|
||||
LazyLinkExitFrameLayout* ll = iter.exitFrame()->as<LazyLinkExitFrameLayout>();
|
||||
ScriptFromCalleeToken(ll->jsFrame()->calleeToken())->baselineScript()->setActive();
|
||||
break;
|
||||
}
|
||||
case JitFrame_Bailout:
|
||||
case JitFrame_IonJS: {
|
||||
// Keep the baseline script around, since bailouts from the ion
|
||||
|
||||
@@ -4842,7 +4842,7 @@ CodeGenerator::visitNewStringObject(LNewStringObject* lir)
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
typedef bool(*InitElemFn)(JSContext* cx, HandleObject obj,
|
||||
typedef bool(*InitElemFn)(JSContext* cx, jsbytecode* pc, HandleObject obj,
|
||||
HandleValue id, HandleValue value);
|
||||
static const VMFunction InitElemInfo =
|
||||
FunctionInfo<InitElemFn>(InitElemOperation);
|
||||
@@ -4855,6 +4855,7 @@ CodeGenerator::visitInitElem(LInitElem* lir)
|
||||
pushArg(ToValue(lir, LInitElem::ValueIndex));
|
||||
pushArg(ToValue(lir, LInitElem::IdIndex));
|
||||
pushArg(objReg);
|
||||
pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
|
||||
|
||||
callVM(InitElemInfo, lir);
|
||||
}
|
||||
@@ -5277,7 +5278,7 @@ CodeGenerator::visitAbsI(LAbsI* ins)
|
||||
masm.branchTest32(Assembler::NotSigned, input, input, &positive);
|
||||
masm.neg32(input);
|
||||
LSnapshot* snapshot = ins->snapshot();
|
||||
#ifdef JS_CODEGEN_MIPS32
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
if (snapshot)
|
||||
bailoutCmp32(Assembler::Equal, input, Imm32(INT32_MIN), snapshot);
|
||||
#else
|
||||
@@ -6945,7 +6946,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||
// If index > initializedLength, call a stub. Note that this relies on the
|
||||
// condition flags sticking from the incoming branch.
|
||||
Label callStub;
|
||||
#ifdef JS_CODEGEN_MIPS32
|
||||
#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
// Had to reimplement for MIPS because there are no flags.
|
||||
if (unboxedType == JSVAL_TYPE_MAGIC) {
|
||||
Address initLength(elements, ObjectElements::offsetOfInitializedLength());
|
||||
@@ -7798,7 +7799,7 @@ CodeGenerator::generateAsmJS(AsmJSFunctionLabels* labels)
|
||||
if (!generateOutOfLineCode())
|
||||
return false;
|
||||
|
||||
masm.bind(&labels->end);
|
||||
masm.bind(&labels->endAfterOOL);
|
||||
|
||||
// The only remaining work needed to compile this function is to patch the
|
||||
// switch-statement jump tables (the entries of the table need the absolute
|
||||
@@ -9290,49 +9291,6 @@ CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir)
|
||||
masm.bind(&Ldone);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir)
|
||||
{
|
||||
Register elements = ToRegister(lir->elements());
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
|
||||
|
||||
Register oldval = ToRegister(lir->oldval());
|
||||
Register newval = ToRegister(lir->newval());
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address dest(elements, ToInt32(lir->index()) * width);
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
} else {
|
||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir)
|
||||
{
|
||||
Register elements = ToRegister(lir->elements());
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
|
||||
|
||||
Register value = ToRegister(lir->value());
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address dest(elements, ToInt32(lir->index()) * width);
|
||||
masm.atomicExchangeToTypedIntArray(arrayType, dest, value, temp, output);
|
||||
} else {
|
||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.atomicExchangeToTypedIntArray(arrayType, dest, value, temp, output);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitClampIToUint8(LClampIToUint8* lir)
|
||||
{
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
# include "jit/arm64/CodeGenerator-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/CodeGenerator-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/CodeGenerator-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/CodeGenerator-none.h"
|
||||
#else
|
||||
@@ -286,8 +288,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitStoreUnboxedScalar(LStoreUnboxedScalar* lir);
|
||||
void visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole* lir);
|
||||
void visitAtomicIsLockFree(LAtomicIsLockFree* lir);
|
||||
void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
|
||||
void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
|
||||
void visitClampIToUint8(LClampIToUint8* lir);
|
||||
void visitClampDToUint8(LClampDToUint8* lir);
|
||||
void visitClampVToUint8(LClampVToUint8* lir);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
#include "jit/mips32/Simulator-mips32.h"
|
||||
#include "jit/mips64/Simulator-mips64.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "js/Vector.h"
|
||||
@@ -55,7 +56,9 @@ extern "C" void sync_instruction_memory(caddr_t v, u_int len);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(JS_CODEGEN_MIPS32) && defined(__linux__) && !defined(JS_SIMULATOR_MIPS32)
|
||||
#if defined(__linux__) && \
|
||||
(defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)) && \
|
||||
(!defined(JS_SIMULATOR_MIPS32) && !defined(JS_SIMULATOR_MIPS64))
|
||||
#include <sys/cachectl.h>
|
||||
#endif
|
||||
|
||||
@@ -394,12 +397,12 @@ class ExecutableAllocator
|
||||
static void cacheFlush(void*, size_t)
|
||||
{
|
||||
}
|
||||
#elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32)
|
||||
#elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
js::jit::Simulator::FlushICache(code, size);
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
static void cacheFlush(void* code, size_t size)
|
||||
{
|
||||
#if defined(__GNUC__)
|
||||
|
||||
+7
-7
@@ -3210,7 +3210,7 @@ PerThreadData::setAutoFlushICache(AutoFlushICache* afc)
|
||||
void
|
||||
AutoFlushICache::setRange(uintptr_t start, size_t len)
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
AutoFlushICache* afc = TlsPerThreadData.get()->PerThreadData::autoFlushICache();
|
||||
MOZ_ASSERT(afc);
|
||||
MOZ_ASSERT(!afc->start_);
|
||||
@@ -3243,13 +3243,13 @@ AutoFlushICache::setRange(uintptr_t start, size_t len)
|
||||
void
|
||||
AutoFlushICache::flush(uintptr_t start, size_t len)
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
PerThreadData* pt = TlsPerThreadData.get();
|
||||
AutoFlushICache* afc = pt ? pt->PerThreadData::autoFlushICache() : nullptr;
|
||||
if (!afc) {
|
||||
JitSpewCont(JitSpew_CacheFlush, "#");
|
||||
ExecutableAllocator::cacheFlush((void*)start, len);
|
||||
MOZ_ASSERT(len <= 16);
|
||||
MOZ_ASSERT(len <= 32);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3270,7 +3270,7 @@ AutoFlushICache::flush(uintptr_t start, size_t len)
|
||||
void
|
||||
AutoFlushICache::setInhibit()
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
AutoFlushICache* afc = TlsPerThreadData.get()->PerThreadData::autoFlushICache();
|
||||
MOZ_ASSERT(afc);
|
||||
MOZ_ASSERT(afc->start_);
|
||||
@@ -3297,14 +3297,14 @@ AutoFlushICache::setInhibit()
|
||||
// the respective AutoFlushICache dynamic context.
|
||||
//
|
||||
AutoFlushICache::AutoFlushICache(const char* nonce, bool inhibit)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
: start_(0),
|
||||
stop_(0),
|
||||
name_(nonce),
|
||||
inhibit_(inhibit)
|
||||
#endif
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
PerThreadData* pt = TlsPerThreadData.get();
|
||||
AutoFlushICache* afc = pt->PerThreadData::autoFlushICache();
|
||||
if (afc)
|
||||
@@ -3319,7 +3319,7 @@ AutoFlushICache::AutoFlushICache(const char* nonce, bool inhibit)
|
||||
|
||||
AutoFlushICache::~AutoFlushICache()
|
||||
{
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
PerThreadData* pt = TlsPerThreadData.get();
|
||||
MOZ_ASSERT(pt->PerThreadData::autoFlushICache() == this);
|
||||
|
||||
|
||||
@@ -1843,6 +1843,7 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
return jsop_newobject();
|
||||
|
||||
case JSOP_INITELEM:
|
||||
case JSOP_INITHIDDENELEM:
|
||||
return jsop_initelem();
|
||||
|
||||
case JSOP_INITELEM_ARRAY:
|
||||
@@ -1862,13 +1863,17 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
}
|
||||
|
||||
case JSOP_INITPROP_GETTER:
|
||||
case JSOP_INITPROP_SETTER: {
|
||||
case JSOP_INITHIDDENPROP_GETTER:
|
||||
case JSOP_INITPROP_SETTER:
|
||||
case JSOP_INITHIDDENPROP_SETTER: {
|
||||
PropertyName* name = info().getAtom(pc)->asPropertyName();
|
||||
return jsop_initprop_getter_setter(name);
|
||||
}
|
||||
|
||||
case JSOP_INITELEM_GETTER:
|
||||
case JSOP_INITHIDDENELEM_GETTER:
|
||||
case JSOP_INITELEM_SETTER:
|
||||
case JSOP_INITHIDDENELEM_SETTER:
|
||||
return jsop_initelem_getter_setter();
|
||||
|
||||
case JSOP_FUNCALL:
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
# include "jit/arm64/Assembler-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/Assembler-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/Assembler-mips64.h"
|
||||
#endif
|
||||
#include "jit/JitCompartment.h"
|
||||
#include "jit/Registers.h"
|
||||
|
||||
@@ -743,7 +743,7 @@ struct VMFunction;
|
||||
struct AutoFlushICache
|
||||
{
|
||||
private:
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
uintptr_t start_;
|
||||
uintptr_t stop_;
|
||||
const char* name_;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# include "jit/arm64/vixl/Simulator-vixl.h"
|
||||
#elif defined(JS_SIMULATOR_MIPS32)
|
||||
#include "jit/mips32/Simulator-mips32.h"
|
||||
#elif defined(JS_SIMULATOR_MIPS64)
|
||||
#include "jit/mips64/Simulator-mips64.h"
|
||||
#endif
|
||||
|
||||
#ifdef JS_SIMULATOR
|
||||
|
||||
@@ -2578,6 +2578,11 @@ MachineState::FromBailout(RegisterDump::GPRArray& regs, RegisterDump::FPUArray&
|
||||
machine.setRegisterLocation(FloatRegister::FromIndex(i, FloatRegister::Single),
|
||||
(double*)&fbase[i]);
|
||||
}
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) {
|
||||
machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Double), &fpregs[i]);
|
||||
machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]);
|
||||
}
|
||||
#elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
|
||||
for (unsigned i = 0; i < FloatRegisters::TotalPhys; i++) {
|
||||
machine.setRegisterLocation(FloatRegister(i, FloatRegisters::Single), &fpregs[i]);
|
||||
|
||||
+3
-1
@@ -1827,9 +1827,11 @@ LAllocation::toRegister() const
|
||||
# include "jit/arm/LIR-arm.h"
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
# include "jit/arm64/LIR-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
# if defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/LIR-mips32.h"
|
||||
# elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/LIR-mips64.h"
|
||||
# endif
|
||||
# include "jit/mips-shared/LIR-mips-shared.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
# include "jit/arm64/LOpcodes-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/LOpcodes-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/LOpcodes-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/LOpcodes-none.h"
|
||||
#else
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
# include "jit/arm64/Lowering-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/Lowering-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/Lowering-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/Lowering-none.h"
|
||||
#else
|
||||
|
||||
+3
-3
@@ -13696,17 +13696,17 @@ class MAsmJSCall final
|
||||
private:
|
||||
Which which_;
|
||||
union {
|
||||
Label* internal_;
|
||||
AsmJSInternalCallee internal_;
|
||||
MDefinition* dynamic_;
|
||||
AsmJSImmKind builtin_;
|
||||
} u;
|
||||
public:
|
||||
Callee() {}
|
||||
explicit Callee(Label* callee) : which_(Internal) { u.internal_ = callee; }
|
||||
explicit Callee(AsmJSInternalCallee callee) : which_(Internal) { u.internal_ = callee; }
|
||||
explicit Callee(MDefinition* callee) : which_(Dynamic) { u.dynamic_ = callee; }
|
||||
explicit Callee(AsmJSImmKind callee) : which_(Builtin) { u.builtin_ = callee; }
|
||||
Which which() const { return which_; }
|
||||
Label* internal() const { MOZ_ASSERT(which_ == Internal); return u.internal_; }
|
||||
AsmJSInternalCallee internal() const { MOZ_ASSERT(which_ == Internal); return u.internal_; }
|
||||
MDefinition* dynamic() const { MOZ_ASSERT(which_ == Dynamic); return u.dynamic_; }
|
||||
AsmJSImmKind builtin() const { MOZ_ASSERT(which_ == Builtin); return u.builtin_; }
|
||||
};
|
||||
|
||||
+15
-4
@@ -345,17 +345,28 @@ MBasicBlock::NewAsmJS(MIRGraph& graph, CompileInfo& info, MBasicBlock* pred, Kin
|
||||
if (block->kind_ == PENDING_LOOP_HEADER) {
|
||||
size_t nphis = block->stackPosition_;
|
||||
|
||||
size_t nfree = graph.phiFreeListLength();
|
||||
|
||||
TempAllocator& alloc = graph.alloc();
|
||||
MPhi* phis = alloc.allocateArray<MPhi>(nphis);
|
||||
if (!phis)
|
||||
return nullptr;
|
||||
MPhi* phis = nullptr;
|
||||
if (nphis > nfree) {
|
||||
phis = alloc.allocateArray<MPhi>(nphis - nfree);
|
||||
if (!phis)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Note: Phis are inserted in the same order as the slots.
|
||||
for (size_t i = 0; i < nphis; i++) {
|
||||
MDefinition* predSlot = pred->getSlot(i);
|
||||
|
||||
MOZ_ASSERT(predSlot->type() != MIRType_Value);
|
||||
MPhi* phi = new(phis + i) MPhi(alloc, predSlot->type());
|
||||
|
||||
MPhi* phi;
|
||||
if (i < nfree)
|
||||
phi = graph.takePhiFromFreeList();
|
||||
else
|
||||
phi = phis + (i - nfree);
|
||||
new(phi) MPhi(alloc, predSlot->type());
|
||||
|
||||
phi->addInput(predSlot);
|
||||
|
||||
|
||||
+18
-1
@@ -693,6 +693,9 @@ class MIRGraph
|
||||
size_t numBlocks_;
|
||||
bool hasTryBlock_;
|
||||
|
||||
InlineList<MPhi> phiFreeList_;
|
||||
size_t phiFreeListLength_;
|
||||
|
||||
public:
|
||||
explicit MIRGraph(TempAllocator* alloc)
|
||||
: alloc_(alloc),
|
||||
@@ -701,7 +704,8 @@ class MIRGraph
|
||||
idGen_(0),
|
||||
osrBlock_(nullptr),
|
||||
numBlocks_(0),
|
||||
hasTryBlock_(false)
|
||||
hasTryBlock_(false),
|
||||
phiFreeListLength_(0)
|
||||
{ }
|
||||
|
||||
TempAllocator& alloc() const {
|
||||
@@ -812,6 +816,19 @@ class MIRGraph
|
||||
|
||||
void dump(GenericPrinter& out);
|
||||
void dump();
|
||||
|
||||
void addPhiToFreeList(MPhi* phi) {
|
||||
phiFreeList_.pushBack(phi);
|
||||
phiFreeListLength_++;
|
||||
}
|
||||
size_t phiFreeListLength() const {
|
||||
return phiFreeListLength_;
|
||||
}
|
||||
MPhi* takePhiFromFreeList() {
|
||||
MOZ_ASSERT(phiFreeListLength_ > 0);
|
||||
phiFreeListLength_--;
|
||||
return phiFreeList_.popBack();
|
||||
}
|
||||
};
|
||||
|
||||
class MDefinitionIterator
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
# include "jit/arm64/MacroAssembler-arm64-inl.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/MacroAssembler-mips32-inl.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/MacroAssembler-mips64-inl.h"
|
||||
#elif !defined(JS_CODEGEN_NONE)
|
||||
# error "Unknown architecture!"
|
||||
#endif
|
||||
@@ -79,15 +81,22 @@ MacroAssembler::PushWithPatch(ImmPtr imm)
|
||||
void
|
||||
MacroAssembler::call(const CallSiteDesc& desc, const Register reg)
|
||||
{
|
||||
call(reg);
|
||||
append(desc, currentOffset(), framePushed());
|
||||
CodeOffsetLabel l = call(reg);
|
||||
append(desc, l, framePushed());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::call(const CallSiteDesc& desc, Label* label)
|
||||
{
|
||||
call(label);
|
||||
append(desc, currentOffset(), framePushed());
|
||||
CodeOffsetLabel l = call(label);
|
||||
append(desc, l, framePushed());
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::call(const CallSiteDesc& desc, AsmJSInternalCallee callee)
|
||||
{
|
||||
CodeOffsetLabel l = callWithPatch();
|
||||
append(desc, l, framePushed(), callee.index);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
|
||||
@@ -446,95 +446,6 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const A
|
||||
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, const ValueOperand& dest,
|
||||
bool allowDouble, Register temp, Label* fail);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
|
||||
Register oldval, Register newval,
|
||||
Register temp, AnyRegister output)
|
||||
{
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
compareExchange8SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8Clamped:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
compareExchange16SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
compareExchange32(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
compareExchange32(mem, oldval, newval, temp);
|
||||
convertUInt32ToDouble(temp, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
template void
|
||||
MacroAssembler::compareExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssembler::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
|
||||
Register value, Register temp, AnyRegister output)
|
||||
{
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
atomicExchange8SignExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
atomicExchange8ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8Clamped:
|
||||
atomicExchange8ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
atomicExchange16SignExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
atomicExchange16ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
atomicExchange32(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
atomicExchange32(mem, value, temp);
|
||||
convertUInt32ToDouble(temp, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssembler::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
|
||||
Register value, Register temp, AnyRegister output);
|
||||
template void
|
||||
MacroAssembler::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
|
||||
Register value, Register temp, AnyRegister output);
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output)
|
||||
|
||||
+25
-16
@@ -22,6 +22,8 @@
|
||||
# include "jit/arm64/MacroAssembler-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/MacroAssembler-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/MacroAssembler-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/MacroAssembler-none.h"
|
||||
#else
|
||||
@@ -62,8 +64,8 @@
|
||||
// architectures on each method declaration, such as PER_ARCH and
|
||||
// PER_SHARED_ARCH.
|
||||
|
||||
# define ALL_ARCH mips32, arm, arm64, x86, x64
|
||||
# define ALL_SHARED_ARCH mips32, arm, arm64, x86_shared
|
||||
# define ALL_ARCH mips32, mips64, arm, arm64, x86, x64
|
||||
# define ALL_SHARED_ARCH arm, arm64, x86_shared, mips_shared
|
||||
|
||||
// * How this macro works:
|
||||
//
|
||||
@@ -107,6 +109,8 @@
|
||||
# define DEFINED_ON_arm
|
||||
# define DEFINED_ON_arm64
|
||||
# define DEFINED_ON_mips32
|
||||
# define DEFINED_ON_mips64
|
||||
# define DEFINED_ON_mips_shared
|
||||
# define DEFINED_ON_none
|
||||
|
||||
// Specialize for each architecture.
|
||||
@@ -129,6 +133,13 @@
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# undef DEFINED_ON_mips32
|
||||
# define DEFINED_ON_mips32 define
|
||||
# undef DEFINED_ON_mips_shared
|
||||
# define DEFINED_ON_mips_shared define
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# undef DEFINED_ON_mips64
|
||||
# define DEFINED_ON_mips64 define
|
||||
# undef DEFINED_ON_mips_shared
|
||||
# define DEFINED_ON_mips_shared define
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# undef DEFINED_ON_none
|
||||
# define DEFINED_ON_none crash
|
||||
@@ -417,12 +428,14 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
// ===============================================================
|
||||
// Stack manipulation functions.
|
||||
|
||||
void PushRegsInMask(LiveRegisterSet set) PER_SHARED_ARCH;
|
||||
void PushRegsInMask(LiveRegisterSet set)
|
||||
DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
|
||||
void PushRegsInMask(LiveGeneralRegisterSet set);
|
||||
|
||||
void PopRegsInMask(LiveRegisterSet set);
|
||||
void PopRegsInMask(LiveGeneralRegisterSet set);
|
||||
void PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore) PER_SHARED_ARCH;
|
||||
void PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
|
||||
DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
|
||||
|
||||
void Push(const Operand op) DEFINED_ON(x86_shared);
|
||||
void Push(Register reg) PER_SHARED_ARCH;
|
||||
@@ -471,9 +484,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
// ===============================================================
|
||||
// Simple call functions.
|
||||
|
||||
void call(Register reg) PER_SHARED_ARCH;
|
||||
CodeOffsetLabel call(Register reg) PER_SHARED_ARCH;
|
||||
CodeOffsetLabel call(Label* label) PER_SHARED_ARCH;
|
||||
void call(const Address& addr) DEFINED_ON(x86_shared);
|
||||
void call(Label* label) PER_SHARED_ARCH;
|
||||
void call(ImmWord imm) PER_SHARED_ARCH;
|
||||
// Call a target native function, which is neither traceable nor movable.
|
||||
void call(ImmPtr imm) PER_SHARED_ARCH;
|
||||
@@ -483,12 +496,16 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
|
||||
inline void call(const CallSiteDesc& desc, const Register reg);
|
||||
inline void call(const CallSiteDesc& desc, Label* label);
|
||||
inline void call(const CallSiteDesc& desc, AsmJSInternalCallee callee);
|
||||
|
||||
CodeOffsetLabel callWithPatch() PER_SHARED_ARCH;
|
||||
void patchCall(uint32_t callerOffset, uint32_t calleeOffset) PER_SHARED_ARCH;
|
||||
|
||||
// Push the return address and make a call. On platforms where this function
|
||||
// is not defined, push the link register (pushReturnAddress) at the entry
|
||||
// point of the callee.
|
||||
void callAndPushReturnAddress(Register reg) DEFINED_ON(mips32, x86_shared);
|
||||
void callAndPushReturnAddress(Label* label) DEFINED_ON(mips32, x86_shared);
|
||||
void callAndPushReturnAddress(Register reg) DEFINED_ON(mips_shared, x86_shared);
|
||||
void callAndPushReturnAddress(Label* label) DEFINED_ON(mips_shared, x86_shared);
|
||||
|
||||
void pushReturnAddress() DEFINED_ON(arm, arm64);
|
||||
|
||||
@@ -1069,14 +1086,6 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register oldval, Register newval,
|
||||
Register temp, AnyRegister output);
|
||||
|
||||
template<typename T>
|
||||
void atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register value,
|
||||
Register temp, AnyRegister output);
|
||||
|
||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex& dest,
|
||||
unsigned numElems = 0);
|
||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest,
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# include "jit/arm64/MoveEmitter-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/MoveEmitter-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/MoveEmitter-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/MoveEmitter-none.h"
|
||||
#else
|
||||
|
||||
@@ -279,7 +279,7 @@ class RegisterAllocator
|
||||
if (mir->compilingAsmJS()) {
|
||||
#if defined(JS_CODEGEN_X64)
|
||||
allRegisters_.take(AnyRegister(HeapReg));
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
allRegisters_.take(AnyRegister(HeapReg));
|
||||
allRegisters_.take(AnyRegister(GlobalReg));
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
|
||||
@@ -1309,7 +1309,7 @@ SavedNonVolatileRegisters(AllocatableGeneralRegisterSet unused)
|
||||
result.add(Register::FromCode(Registers::lr));
|
||||
#elif defined(JS_CODEGEN_ARM64)
|
||||
result.add(Register::FromCode(Registers::lr));
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
result.add(Register::FromCode(Registers::ra));
|
||||
#endif
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
# include "jit/arm64/Architecture-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/Architecture-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/Architecture-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/Architecture-none.h"
|
||||
#else
|
||||
|
||||
@@ -852,7 +852,7 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler& masm, Register obj, Val
|
||||
masm.branchValueIsNurseryObject(Assembler::NotEqual, val, scratch, &skipBarrier);
|
||||
|
||||
// void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
|
||||
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
saveRegs.add(ICTailCallReg);
|
||||
#endif
|
||||
saveRegs.set() = GeneralRegisterSet::Intersect(saveRegs.set(), GeneralRegisterSet::Volatile());
|
||||
|
||||
@@ -1026,7 +1026,7 @@ class ICStubCompiler
|
||||
MOZ_ASSERT(!regs.has(BaselineStackReg));
|
||||
MOZ_ASSERT(!regs.has(ICTailCallReg));
|
||||
regs.take(BaselineSecondScratchReg);
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
|
||||
MOZ_ASSERT(!regs.has(BaselineStackReg));
|
||||
MOZ_ASSERT(!regs.has(ICTailCallReg));
|
||||
MOZ_ASSERT(!regs.has(BaselineSecondScratchReg));
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
# include "jit/arm64/SharedICHelpers-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/SharedICHelpers-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/SharedICHelpers-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/SharedICHelpers-none.h"
|
||||
#else
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
# include "jit/arm64/SharedICRegisters-arm64.h"
|
||||
#elif defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/SharedICRegisters-mips32.h"
|
||||
#elif defined(JS_CODEGEN_MIPS64)
|
||||
# include "jit/mips64/SharedICRegisters-mips64.h"
|
||||
#elif defined(JS_CODEGEN_NONE)
|
||||
# include "jit/none/SharedICRegisters-none.h"
|
||||
#else
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "jit/JitCompartment.h"
|
||||
#include "jit/JitFrames.h"
|
||||
#include "jit/mips32/Simulator-mips32.h"
|
||||
#include "jit/mips64/Simulator-mips64.h"
|
||||
#include "vm/ArrayObject.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/Interpreter.h"
|
||||
|
||||
@@ -1689,6 +1689,48 @@ CodeGeneratorARM::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStati
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir)
|
||||
{
|
||||
Register elements = ToRegister(lir->elements());
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
|
||||
|
||||
Register oldval = ToRegister(lir->oldval());
|
||||
Register newval = ToRegister(lir->newval());
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address dest(elements, ToInt32(lir->index()) * width);
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
} else {
|
||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir)
|
||||
{
|
||||
Register elements = ToRegister(lir->elements());
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
|
||||
|
||||
Register value = ToRegister(lir->value());
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address dest(elements, ToInt32(lir->index()) * width);
|
||||
masm.atomicExchangeToTypedIntArray(arrayType, dest, value, temp, output);
|
||||
} else {
|
||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.atomicExchangeToTypedIntArray(arrayType, dest, value, temp, output);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
void
|
||||
|
||||
@@ -196,6 +196,8 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
||||
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
|
||||
void visitAtomicTypedArrayElementBinop(LAtomicTypedArrayElementBinop* lir);
|
||||
void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir);
|
||||
void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
|
||||
void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
|
||||
void visitAsmJSCall(LAsmJSCall* ins);
|
||||
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
|
||||
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
|
||||
|
||||
@@ -4786,6 +4786,95 @@ template void
|
||||
js::jit::MacroAssemblerARMCompat::atomicEffectOp(int nbytes, AtomicOp op, const Register& value,
|
||||
const BaseIndex& mem, Register flagTemp);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
|
||||
Register oldval, Register newval,
|
||||
Register temp, AnyRegister output)
|
||||
{
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
compareExchange8SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8Clamped:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
compareExchange16SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
compareExchange32(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
compareExchange32(mem, oldval, newval, temp);
|
||||
convertUInt32ToDouble(temp, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssemblerARMCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
template void
|
||||
MacroAssemblerARMCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
|
||||
Register value, Register temp, AnyRegister output)
|
||||
{
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
atomicExchange8SignExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
atomicExchange8ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8Clamped:
|
||||
atomicExchange8ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
atomicExchange16SignExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
atomicExchange16ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
atomicExchange32(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
atomicExchange32(mem, value, temp);
|
||||
convertUInt32ToDouble(temp, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
|
||||
Register value, Register temp, AnyRegister output);
|
||||
template void
|
||||
MacroAssemblerARMCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
|
||||
Register value, Register temp, AnyRegister output);
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::profilerEnterFrame(Register framePtr, Register scratch)
|
||||
{
|
||||
@@ -4968,17 +5057,19 @@ MacroAssembler::reserveStack(uint32_t amount)
|
||||
// ===============================================================
|
||||
// Simple call functions.
|
||||
|
||||
void
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::call(Register reg)
|
||||
{
|
||||
as_blx(reg);
|
||||
return CodeOffsetLabel(currentOffset());
|
||||
}
|
||||
|
||||
void
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::call(Label* label)
|
||||
{
|
||||
// For now, assume that it'll be nearby?
|
||||
// For now, assume that it'll be nearby.
|
||||
as_bl(label, Always);
|
||||
return CodeOffsetLabel(currentOffset());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -5018,6 +5109,20 @@ MacroAssembler::call(JitCode* c)
|
||||
callJitNoProfiler(scratch);
|
||||
}
|
||||
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::callWithPatch()
|
||||
{
|
||||
// For now, assume that it'll be nearby.
|
||||
as_bl(BOffImm(), Always, /* documentation */ nullptr);
|
||||
return CodeOffsetLabel(currentOffset());
|
||||
}
|
||||
void
|
||||
MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
BufferOffset inst(callerOffset - 4);
|
||||
as_bl(BufferOffset(calleeOffset).diffB<BOffImm>(inst), Always, inst);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushReturnAddress()
|
||||
{
|
||||
|
||||
@@ -1617,6 +1617,14 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
atomicEffectOp(4, AtomicFetchXorOp, value, mem, flagTemp);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register oldval, Register newval,
|
||||
Register temp, AnyRegister output);
|
||||
|
||||
template<typename T>
|
||||
void atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register value,
|
||||
Register temp, AnyRegister output);
|
||||
|
||||
void clampIntToUint8(Register reg) {
|
||||
// Look at (reg >> 8) if it is 0, then reg shouldn't be clamped if it is
|
||||
// <0, then we want to clamp to 0, otherwise, we wish to clamp to 255
|
||||
|
||||
@@ -737,3 +737,47 @@ CodeGeneratorARM64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
regs->add(s1);
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir)
|
||||
{
|
||||
Register elements = ToRegister(lir->elements());
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
|
||||
|
||||
Register oldval = ToRegister(lir->oldval());
|
||||
Register newval = ToRegister(lir->newval());
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address dest(elements, ToInt32(lir->index()) * width);
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
} else {
|
||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.compareExchangeToTypedIntArray(arrayType, dest, oldval, newval, temp, output);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir)
|
||||
{
|
||||
Register elements = ToRegister(lir->elements());
|
||||
AnyRegister output = ToAnyRegister(lir->output());
|
||||
Register temp = lir->temp()->isBogusTemp() ? InvalidReg : ToRegister(lir->temp());
|
||||
|
||||
Register value = ToRegister(lir->value());
|
||||
|
||||
Scalar::Type arrayType = lir->mir()->arrayType();
|
||||
int width = Scalar::byteSize(arrayType);
|
||||
|
||||
if (lir->index()->isConstant()) {
|
||||
Address dest(elements, ToInt32(lir->index()) * width);
|
||||
masm.atomicExchangeToTypedIntArray(arrayType, dest, value, temp, output);
|
||||
} else {
|
||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width));
|
||||
masm.atomicExchangeToTypedIntArray(arrayType, dest, value, temp, output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -207,6 +207,8 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
|
||||
void visitNegF(LNegF* lir);
|
||||
void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
|
||||
void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
|
||||
void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir);
|
||||
void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir);
|
||||
void visitAsmJSCall(LAsmJSCall* ins);
|
||||
void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
|
||||
void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
|
||||
|
||||
@@ -255,6 +255,95 @@ MacroAssemblerCompat::breakpoint()
|
||||
Brk((code++) & 0xffff);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
|
||||
Register oldval, Register newval,
|
||||
Register temp, AnyRegister output)
|
||||
{
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
compareExchange8SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8Clamped:
|
||||
compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
compareExchange16SignExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
compareExchange32(mem, oldval, newval, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
compareExchange32(mem, oldval, newval, temp);
|
||||
convertUInt32ToDouble(temp, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssemblerCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
template void
|
||||
MacroAssemblerCompat::compareExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
|
||||
Register oldval, Register newval, Register temp,
|
||||
AnyRegister output);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
MacroAssemblerCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem,
|
||||
Register value, Register temp, AnyRegister output)
|
||||
{
|
||||
switch (arrayType) {
|
||||
case Scalar::Int8:
|
||||
atomicExchange8SignExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8:
|
||||
atomicExchange8ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint8Clamped:
|
||||
atomicExchange8ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Int16:
|
||||
atomicExchange16SignExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint16:
|
||||
atomicExchange16ZeroExtend(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Int32:
|
||||
atomicExchange32(mem, value, output.gpr());
|
||||
break;
|
||||
case Scalar::Uint32:
|
||||
// At the moment, the code in MCallOptimize.cpp requires the output
|
||||
// type to be double for uint32 arrays. See bug 1077305.
|
||||
MOZ_ASSERT(output.isFloat());
|
||||
atomicExchange32(mem, value, temp);
|
||||
convertUInt32ToDouble(temp, output.fpu());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid typed array type");
|
||||
}
|
||||
}
|
||||
|
||||
template void
|
||||
MacroAssemblerCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const Address& mem,
|
||||
Register value, Register temp, AnyRegister output);
|
||||
template void
|
||||
MacroAssemblerCompat::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
|
||||
Register value, Register temp, AnyRegister output);
|
||||
|
||||
//{{{ check_macroassembler_style
|
||||
// ===============================================================
|
||||
// Stack manipulation functions.
|
||||
@@ -408,18 +497,20 @@ MacroAssembler::reserveStack(uint32_t amount)
|
||||
// ===============================================================
|
||||
// Simple call functions.
|
||||
|
||||
void
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::call(Register reg)
|
||||
{
|
||||
syncStackPtr();
|
||||
Blr(ARMRegister(reg, 64));
|
||||
return CodeOffsetLabel(currentOffset());
|
||||
}
|
||||
|
||||
void
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::call(Label* label)
|
||||
{
|
||||
syncStackPtr();
|
||||
Bl(label);
|
||||
return CodeOffsetLabel(currentOffset());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -457,6 +548,18 @@ MacroAssembler::call(JitCode* c)
|
||||
blr(scratch64);
|
||||
}
|
||||
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::callWithPatch()
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
return CodeOffsetLabel();
|
||||
}
|
||||
void
|
||||
MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::pushReturnAddress()
|
||||
{
|
||||
|
||||
@@ -2863,6 +2863,14 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
atomicEffectOp(4, AtomicFetchXorOp, value, mem);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void compareExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register oldval, Register newval,
|
||||
Register temp, AnyRegister output);
|
||||
|
||||
template<typename T>
|
||||
void atomicExchangeToTypedIntArray(Scalar::Type arrayType, const T& mem, Register value,
|
||||
Register temp, AnyRegister output);
|
||||
|
||||
// Emit a BLR or NOP instruction. ToggleCall can be used to patch
|
||||
// this instruction.
|
||||
CodeOffsetLabel toggledCall(JitCode* target, bool enabled) {
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips_shared_MacroAssembler_mips_shared_inl_h
|
||||
#define jit_mips_shared_MacroAssembler_mips_shared_inl_h
|
||||
|
||||
#include "jit/mips-shared/MacroAssembler-mips-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
//{{{ check_macroassembler_style
|
||||
// ===============================================================
|
||||
// Logical instructions
|
||||
|
||||
void
|
||||
MacroAssembler::not32(Register reg)
|
||||
{
|
||||
ma_not(reg, reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(Register src, Register dest)
|
||||
{
|
||||
as_and(dest, dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_and(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(Imm32 imm, const Address& dest)
|
||||
{
|
||||
load32(dest, SecondScratchReg);
|
||||
ma_and(SecondScratchReg, imm);
|
||||
store32(SecondScratchReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(const Address& src, Register dest)
|
||||
{
|
||||
load32(src, SecondScratchReg);
|
||||
ma_and(dest, SecondScratchReg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Register src, Register dest)
|
||||
{
|
||||
ma_or(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_or(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Imm32 imm, const Address& dest)
|
||||
{
|
||||
load32(dest, SecondScratchReg);
|
||||
ma_or(SecondScratchReg, imm);
|
||||
store32(SecondScratchReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_xor(dest, imm);
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips_shared_MacroAssembler_mips_shared_inl_h */
|
||||
@@ -0,0 +1,949 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/mips-shared/MacroAssembler-mips-shared.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace jit;
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_move(Register rd, Register rs)
|
||||
{
|
||||
as_or(rd, rs, zero);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_li(Register dest, ImmGCPtr ptr)
|
||||
{
|
||||
writeDataRelocation(ptr);
|
||||
asMasm().ma_liPatchable(dest, ImmPtr(ptr.value));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_li(Register dest, Imm32 imm)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(imm.value)) {
|
||||
as_addiu(dest, zero, imm.value);
|
||||
} else if (Imm16::IsInUnsignedRange(imm.value)) {
|
||||
as_ori(dest, zero, Imm16::Lower(imm).encode());
|
||||
} else if (Imm16::Lower(imm).encode() == 0) {
|
||||
as_lui(dest, Imm16::Upper(imm).encode());
|
||||
} else {
|
||||
as_lui(dest, Imm16::Upper(imm).encode());
|
||||
as_ori(dest, dest, Imm16::Lower(imm).encode());
|
||||
}
|
||||
}
|
||||
|
||||
// Shifts
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_sll(Register rd, Register rt, Imm32 shift)
|
||||
{
|
||||
as_sll(rd, rt, shift.value % 32);
|
||||
}
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_srl(Register rd, Register rt, Imm32 shift)
|
||||
{
|
||||
as_srl(rd, rt, shift.value % 32);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_sra(Register rd, Register rt, Imm32 shift)
|
||||
{
|
||||
as_sra(rd, rt, shift.value % 32);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_ror(Register rd, Register rt, Imm32 shift)
|
||||
{
|
||||
as_rotr(rd, rt, shift.value % 32);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_rol(Register rd, Register rt, Imm32 shift)
|
||||
{
|
||||
as_rotr(rd, rt, 32 - (shift.value % 32));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_sll(Register rd, Register rt, Register shift)
|
||||
{
|
||||
as_sllv(rd, rt, shift);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_srl(Register rd, Register rt, Register shift)
|
||||
{
|
||||
as_srlv(rd, rt, shift);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_sra(Register rd, Register rt, Register shift)
|
||||
{
|
||||
as_srav(rd, rt, shift);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_ror(Register rd, Register rt, Register shift)
|
||||
{
|
||||
as_rotrv(rd, rt, shift);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_rol(Register rd, Register rt, Register shift)
|
||||
{
|
||||
ma_negu(ScratchRegister, shift);
|
||||
as_rotrv(rd, rt, ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_negu(Register rd, Register rs)
|
||||
{
|
||||
as_subu(rd, zero, rs);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_not(Register rd, Register rs)
|
||||
{
|
||||
as_nor(rd, rs, zero);
|
||||
}
|
||||
|
||||
// And.
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_and(Register rd, Register rs)
|
||||
{
|
||||
as_and(rd, rd, rs);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_and(Register rd, Imm32 imm)
|
||||
{
|
||||
ma_and(rd, rd, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_and(Register rd, Register rs, Imm32 imm)
|
||||
{
|
||||
if (Imm16::IsInUnsignedRange(imm.value)) {
|
||||
as_andi(rd, rs, imm.value);
|
||||
} else {
|
||||
ma_li(ScratchRegister, imm);
|
||||
as_and(rd, rs, ScratchRegister);
|
||||
}
|
||||
}
|
||||
|
||||
// Or.
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_or(Register rd, Register rs)
|
||||
{
|
||||
as_or(rd, rd, rs);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_or(Register rd, Imm32 imm)
|
||||
{
|
||||
ma_or(rd, rd, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_or(Register rd, Register rs, Imm32 imm)
|
||||
{
|
||||
if (Imm16::IsInUnsignedRange(imm.value)) {
|
||||
as_ori(rd, rs, imm.value);
|
||||
} else {
|
||||
ma_li(ScratchRegister, imm);
|
||||
as_or(rd, rs, ScratchRegister);
|
||||
}
|
||||
}
|
||||
|
||||
// xor
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_xor(Register rd, Register rs)
|
||||
{
|
||||
as_xor(rd, rd, rs);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_xor(Register rd, Imm32 imm)
|
||||
{
|
||||
ma_xor(rd, rd, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_xor(Register rd, Register rs, Imm32 imm)
|
||||
{
|
||||
if (Imm16::IsInUnsignedRange(imm.value)) {
|
||||
as_xori(rd, rs, imm.value);
|
||||
} else {
|
||||
ma_li(ScratchRegister, imm);
|
||||
as_xor(rd, rs, ScratchRegister);
|
||||
}
|
||||
}
|
||||
|
||||
// Arithmetic-based ops.
|
||||
|
||||
// Add.
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_addu(Register rd, Register rs, Imm32 imm)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(imm.value)) {
|
||||
as_addiu(rd, rs, imm.value);
|
||||
} else {
|
||||
ma_li(ScratchRegister, imm);
|
||||
as_addu(rd, rs, ScratchRegister);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_addu(Register rd, Register rs)
|
||||
{
|
||||
as_addu(rd, rd, rs);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_addu(Register rd, Imm32 imm)
|
||||
{
|
||||
ma_addu(rd, rd, imm);
|
||||
}
|
||||
|
||||
// Subtract.
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_subu(Register rd, Register rs, Imm32 imm)
|
||||
{
|
||||
if (Imm16::IsInSignedRange(-imm.value)) {
|
||||
as_addiu(rd, rs, -imm.value);
|
||||
} else {
|
||||
ma_li(ScratchRegister, imm);
|
||||
as_subu(rd, rs, ScratchRegister);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_subu(Register rd, Imm32 imm)
|
||||
{
|
||||
ma_subu(rd, rd, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow)
|
||||
{
|
||||
if (imm.value != INT32_MIN) {
|
||||
asMasm().ma_addTestOverflow(rd, rs, Imm32(-imm.value), overflow);
|
||||
} else {
|
||||
ma_li(ScratchRegister, Imm32(imm.value));
|
||||
asMasm().ma_subTestOverflow(rd, rs, ScratchRegister, overflow);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_mult(Register rs, Imm32 imm)
|
||||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
as_mult(rs, ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label* overflow)
|
||||
{
|
||||
as_mult(rs, rt);
|
||||
as_mflo(rd);
|
||||
as_sra(ScratchRegister, rd, 31);
|
||||
as_mfhi(SecondScratchReg);
|
||||
ma_b(ScratchRegister, SecondScratchReg, overflow, Assembler::NotEqual);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow)
|
||||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_mul_branch_overflow(rd, rs, ScratchRegister, overflow);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_div_branch_overflow(Register rd, Register rs, Register rt, Label* overflow)
|
||||
{
|
||||
as_div(rs, rt);
|
||||
as_mflo(rd);
|
||||
as_mfhi(ScratchRegister);
|
||||
ma_b(ScratchRegister, ScratchRegister, overflow, Assembler::NonZero);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_div_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow)
|
||||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_div_branch_overflow(rd, rs, ScratchRegister, overflow);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_mod_mask(Register src, Register dest, Register hold, Register remain,
|
||||
int32_t shift, Label* negZero)
|
||||
{
|
||||
// MATH:
|
||||
// We wish to compute x % (1<<y) - 1 for a known constant, y.
|
||||
// First, let b = (1<<y) and C = (1<<y)-1, then think of the 32 bit
|
||||
// dividend as a number in base b, namely
|
||||
// c_0*1 + c_1*b + c_2*b^2 ... c_n*b^n
|
||||
// now, since both addition and multiplication commute with modulus,
|
||||
// x % C == (c_0 + c_1*b + ... + c_n*b^n) % C ==
|
||||
// (c_0 % C) + (c_1%C) * (b % C) + (c_2 % C) * (b^2 % C)...
|
||||
// now, since b == C + 1, b % C == 1, and b^n % C == 1
|
||||
// this means that the whole thing simplifies to:
|
||||
// c_0 + c_1 + c_2 ... c_n % C
|
||||
// each c_n can easily be computed by a shift/bitextract, and the modulus
|
||||
// can be maintained by simply subtracting by C whenever the number gets
|
||||
// over C.
|
||||
int32_t mask = (1 << shift) - 1;
|
||||
Label head, negative, sumSigned, done;
|
||||
|
||||
// hold holds -1 if the value was negative, 1 otherwise.
|
||||
// remain holds the remaining bits that have not been processed
|
||||
// SecondScratchReg serves as a temporary location to store extracted bits
|
||||
// into as well as holding the trial subtraction as a temp value dest is
|
||||
// the accumulator (and holds the final result)
|
||||
|
||||
// move the whole value into the remain.
|
||||
ma_move(remain, src);
|
||||
// Zero out the dest.
|
||||
ma_li(dest, Imm32(0));
|
||||
// Set the hold appropriately.
|
||||
ma_b(remain, remain, &negative, Signed, ShortJump);
|
||||
ma_li(hold, Imm32(1));
|
||||
ma_b(&head, ShortJump);
|
||||
|
||||
bind(&negative);
|
||||
ma_li(hold, Imm32(-1));
|
||||
ma_negu(remain, remain);
|
||||
|
||||
// Begin the main loop.
|
||||
bind(&head);
|
||||
|
||||
// Extract the bottom bits into SecondScratchReg.
|
||||
ma_and(SecondScratchReg, remain, Imm32(mask));
|
||||
// Add those bits to the accumulator.
|
||||
as_addu(dest, dest, SecondScratchReg);
|
||||
// Do a trial subtraction
|
||||
ma_subu(SecondScratchReg, dest, Imm32(mask));
|
||||
// If (sum - C) > 0, store sum - C back into sum, thus performing a
|
||||
// modulus.
|
||||
ma_b(SecondScratchReg, SecondScratchReg, &sumSigned, Signed, ShortJump);
|
||||
ma_move(dest, SecondScratchReg);
|
||||
bind(&sumSigned);
|
||||
// Get rid of the bits that we extracted before.
|
||||
as_srl(remain, remain, shift);
|
||||
// If the shift produced zero, finish, otherwise, continue in the loop.
|
||||
ma_b(remain, remain, &head, NonZero, ShortJump);
|
||||
// Check the hold to see if we need to negate the result.
|
||||
ma_b(hold, hold, &done, NotSigned, ShortJump);
|
||||
|
||||
// If the hold was non-zero, negate the result to be in line with
|
||||
// what JS wants
|
||||
if (negZero != nullptr) {
|
||||
// Jump out in case of negative zero.
|
||||
ma_b(hold, hold, negZero, Zero);
|
||||
ma_negu(dest, dest);
|
||||
} else {
|
||||
ma_negu(dest, dest);
|
||||
}
|
||||
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
// Memory.
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_load(Register dest, const BaseIndex& src,
|
||||
LoadStoreSize size, LoadStoreExtension extension)
|
||||
{
|
||||
asMasm().computeScaledAddress(src, SecondScratchReg);
|
||||
asMasm().ma_load(dest, Address(SecondScratchReg, src.offset), size, extension);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_store(Register data, const BaseIndex& dest,
|
||||
LoadStoreSize size, LoadStoreExtension extension)
|
||||
{
|
||||
asMasm().computeScaledAddress(dest, SecondScratchReg);
|
||||
asMasm().ma_store(data, Address(SecondScratchReg, dest.offset), size, extension);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_store(Imm32 imm, const BaseIndex& dest,
|
||||
LoadStoreSize size, LoadStoreExtension extension)
|
||||
{
|
||||
// Make sure that SecondScratchReg contains absolute address so that
|
||||
// offset is 0.
|
||||
asMasm().computeEffectiveAddress(dest, SecondScratchReg);
|
||||
|
||||
// Scrach register is free now, use it for loading imm value
|
||||
ma_li(ScratchRegister, imm);
|
||||
|
||||
// with offset=0 ScratchRegister will not be used in ma_store()
|
||||
// so we can use it as a parameter here
|
||||
asMasm().ma_store(ScratchRegister, Address(SecondScratchReg, 0), size, extension);
|
||||
}
|
||||
|
||||
// Branches when done from within mips-specific code.
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_b(Register lhs, Register rhs, Label* label, Condition c, JumpKind jumpKind)
|
||||
{
|
||||
switch (c) {
|
||||
case Equal :
|
||||
case NotEqual:
|
||||
asMasm().branchWithCode(getBranchCode(lhs, rhs, c), label, jumpKind);
|
||||
break;
|
||||
case Always:
|
||||
ma_b(label, jumpKind);
|
||||
break;
|
||||
case Zero:
|
||||
case NonZero:
|
||||
case Signed:
|
||||
case NotSigned:
|
||||
MOZ_ASSERT(lhs == rhs);
|
||||
asMasm().branchWithCode(getBranchCode(lhs, c), label, jumpKind);
|
||||
break;
|
||||
default:
|
||||
Condition cond = ma_cmp(ScratchRegister, lhs, rhs, c);
|
||||
asMasm().branchWithCode(getBranchCode(ScratchRegister, cond), label, jumpKind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_b(Register lhs, Imm32 imm, Label* label, Condition c, JumpKind jumpKind)
|
||||
{
|
||||
MOZ_ASSERT(c != Overflow);
|
||||
if (imm.value == 0) {
|
||||
if (c == Always || c == AboveOrEqual)
|
||||
ma_b(label, jumpKind);
|
||||
else if (c == Below)
|
||||
; // This condition is always false. No branch required.
|
||||
else
|
||||
asMasm().branchWithCode(getBranchCode(lhs, c), label, jumpKind);
|
||||
} else {
|
||||
MOZ_ASSERT(lhs != ScratchRegister);
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_b(lhs, ScratchRegister, label, c, jumpKind);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_b(Register lhs, ImmPtr imm, Label* l, Condition c, JumpKind jumpKind)
|
||||
{
|
||||
asMasm().ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_b(Label* label, JumpKind jumpKind)
|
||||
{
|
||||
asMasm().branchWithCode(getBranchCode(BranchIsJump), label, jumpKind);
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
MacroAssemblerMIPSShared::ma_cmp(Register scratch, Register lhs, Register rhs, Condition c)
|
||||
{
|
||||
switch (c) {
|
||||
case Above:
|
||||
// bgtu s,t,label =>
|
||||
// sltu at,t,s
|
||||
// bne at,$zero,offs
|
||||
as_sltu(scratch, rhs, lhs);
|
||||
return NotEqual;
|
||||
case AboveOrEqual:
|
||||
// bgeu s,t,label =>
|
||||
// sltu at,s,t
|
||||
// beq at,$zero,offs
|
||||
as_sltu(scratch, lhs, rhs);
|
||||
return Equal;
|
||||
case Below:
|
||||
// bltu s,t,label =>
|
||||
// sltu at,s,t
|
||||
// bne at,$zero,offs
|
||||
as_sltu(scratch, lhs, rhs);
|
||||
return NotEqual;
|
||||
case BelowOrEqual:
|
||||
// bleu s,t,label =>
|
||||
// sltu at,t,s
|
||||
// beq at,$zero,offs
|
||||
as_sltu(scratch, rhs, lhs);
|
||||
return Equal;
|
||||
case GreaterThan:
|
||||
// bgt s,t,label =>
|
||||
// slt at,t,s
|
||||
// bne at,$zero,offs
|
||||
as_slt(scratch, rhs, lhs);
|
||||
return NotEqual;
|
||||
case GreaterThanOrEqual:
|
||||
// bge s,t,label =>
|
||||
// slt at,s,t
|
||||
// beq at,$zero,offs
|
||||
as_slt(scratch, lhs, rhs);
|
||||
return Equal;
|
||||
case LessThan:
|
||||
// blt s,t,label =>
|
||||
// slt at,s,t
|
||||
// bne at,$zero,offs
|
||||
as_slt(scratch, lhs, rhs);
|
||||
return NotEqual;
|
||||
case LessThanOrEqual:
|
||||
// ble s,t,label =>
|
||||
// slt at,t,s
|
||||
// beq at,$zero,offs
|
||||
as_slt(scratch, rhs, lhs);
|
||||
return Equal;
|
||||
case Equal :
|
||||
case NotEqual:
|
||||
case Zero:
|
||||
case NonZero:
|
||||
case Always:
|
||||
case Signed:
|
||||
case NotSigned:
|
||||
MOZ_CRASH("There is a better way to compare for equality.");
|
||||
break;
|
||||
case Overflow:
|
||||
MOZ_CRASH("Overflow condition not supported for MIPS.");
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid condition for branch.");
|
||||
}
|
||||
return Always;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_cmp_set(Register rd, Register rs, Register rt, Condition c)
|
||||
{
|
||||
switch (c) {
|
||||
case Equal :
|
||||
// seq d,s,t =>
|
||||
// xor d,s,t
|
||||
// sltiu d,d,1
|
||||
as_xor(rd, rs, rt);
|
||||
as_sltiu(rd, rd, 1);
|
||||
break;
|
||||
case NotEqual:
|
||||
// sne d,s,t =>
|
||||
// xor d,s,t
|
||||
// sltu d,$zero,d
|
||||
as_xor(rd, rs, rt);
|
||||
as_sltu(rd, zero, rd);
|
||||
break;
|
||||
case Above:
|
||||
// sgtu d,s,t =>
|
||||
// sltu d,t,s
|
||||
as_sltu(rd, rt, rs);
|
||||
break;
|
||||
case AboveOrEqual:
|
||||
// sgeu d,s,t =>
|
||||
// sltu d,s,t
|
||||
// xori d,d,1
|
||||
as_sltu(rd, rs, rt);
|
||||
as_xori(rd, rd, 1);
|
||||
break;
|
||||
case Below:
|
||||
// sltu d,s,t
|
||||
as_sltu(rd, rs, rt);
|
||||
break;
|
||||
case BelowOrEqual:
|
||||
// sleu d,s,t =>
|
||||
// sltu d,t,s
|
||||
// xori d,d,1
|
||||
as_sltu(rd, rt, rs);
|
||||
as_xori(rd, rd, 1);
|
||||
break;
|
||||
case GreaterThan:
|
||||
// sgt d,s,t =>
|
||||
// slt d,t,s
|
||||
as_slt(rd, rt, rs);
|
||||
break;
|
||||
case GreaterThanOrEqual:
|
||||
// sge d,s,t =>
|
||||
// slt d,s,t
|
||||
// xori d,d,1
|
||||
as_slt(rd, rs, rt);
|
||||
as_xori(rd, rd, 1);
|
||||
break;
|
||||
case LessThan:
|
||||
// slt d,s,t
|
||||
as_slt(rd, rs, rt);
|
||||
break;
|
||||
case LessThanOrEqual:
|
||||
// sle d,s,t =>
|
||||
// slt d,t,s
|
||||
// xori d,d,1
|
||||
as_slt(rd, rt, rs);
|
||||
as_xori(rd, rd, 1);
|
||||
break;
|
||||
case Zero:
|
||||
MOZ_ASSERT(rs == rt);
|
||||
// seq d,s,$zero =>
|
||||
// xor d,s,$zero
|
||||
// sltiu d,d,1
|
||||
as_xor(rd, rs, zero);
|
||||
as_sltiu(rd, rd, 1);
|
||||
break;
|
||||
case NonZero:
|
||||
// sne d,s,$zero =>
|
||||
// xor d,s,$zero
|
||||
// sltu d,$zero,d
|
||||
as_xor(rd, rs, zero);
|
||||
as_sltu(rd, zero, rd);
|
||||
break;
|
||||
case Signed:
|
||||
as_slt(rd, rs, zero);
|
||||
break;
|
||||
case NotSigned:
|
||||
// sge d,s,$zero =>
|
||||
// slt d,s,$zero
|
||||
// xori d,d,1
|
||||
as_slt(rd, rs, zero);
|
||||
as_xori(rd, rd, 1);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid condition for ma_cmp_set.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::compareFloatingPoint(FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
||||
DoubleCondition c, FloatTestKind* testKind,
|
||||
FPConditionBit fcc)
|
||||
{
|
||||
switch (c) {
|
||||
case DoubleOrdered:
|
||||
as_cun(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForFalse;
|
||||
break;
|
||||
case DoubleEqual:
|
||||
as_ceq(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleNotEqual:
|
||||
as_cueq(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForFalse;
|
||||
break;
|
||||
case DoubleGreaterThan:
|
||||
as_colt(fmt, rhs, lhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleGreaterThanOrEqual:
|
||||
as_cole(fmt, rhs, lhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleLessThan:
|
||||
as_colt(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleLessThanOrEqual:
|
||||
as_cole(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleUnordered:
|
||||
as_cun(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleEqualOrUnordered:
|
||||
as_cueq(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleNotEqualOrUnordered:
|
||||
as_ceq(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForFalse;
|
||||
break;
|
||||
case DoubleGreaterThanOrUnordered:
|
||||
as_cult(fmt, rhs, lhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleGreaterThanOrEqualOrUnordered:
|
||||
as_cule(fmt, rhs, lhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleLessThanOrUnordered:
|
||||
as_cult(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
case DoubleLessThanOrEqualOrUnordered:
|
||||
as_cule(fmt, lhs, rhs, fcc);
|
||||
*testKind = TestForTrue;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Invalid DoubleCondition.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_cmp_set_double(Register dest, FloatRegister lhs, FloatRegister rhs,
|
||||
DoubleCondition c)
|
||||
{
|
||||
ma_li(dest, Imm32(0));
|
||||
ma_li(ScratchRegister, Imm32(1));
|
||||
|
||||
FloatTestKind moveCondition;
|
||||
compareFloatingPoint(DoubleFloat, lhs, rhs, c, &moveCondition);
|
||||
|
||||
if (moveCondition == TestForTrue)
|
||||
as_movt(dest, ScratchRegister);
|
||||
else
|
||||
as_movf(dest, ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_cmp_set_float32(Register dest, FloatRegister lhs, FloatRegister rhs,
|
||||
DoubleCondition c)
|
||||
{
|
||||
ma_li(dest, Imm32(0));
|
||||
ma_li(ScratchRegister, Imm32(1));
|
||||
|
||||
FloatTestKind moveCondition;
|
||||
compareFloatingPoint(SingleFloat, lhs, rhs, c, &moveCondition);
|
||||
|
||||
if (moveCondition == TestForTrue)
|
||||
as_movt(dest, ScratchRegister);
|
||||
else
|
||||
as_movf(dest, ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_cmp_set(Register rd, Register rs, Imm32 imm, Condition c)
|
||||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_cmp_set(rd, rs, ScratchRegister, c);
|
||||
}
|
||||
|
||||
// fp instructions
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_lis(FloatRegister dest, float value)
|
||||
{
|
||||
Imm32 imm(mozilla::BitwiseCast<uint32_t>(value));
|
||||
|
||||
ma_li(ScratchRegister, imm);
|
||||
moveToFloat32(ScratchRegister, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_liNegZero(FloatRegister dest)
|
||||
{
|
||||
moveToDoubleLo(zero, dest);
|
||||
ma_li(ScratchRegister, Imm32(INT_MIN));
|
||||
asMasm().moveToDoubleHi(ScratchRegister, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_sd(FloatRegister ft, BaseIndex address)
|
||||
{
|
||||
asMasm().computeScaledAddress(address, SecondScratchReg);
|
||||
asMasm().ma_sd(ft, Address(SecondScratchReg, address.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_ss(FloatRegister ft, BaseIndex address)
|
||||
{
|
||||
asMasm().computeScaledAddress(address, SecondScratchReg);
|
||||
asMasm().ma_ss(ft, Address(SecondScratchReg, address.offset));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label* label,
|
||||
DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc)
|
||||
{
|
||||
FloatTestKind testKind;
|
||||
compareFloatingPoint(SingleFloat, lhs, rhs, c, &testKind, fcc);
|
||||
asMasm().branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label* label,
|
||||
DoubleCondition c, JumpKind jumpKind, FPConditionBit fcc)
|
||||
{
|
||||
FloatTestKind testKind;
|
||||
compareFloatingPoint(DoubleFloat, lhs, rhs, c, &testKind, fcc);
|
||||
asMasm().branchWithCode(getBranchCode(testKind, fcc), label, jumpKind);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_call(ImmPtr dest)
|
||||
{
|
||||
asMasm().ma_liPatchable(CallReg, dest);
|
||||
as_jalr(CallReg);
|
||||
as_nop();
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSShared::ma_jump(ImmPtr dest)
|
||||
{
|
||||
asMasm().ma_liPatchable(ScratchRegister, dest);
|
||||
as_jr(ScratchRegister);
|
||||
as_nop();
|
||||
}
|
||||
|
||||
MacroAssembler&
|
||||
MacroAssemblerMIPSShared::asMasm()
|
||||
{
|
||||
return *static_cast<MacroAssembler*>(this);
|
||||
}
|
||||
|
||||
const MacroAssembler&
|
||||
MacroAssemblerMIPSShared::asMasm() const
|
||||
{
|
||||
return *static_cast<const MacroAssembler*>(this);
|
||||
}
|
||||
|
||||
//{{{ check_macroassembler_style
|
||||
// ===============================================================
|
||||
// Stack manipulation functions.
|
||||
|
||||
void
|
||||
MacroAssembler::Push(Register reg)
|
||||
{
|
||||
ma_push(reg);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Push(const Imm32 imm)
|
||||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_push(ScratchRegister);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Push(const ImmWord imm)
|
||||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_push(ScratchRegister);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Push(const ImmPtr imm)
|
||||
{
|
||||
Push(ImmWord(uintptr_t(imm.value)));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Push(const ImmGCPtr ptr)
|
||||
{
|
||||
ma_li(ScratchRegister, ptr);
|
||||
ma_push(ScratchRegister);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Push(FloatRegister f)
|
||||
{
|
||||
ma_push(f);
|
||||
adjustFrame(sizeof(double));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Pop(Register reg)
|
||||
{
|
||||
ma_pop(reg);
|
||||
adjustFrame(-sizeof(intptr_t));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Pop(const ValueOperand& val)
|
||||
{
|
||||
popValue(val);
|
||||
framePushed_ -= sizeof(Value);
|
||||
}
|
||||
|
||||
|
||||
// ===============================================================
|
||||
// Simple call functions.
|
||||
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::call(Register reg)
|
||||
{
|
||||
as_jalr(reg);
|
||||
as_nop();
|
||||
return CodeOffsetLabel(currentOffset());
|
||||
}
|
||||
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::call(Label* label)
|
||||
{
|
||||
ma_bal(label);
|
||||
return CodeOffsetLabel(currentOffset());
|
||||
}
|
||||
|
||||
CodeOffsetLabel
|
||||
MacroAssembler::callWithPatch()
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
void
|
||||
MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
|
||||
{
|
||||
MOZ_CRASH("NYI");
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::call(AsmJSImmPtr target)
|
||||
{
|
||||
movePtr(target, CallReg);
|
||||
call(CallReg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::call(ImmWord target)
|
||||
{
|
||||
call(ImmPtr((void*)target.value));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::call(ImmPtr target)
|
||||
{
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
addPendingJump(bo, target, Relocation::HARDCODED);
|
||||
ma_call(target);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::call(JitCode* c)
|
||||
{
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
|
||||
ma_liPatchable(ScratchRegister, ImmPtr(c->raw()));
|
||||
callJitNoProfiler(ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callAndPushReturnAddress(Register callee)
|
||||
{
|
||||
// Push return address during jalr delay slot.
|
||||
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
|
||||
as_jalr(callee);
|
||||
storePtr(ra, Address(StackPointer, 0));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callAndPushReturnAddress(Label* label)
|
||||
{
|
||||
// Push return address during bal delay slot.
|
||||
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
|
||||
ma_bal(label, DontFillDelaySlot);
|
||||
storePtr(ra, Address(StackPointer, 0));
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Jit Frames.
|
||||
|
||||
uint32_t
|
||||
MacroAssembler::pushFakeReturnAddress(Register scratch)
|
||||
{
|
||||
CodeLabel cl;
|
||||
|
||||
ma_li(scratch, cl.dest());
|
||||
Push(scratch);
|
||||
bind(cl.src());
|
||||
uint32_t retAddr = currentOffset();
|
||||
|
||||
addCodeLabel(cl);
|
||||
return retAddr;
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
@@ -0,0 +1,185 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips_shared_MacroAssembler_mips_shared_h
|
||||
#define jit_mips_shared_MacroAssembler_mips_shared_h
|
||||
|
||||
#if defined(JS_CODEGEN_MIPS32)
|
||||
# include "jit/mips32/Assembler-mips32.h"
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
enum LoadStoreSize
|
||||
{
|
||||
SizeByte = 8,
|
||||
SizeHalfWord = 16,
|
||||
SizeWord = 32,
|
||||
SizeDouble = 64
|
||||
};
|
||||
|
||||
enum LoadStoreExtension
|
||||
{
|
||||
ZeroExtend = 0,
|
||||
SignExtend = 1
|
||||
};
|
||||
|
||||
enum JumpKind
|
||||
{
|
||||
LongJump = 0,
|
||||
ShortJump = 1
|
||||
};
|
||||
|
||||
enum DelaySlotFill
|
||||
{
|
||||
DontFillDelaySlot = 0,
|
||||
FillDelaySlot = 1
|
||||
};
|
||||
|
||||
static Register CallReg = t9;
|
||||
|
||||
class MacroAssemblerMIPSShared : public Assembler
|
||||
{
|
||||
protected:
|
||||
// Perform a downcast. Should be removed by Bug 996602.
|
||||
MacroAssembler& asMasm();
|
||||
const MacroAssembler& asMasm() const;
|
||||
|
||||
Condition ma_cmp(Register rd, Register lhs, Register rhs, Condition c);
|
||||
|
||||
void compareFloatingPoint(FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
||||
DoubleCondition c, FloatTestKind* testKind,
|
||||
FPConditionBit fcc = FCC0);
|
||||
|
||||
public:
|
||||
void ma_move(Register rd, Register rs);
|
||||
|
||||
void ma_li(Register dest, ImmGCPtr ptr);
|
||||
|
||||
void ma_li(Register dest, Imm32 imm);
|
||||
|
||||
// Shift operations
|
||||
void ma_sll(Register rd, Register rt, Imm32 shift);
|
||||
void ma_srl(Register rd, Register rt, Imm32 shift);
|
||||
void ma_sra(Register rd, Register rt, Imm32 shift);
|
||||
void ma_ror(Register rd, Register rt, Imm32 shift);
|
||||
void ma_rol(Register rd, Register rt, Imm32 shift);
|
||||
|
||||
void ma_sll(Register rd, Register rt, Register shift);
|
||||
void ma_srl(Register rd, Register rt, Register shift);
|
||||
void ma_sra(Register rd, Register rt, Register shift);
|
||||
void ma_ror(Register rd, Register rt, Register shift);
|
||||
void ma_rol(Register rd, Register rt, Register shift);
|
||||
|
||||
// Negate
|
||||
void ma_negu(Register rd, Register rs);
|
||||
|
||||
void ma_not(Register rd, Register rs);
|
||||
|
||||
// and
|
||||
void ma_and(Register rd, Register rs);
|
||||
void ma_and(Register rd, Imm32 imm);
|
||||
void ma_and(Register rd, Register rs, Imm32 imm);
|
||||
|
||||
// or
|
||||
void ma_or(Register rd, Register rs);
|
||||
void ma_or(Register rd, Imm32 imm);
|
||||
void ma_or(Register rd, Register rs, Imm32 imm);
|
||||
|
||||
// xor
|
||||
void ma_xor(Register rd, Register rs);
|
||||
void ma_xor(Register rd, Imm32 imm);
|
||||
void ma_xor(Register rd, Register rs, Imm32 imm);
|
||||
|
||||
// load
|
||||
void ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
|
||||
// store
|
||||
void ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
void ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
|
||||
// arithmetic based ops
|
||||
// add
|
||||
void ma_addu(Register rd, Register rs, Imm32 imm);
|
||||
void ma_addu(Register rd, Register rs);
|
||||
void ma_addu(Register rd, Imm32 imm);
|
||||
|
||||
// subtract
|
||||
void ma_subu(Register rd, Register rs, Imm32 imm);
|
||||
void ma_subu(Register rd, Imm32 imm);
|
||||
void ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
||||
|
||||
// multiplies. For now, there are only few that we care about.
|
||||
void ma_mult(Register rs, Imm32 imm);
|
||||
void ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label* overflow);
|
||||
void ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
||||
|
||||
// divisions
|
||||
void ma_div_branch_overflow(Register rd, Register rs, Register rt, Label* overflow);
|
||||
void ma_div_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
||||
|
||||
// fast mod, uses scratch registers, and thus needs to be in the assembler
|
||||
// implicitly assumes that we can overwrite dest at the beginning of the sequence
|
||||
void ma_mod_mask(Register src, Register dest, Register hold, Register remain,
|
||||
int32_t shift, Label* negZero = nullptr);
|
||||
|
||||
// branches when done from within mips-specific code
|
||||
void ma_b(Register lhs, Register rhs, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
||||
void ma_b(Register lhs, Imm32 imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
||||
void ma_b(Register lhs, ImmPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
||||
void ma_b(Register lhs, ImmGCPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump) {
|
||||
MOZ_ASSERT(lhs != ScratchRegister);
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_b(lhs, ScratchRegister, l, c, jumpKind);
|
||||
}
|
||||
|
||||
void ma_b(Label* l, JumpKind jumpKind = LongJump);
|
||||
|
||||
// fp instructions
|
||||
void ma_lis(FloatRegister dest, float value);
|
||||
void ma_liNegZero(FloatRegister dest);
|
||||
|
||||
void ma_sd(FloatRegister fd, BaseIndex address);
|
||||
void ma_ss(FloatRegister fd, BaseIndex address);
|
||||
|
||||
//FP branches
|
||||
void ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label* label, DoubleCondition c,
|
||||
JumpKind jumpKind = LongJump, FPConditionBit fcc = FCC0);
|
||||
void ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label* label, DoubleCondition c,
|
||||
JumpKind jumpKind = LongJump, FPConditionBit fcc = FCC0);
|
||||
|
||||
void ma_call(ImmPtr dest);
|
||||
|
||||
void ma_jump(ImmPtr dest);
|
||||
|
||||
void ma_cmp_set(Register dst, Register lhs, Register rhs, Condition c);
|
||||
void ma_cmp_set(Register dst, Register lhs, Imm32 imm, Condition c);
|
||||
void ma_cmp_set_double(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
|
||||
void ma_cmp_set_float32(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
|
||||
|
||||
void moveToDoubleLo(Register src, FloatRegister dest) {
|
||||
as_mtc1(src, dest);
|
||||
}
|
||||
void moveFromDoubleLo(FloatRegister src, Register dest) {
|
||||
as_mfc1(dest, src);
|
||||
}
|
||||
|
||||
void moveToFloat32(Register src, FloatRegister dest) {
|
||||
as_mtc1(src, dest);
|
||||
}
|
||||
void moveFromFloat32(FloatRegister src, Register dest) {
|
||||
as_mfc1(dest, src);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips_shared_MacroAssembler_mips_shared_h */
|
||||
@@ -37,7 +37,7 @@ class MoveEmitterMIPSShared
|
||||
void assertDone();
|
||||
Register tempReg();
|
||||
FloatRegister tempFloatReg();
|
||||
Address cycleSlot(uint32_t slot, uint32_t subslot) const;
|
||||
Address cycleSlot(uint32_t slot, uint32_t subslot = 0) const;
|
||||
int32_t getAdjustedOffset(const MoveOperand& operand);
|
||||
Address getAdjustedAddress(const MoveOperand& operand);
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "jit/mips32/MacroAssembler-mips32.h"
|
||||
|
||||
#include "jit/mips-shared/MacroAssembler-mips-shared-inl.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
@@ -16,39 +18,6 @@ namespace jit {
|
||||
// ===============================================================
|
||||
// Logical instructions
|
||||
|
||||
void
|
||||
MacroAssembler::not32(Register reg)
|
||||
{
|
||||
ma_not(reg, reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(Register src, Register dest)
|
||||
{
|
||||
as_and(dest, dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_and(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(Imm32 imm, const Address& dest)
|
||||
{
|
||||
load32(dest, SecondScratchReg);
|
||||
ma_and(SecondScratchReg, imm);
|
||||
store32(SecondScratchReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and32(const Address& src, Register dest)
|
||||
{
|
||||
load32(src, SecondScratchReg);
|
||||
ma_and(dest, SecondScratchReg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::andPtr(Register src, Register dest)
|
||||
{
|
||||
@@ -68,26 +37,6 @@ MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Register src, Register dest)
|
||||
{
|
||||
ma_or(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_or(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Imm32 imm, const Address& dest)
|
||||
{
|
||||
load32(dest, SecondScratchReg);
|
||||
ma_or(SecondScratchReg, imm);
|
||||
store32(SecondScratchReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::orPtr(Register src, Register dest)
|
||||
{
|
||||
@@ -107,12 +56,6 @@ MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
or32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_xor(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xorPtr(Register src, Register dest)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,38 +12,12 @@
|
||||
#include "jit/AtomicOp.h"
|
||||
#include "jit/IonCaches.h"
|
||||
#include "jit/JitFrames.h"
|
||||
#include "jit/mips32/Assembler-mips32.h"
|
||||
#include "jit/mips-shared/MacroAssembler-mips-shared.h"
|
||||
#include "jit/MoveResolver.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
enum LoadStoreSize
|
||||
{
|
||||
SizeByte = 8,
|
||||
SizeHalfWord = 16,
|
||||
SizeWord = 32,
|
||||
SizeDouble = 64
|
||||
};
|
||||
|
||||
enum LoadStoreExtension
|
||||
{
|
||||
ZeroExtend = 0,
|
||||
SignExtend = 1
|
||||
};
|
||||
|
||||
enum JumpKind
|
||||
{
|
||||
LongJump = 0,
|
||||
ShortJump = 1
|
||||
};
|
||||
|
||||
enum DelaySlotFill
|
||||
{
|
||||
DontFillDelaySlot = 0,
|
||||
FillDelaySlot = 1
|
||||
};
|
||||
|
||||
struct ImmTag : public Imm32
|
||||
{
|
||||
ImmTag(JSValueTag mask)
|
||||
@@ -61,7 +35,6 @@ struct ImmType : public ImmTag
|
||||
static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
|
||||
static const ValueOperand softfpReturnOperand = ValueOperand(v1, v0);
|
||||
|
||||
static Register CallReg = t9;
|
||||
static const int defaultShift = 3;
|
||||
static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
|
||||
|
||||
@@ -69,107 +42,40 @@ static const uint32_t LOW_32_MASK = (1LL << 32) - 1;
|
||||
static const int32_t LOW_32_OFFSET = 0;
|
||||
static const int32_t HIGH_32_OFFSET = 4;
|
||||
|
||||
class MacroAssemblerMIPS : public Assembler
|
||||
class MacroAssemblerMIPS : public MacroAssemblerMIPSShared
|
||||
{
|
||||
protected:
|
||||
// Perform a downcast. Should be removed by Bug 996602.
|
||||
MacroAssembler& asMasm();
|
||||
const MacroAssembler& asMasm() const;
|
||||
|
||||
void branchWithCode(InstImm code, Label* label, JumpKind jumpKind);
|
||||
Condition ma_cmp(Register rd, Register lhs, Register rhs, Condition c);
|
||||
|
||||
void compareFloatingPoint(FloatFormat fmt, FloatRegister lhs, FloatRegister rhs,
|
||||
DoubleCondition c, FloatTestKind* testKind,
|
||||
FPConditionBit fcc = FCC0);
|
||||
|
||||
public:
|
||||
void ma_move(Register rd, Register rs);
|
||||
|
||||
void ma_li(Register dest, ImmGCPtr ptr);
|
||||
using MacroAssemblerMIPSShared::ma_b;
|
||||
using MacroAssemblerMIPSShared::ma_li;
|
||||
using MacroAssemblerMIPSShared::ma_ss;
|
||||
using MacroAssemblerMIPSShared::ma_sd;
|
||||
using MacroAssemblerMIPSShared::ma_load;
|
||||
using MacroAssemblerMIPSShared::ma_store;
|
||||
using MacroAssemblerMIPSShared::ma_cmp_set;
|
||||
using MacroAssemblerMIPSShared::ma_subTestOverflow;
|
||||
|
||||
void ma_li(Register dest, AbsoluteLabel* label);
|
||||
|
||||
void ma_li(Register dest, Imm32 imm);
|
||||
void ma_liPatchable(Register dest, Imm32 imm);
|
||||
void ma_li(Register dest, ImmWord imm);
|
||||
void ma_liPatchable(Register dest, ImmPtr imm);
|
||||
void ma_liPatchable(Register dest, ImmWord imm);
|
||||
|
||||
// Shift operations
|
||||
void ma_sll(Register rd, Register rt, Imm32 shift);
|
||||
void ma_srl(Register rd, Register rt, Imm32 shift);
|
||||
void ma_sra(Register rd, Register rt, Imm32 shift);
|
||||
void ma_ror(Register rd, Register rt, Imm32 shift);
|
||||
void ma_rol(Register rd, Register rt, Imm32 shift);
|
||||
|
||||
void ma_sll(Register rd, Register rt, Register shift);
|
||||
void ma_srl(Register rd, Register rt, Register shift);
|
||||
void ma_sra(Register rd, Register rt, Register shift);
|
||||
void ma_ror(Register rd, Register rt, Register shift);
|
||||
void ma_rol(Register rd, Register rt, Register shift);
|
||||
|
||||
// Negate
|
||||
void ma_negu(Register rd, Register rs);
|
||||
|
||||
void ma_not(Register rd, Register rs);
|
||||
|
||||
// and
|
||||
void ma_and(Register rd, Register rs);
|
||||
void ma_and(Register rd, Imm32 imm);
|
||||
void ma_and(Register rd, Register rs, Imm32 imm);
|
||||
|
||||
// or
|
||||
void ma_or(Register rd, Register rs);
|
||||
void ma_or(Register rd, Imm32 imm);
|
||||
void ma_or(Register rd, Register rs, Imm32 imm);
|
||||
|
||||
// xor
|
||||
void ma_xor(Register rd, Register rs);
|
||||
void ma_xor(Register rd, Imm32 imm);
|
||||
void ma_xor(Register rd, Register rs, Imm32 imm);
|
||||
|
||||
// load
|
||||
void ma_load(Register dest, Address address, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
void ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
|
||||
// store
|
||||
void ma_store(Register data, Address address, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
void ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
void ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord,
|
||||
LoadStoreExtension extension = SignExtend);
|
||||
|
||||
// arithmetic based ops
|
||||
// add
|
||||
void ma_addu(Register rd, Register rs, Imm32 imm);
|
||||
void ma_addu(Register rd, Register rs);
|
||||
void ma_addu(Register rd, Imm32 imm);
|
||||
void ma_addTestOverflow(Register rd, Register rs, Register rt, Label* overflow);
|
||||
void ma_addTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
||||
|
||||
// subtract
|
||||
void ma_subu(Register rd, Register rs, Imm32 imm);
|
||||
void ma_subu(Register rd, Imm32 imm);
|
||||
void ma_subTestOverflow(Register rd, Register rs, Register rt, Label* overflow);
|
||||
void ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
||||
|
||||
// multiplies. For now, there are only few that we care about.
|
||||
void ma_mult(Register rs, Imm32 imm);
|
||||
void ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label* overflow);
|
||||
void ma_mul_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
||||
|
||||
// divisions
|
||||
void ma_div_branch_overflow(Register rd, Register rs, Register rt, Label* overflow);
|
||||
void ma_div_branch_overflow(Register rd, Register rs, Imm32 imm, Label* overflow);
|
||||
|
||||
// fast mod, uses scratch registers, and thus needs to be in the assembler
|
||||
// implicitly assumes that we can overwrite dest at the beginning of the sequence
|
||||
void ma_mod_mask(Register src, Register dest, Register hold, Register remain,
|
||||
int32_t shift, Label* negZero = nullptr);
|
||||
|
||||
// memory
|
||||
// shortcut for when we know we're transferring 32 bits of data
|
||||
@@ -182,17 +88,8 @@ class MacroAssemblerMIPS : public Assembler
|
||||
void ma_pop(Register r);
|
||||
void ma_push(Register r);
|
||||
|
||||
void branchWithCode(InstImm code, Label* label, JumpKind jumpKind);
|
||||
// branches when done from within mips-specific code
|
||||
void ma_b(Register lhs, Register rhs, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
||||
void ma_b(Register lhs, Imm32 imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
||||
void ma_b(Register lhs, ImmPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump) {
|
||||
ma_b(lhs, ImmWord(uintptr_t(imm.value)), l, c, jumpKind);
|
||||
}
|
||||
void ma_b(Register lhs, ImmGCPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump) {
|
||||
MOZ_ASSERT(lhs != ScratchRegister);
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_b(lhs, ScratchRegister, l, c, jumpKind);
|
||||
}
|
||||
void ma_b(Register lhs, ImmWord imm, Label* l, Condition c, JumpKind jumpKind = LongJump)
|
||||
{
|
||||
ma_b(lhs, Imm32(uint32_t(imm.value)), l, c, jumpKind);
|
||||
@@ -211,13 +108,10 @@ class MacroAssemblerMIPS : public Assembler
|
||||
ma_b(ScratchRegister, rhs, l, c, jumpKind);
|
||||
}
|
||||
|
||||
void ma_b(Label* l, JumpKind jumpKind = LongJump);
|
||||
void ma_bal(Label* l, DelaySlotFill delaySlotFill = FillDelaySlot);
|
||||
|
||||
// fp instructions
|
||||
void ma_lis(FloatRegister dest, float value);
|
||||
void ma_lid(FloatRegister dest, double value);
|
||||
void ma_liNegZero(FloatRegister dest);
|
||||
|
||||
void ma_mv(FloatRegister src, ValueOperand dest);
|
||||
void ma_mv(ValueOperand src, FloatRegister dest);
|
||||
@@ -225,25 +119,11 @@ class MacroAssemblerMIPS : public Assembler
|
||||
void ma_ls(FloatRegister fd, Address address);
|
||||
void ma_ld(FloatRegister fd, Address address);
|
||||
void ma_sd(FloatRegister fd, Address address);
|
||||
void ma_sd(FloatRegister fd, BaseIndex address);
|
||||
void ma_ss(FloatRegister fd, Address address);
|
||||
void ma_ss(FloatRegister fd, BaseIndex address);
|
||||
|
||||
void ma_pop(FloatRegister fs);
|
||||
void ma_push(FloatRegister fs);
|
||||
|
||||
//FP branches
|
||||
void ma_bc1s(FloatRegister lhs, FloatRegister rhs, Label* label, DoubleCondition c,
|
||||
JumpKind jumpKind = LongJump, FPConditionBit fcc = FCC0);
|
||||
void ma_bc1d(FloatRegister lhs, FloatRegister rhs, Label* label, DoubleCondition c,
|
||||
JumpKind jumpKind = LongJump, FPConditionBit fcc = FCC0);
|
||||
|
||||
void ma_call(ImmPtr dest);
|
||||
|
||||
void ma_jump(ImmPtr dest);
|
||||
|
||||
void ma_cmp_set(Register dst, Register lhs, Register rhs, Condition c);
|
||||
void ma_cmp_set(Register dst, Register lhs, Imm32 imm, Condition c);
|
||||
void ma_cmp_set(Register dst, Register lhs, ImmPtr imm, Condition c) {
|
||||
ma_cmp_set(dst, lhs, Imm32(uint32_t(imm.value)), c);
|
||||
}
|
||||
@@ -254,8 +134,6 @@ class MacroAssemblerMIPS : public Assembler
|
||||
ma_li(SecondScratchReg, Imm32(uint32_t(imm.value)));
|
||||
ma_cmp_set(dst, ScratchRegister, SecondScratchReg, c);
|
||||
}
|
||||
void ma_cmp_set_double(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
|
||||
void ma_cmp_set_float32(Register dst, FloatRegister lhs, FloatRegister rhs, DoubleCondition c);
|
||||
|
||||
// These fuctions abstract the access to high part of the double precision
|
||||
// float register. It is intended to work on both 32 bit and 64 bit
|
||||
@@ -267,20 +145,6 @@ class MacroAssemblerMIPS : public Assembler
|
||||
void moveFromDoubleHi(FloatRegister src, Register dest) {
|
||||
as_mfc1(dest, getOddPair(src));
|
||||
}
|
||||
|
||||
void moveToDoubleLo(Register src, FloatRegister dest) {
|
||||
as_mtc1(src, dest);
|
||||
}
|
||||
void moveFromDoubleLo(FloatRegister src, Register dest) {
|
||||
as_mfc1(dest, src);
|
||||
}
|
||||
|
||||
void moveToFloat32(Register src, FloatRegister dest) {
|
||||
as_mtc1(src, dest);
|
||||
}
|
||||
void moveFromFloat32(FloatRegister src, Register dest) {
|
||||
as_mfc1(dest, src);
|
||||
}
|
||||
};
|
||||
|
||||
class MacroAssembler;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
|
||||
#include "jit/mips-shared/Bailouts-mips-shared.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator& activations,
|
||||
BailoutStack* bailout)
|
||||
: machine_(bailout->machine())
|
||||
{
|
||||
uint8_t* sp = bailout->parentStackPointer();
|
||||
framePointer_ = sp + bailout->frameSize();
|
||||
topFrameSize_ = framePointer_ - sp;
|
||||
|
||||
JSScript* script = ScriptFromCalleeToken(((JitFrameLayout*) framePointer_)->calleeToken());
|
||||
topIonScript_ = script->ionScript();
|
||||
|
||||
attachOnJitActivation(activations);
|
||||
snapshotOffset_ = bailout->snapshotOffset();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/mips64/BaselineCompiler-mips64.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
BaselineCompilerMIPS64::BaselineCompilerMIPS64(JSContext* cx, TempAllocator& alloc,
|
||||
JSScript* script)
|
||||
: BaselineCompilerMIPSShared(cx, alloc, script)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips64_BaselineCompiler_mips64_h
|
||||
#define jit_mips64_BaselineCompiler_mips64_h
|
||||
|
||||
#include "jit/mips-shared/BaselineCompiler-mips-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class BaselineCompilerMIPS64 : public BaselineCompilerMIPSShared
|
||||
{
|
||||
protected:
|
||||
BaselineCompilerMIPS64(JSContext* cx, TempAllocator& alloc, JSScript* script);
|
||||
};
|
||||
|
||||
typedef BaselineCompilerMIPS64 BaselineCompilerSpecific;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips64_BaselineCompiler_mips64_h */
|
||||
@@ -0,0 +1,47 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/BaselineCompiler.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/Linker.h"
|
||||
#include "jit/SharedICHelpers.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
// ICCompare_Int32
|
||||
|
||||
bool
|
||||
ICCompare_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
// Guard that R0 is an integer and R1 is an integer.
|
||||
Label failure;
|
||||
Label conditionTrue;
|
||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
||||
|
||||
// Compare payload regs of R0 and R1.
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
Assembler::Condition cond = JSOpToCondition(op, /* signed = */true);
|
||||
masm.ma_cmp_set(R0.valueReg(), ExtractTemp0, ExtractTemp1, cond);
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, R0.valueReg(), R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
// Failure case - jump to next stub
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
@@ -0,0 +1,282 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/mips64/CodeGenerator-mips64.h"
|
||||
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
|
||||
#include "jit/CodeGenerator.h"
|
||||
#include "jit/JitCompartment.h"
|
||||
#include "jit/JitFrames.h"
|
||||
#include "jit/MIR.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "vm/Shape.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
#include "jit/shared/CodeGenerator-shared-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase<CodeGeneratorMIPS64>
|
||||
{
|
||||
MTableSwitch* mir_;
|
||||
CodeLabel jumpLabel_;
|
||||
|
||||
void accept(CodeGeneratorMIPS64* codegen) {
|
||||
codegen->visitOutOfLineTableSwitch(this);
|
||||
}
|
||||
|
||||
public:
|
||||
OutOfLineTableSwitch(MTableSwitch* mir)
|
||||
: mir_(mir)
|
||||
{}
|
||||
|
||||
MTableSwitch* mir() const {
|
||||
return mir_;
|
||||
}
|
||||
|
||||
CodeLabel* jumpLabel() {
|
||||
return &jumpLabel_;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
|
||||
{
|
||||
MTableSwitch* mir = ool->mir();
|
||||
|
||||
masm.haltingAlign(sizeof(void*));
|
||||
masm.bind(ool->jumpLabel()->src());
|
||||
masm.addCodeLabel(*ool->jumpLabel());
|
||||
|
||||
for (size_t i = 0; i < mir->numCases(); i++) {
|
||||
LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir();
|
||||
Label* caseheader = caseblock->label();
|
||||
uint32_t caseoffset = caseheader->offset();
|
||||
|
||||
// The entries of the jump table need to be absolute addresses and thus
|
||||
// must be patched after codegen is finished. Each table entry uses 8
|
||||
// instructions (4 for load address, 2 for branch, and 2 padding).
|
||||
CodeLabel cl;
|
||||
masm.ma_li(ScratchRegister, cl.dest());
|
||||
masm.branch(ScratchRegister);
|
||||
masm.as_nop();
|
||||
masm.as_nop();
|
||||
cl.src()->bind(caseoffset);
|
||||
masm.addCodeLabel(cl);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::emitTableSwitchDispatch(MTableSwitch* mir, Register index,
|
||||
Register address)
|
||||
{
|
||||
Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
|
||||
|
||||
// Lower value with low value
|
||||
if (mir->low() != 0)
|
||||
masm.subPtr(Imm32(mir->low()), index);
|
||||
|
||||
// Jump to default case if input is out of range
|
||||
int32_t cases = mir->numCases();
|
||||
masm.branch32(Assembler::AboveOrEqual, index, Imm32(cases), defaultcase);
|
||||
|
||||
// To fill in the CodeLabels for the case entries, we need to first
|
||||
// generate the case entries (we don't yet know their offsets in the
|
||||
// instruction stream).
|
||||
OutOfLineTableSwitch* ool = new(alloc()) OutOfLineTableSwitch(mir);
|
||||
addOutOfLineCode(ool, mir);
|
||||
|
||||
// Compute the position where a pointer to the right case stands.
|
||||
masm.ma_li(address, ool->jumpLabel()->dest());
|
||||
// index = size of table entry * index.
|
||||
// See CodeGeneratorMIPS64::visitOutOfLineTableSwitch
|
||||
masm.lshiftPtr(Imm32(5), index);
|
||||
masm.addPtr(index, address);
|
||||
|
||||
masm.branch(address);
|
||||
}
|
||||
|
||||
FrameSizeClass
|
||||
FrameSizeClass::FromDepth(uint32_t frameDepth)
|
||||
{
|
||||
return FrameSizeClass::None();
|
||||
}
|
||||
|
||||
FrameSizeClass
|
||||
FrameSizeClass::ClassLimit()
|
||||
{
|
||||
return FrameSizeClass(0);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
FrameSizeClass::frameSize() const
|
||||
{
|
||||
MOZ_CRASH("MIPS64 does not use frame size classes");
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorMIPS64::ToValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getOperand(pos)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorMIPS64::ToOutValue(LInstruction* ins)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getDef(0)));
|
||||
}
|
||||
|
||||
ValueOperand
|
||||
CodeGeneratorMIPS64::ToTempValue(LInstruction* ins, size_t pos)
|
||||
{
|
||||
return ValueOperand(ToRegister(ins->getTemp(pos)));
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::visitBox(LBox* box)
|
||||
{
|
||||
const LAllocation* in = box->getOperand(0);
|
||||
const LDefinition* result = box->getDef(0);
|
||||
|
||||
if (IsFloatingPointType(box->type())) {
|
||||
FloatRegister reg = ToFloatRegister(in);
|
||||
if (box->type() == MIRType_Float32) {
|
||||
masm.convertFloat32ToDouble(reg, ScratchDoubleReg);
|
||||
reg = ScratchDoubleReg;
|
||||
}
|
||||
masm.moveFromDouble(reg, ToRegister(result));
|
||||
} else {
|
||||
masm.boxValue(ValueTypeFromMIRType(box->type()), ToRegister(in), ToRegister(result));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::visitUnbox(LUnbox* unbox)
|
||||
{
|
||||
MUnbox* mir = unbox->mir();
|
||||
|
||||
if (mir->fallible()) {
|
||||
const ValueOperand value = ToValue(unbox, LUnbox::Input);
|
||||
masm.splitTag(value, SecondScratchReg);
|
||||
bailoutCmp32(Assembler::NotEqual, SecondScratchReg, Imm32(MIRTypeToTag(mir->type())),
|
||||
unbox->snapshot());
|
||||
}
|
||||
|
||||
Operand input = ToOperand(unbox->getOperand(LUnbox::Input));
|
||||
Register result = ToRegister(unbox->output());
|
||||
switch (mir->type()) {
|
||||
case MIRType_Int32:
|
||||
masm.unboxInt32(input, result);
|
||||
break;
|
||||
case MIRType_Boolean:
|
||||
masm.unboxBoolean(input, result);
|
||||
break;
|
||||
case MIRType_Object:
|
||||
masm.unboxObject(input, result);
|
||||
break;
|
||||
case MIRType_String:
|
||||
masm.unboxString(input, result);
|
||||
break;
|
||||
case MIRType_Symbol:
|
||||
masm.unboxSymbol(input, result);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Given MIRType cannot be unboxed.");
|
||||
}
|
||||
}
|
||||
|
||||
Register
|
||||
CodeGeneratorMIPS64::splitTagForTest(const ValueOperand& value)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
return SecondScratchReg;
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::visitCompareB(LCompareB* lir)
|
||||
{
|
||||
MCompare* mir = lir->mir();
|
||||
|
||||
const ValueOperand lhs = ToValue(lir, LCompareB::Lhs);
|
||||
const LAllocation* rhs = lir->rhs();
|
||||
const Register output = ToRegister(lir->output());
|
||||
|
||||
MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
|
||||
// Load boxed boolean in ScratchRegister.
|
||||
if (rhs->isConstant())
|
||||
masm.moveValue(*rhs->toConstant(), ScratchRegister);
|
||||
else
|
||||
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
|
||||
|
||||
// Perform the comparison.
|
||||
masm.cmpPtrSet(cond, lhs.valueReg(), ScratchRegister, output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::visitCompareBAndBranch(LCompareBAndBranch* lir)
|
||||
{
|
||||
MCompare* mir = lir->cmpMir();
|
||||
const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs);
|
||||
const LAllocation* rhs = lir->rhs();
|
||||
|
||||
MOZ_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE);
|
||||
|
||||
// Load boxed boolean in ScratchRegister.
|
||||
if (rhs->isConstant())
|
||||
masm.moveValue(*rhs->toConstant(), ScratchRegister);
|
||||
else
|
||||
masm.boxValue(JSVAL_TYPE_BOOLEAN, ToRegister(rhs), ScratchRegister);
|
||||
|
||||
// Perform the comparison.
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
emitBranch(lhs.valueReg(), ScratchRegister, cond, lir->ifTrue(), lir->ifFalse());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::visitCompareBitwise(LCompareBitwise* lir)
|
||||
{
|
||||
MCompare* mir = lir->mir();
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
const ValueOperand lhs = ToValue(lir, LCompareBitwise::LhsInput);
|
||||
const ValueOperand rhs = ToValue(lir, LCompareBitwise::RhsInput);
|
||||
const Register output = ToRegister(lir->output());
|
||||
|
||||
MOZ_ASSERT(IsEqualityOp(mir->jsop()));
|
||||
|
||||
masm.cmpPtrSet(cond, lhs.valueReg(), rhs.valueReg(), output);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir)
|
||||
{
|
||||
MCompare* mir = lir->cmpMir();
|
||||
Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop());
|
||||
const ValueOperand lhs = ToValue(lir, LCompareBitwiseAndBranch::LhsInput);
|
||||
const ValueOperand rhs = ToValue(lir, LCompareBitwiseAndBranch::RhsInput);
|
||||
|
||||
MOZ_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ ||
|
||||
mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE);
|
||||
|
||||
emitBranch(lhs.valueReg(), rhs.valueReg(), cond, lir->ifTrue(), lir->ifFalse());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
MOZ_ASSERT(ReturnFloat32Reg.reg_ == FloatRegisters::f0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.reg_ == FloatRegisters::f0);
|
||||
FloatRegister f1 = { FloatRegisters::f1, FloatRegisters::Single };
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(f1);
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips64_CodeGenerator_mips64_h
|
||||
#define jit_mips64_CodeGenerator_mips64_h
|
||||
|
||||
#include "jit/mips-shared/CodeGenerator-mips-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class CodeGeneratorMIPS64 : public CodeGeneratorMIPSShared
|
||||
{
|
||||
protected:
|
||||
void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
||||
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_NULL), cond, ifTrue, ifFalse);
|
||||
}
|
||||
void testUndefinedEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
||||
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_UNDEFINED), cond, ifTrue, ifFalse);
|
||||
}
|
||||
void testObjectEmitBranch(Assembler::Condition cond, const ValueOperand& value,
|
||||
MBasicBlock* ifTrue, MBasicBlock* ifFalse)
|
||||
{
|
||||
MOZ_ASSERT(value.valueReg() != SecondScratchReg);
|
||||
masm.splitTag(value.valueReg(), SecondScratchReg);
|
||||
emitBranch(SecondScratchReg, ImmTag(JSVAL_TAG_OBJECT), cond, ifTrue, ifFalse);
|
||||
}
|
||||
|
||||
void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base);
|
||||
|
||||
public:
|
||||
virtual void visitCompareB(LCompareB* lir);
|
||||
virtual void visitCompareBAndBranch(LCompareBAndBranch* lir);
|
||||
virtual void visitCompareBitwise(LCompareBitwise* lir);
|
||||
virtual void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
|
||||
|
||||
// Out of line visitors.
|
||||
void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool);
|
||||
protected:
|
||||
ValueOperand ToValue(LInstruction* ins, size_t pos);
|
||||
ValueOperand ToOutValue(LInstruction* ins);
|
||||
ValueOperand ToTempValue(LInstruction* ins, size_t pos);
|
||||
|
||||
// Functions for LTestVAndBranch.
|
||||
Register splitTagForTest(const ValueOperand& value);
|
||||
|
||||
public:
|
||||
CodeGeneratorMIPS64(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
|
||||
: CodeGeneratorMIPSShared(gen, graph, masm)
|
||||
{ }
|
||||
|
||||
public:
|
||||
void visitBox(LBox* box);
|
||||
void visitUnbox(LUnbox* unbox);
|
||||
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
};
|
||||
|
||||
typedef CodeGeneratorMIPS64 CodeGeneratorSpecific;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips64_CodeGenerator_mips64_h */
|
||||
@@ -0,0 +1,52 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips64_LIR_mips64_h
|
||||
#define jit_mips64_LIR_mips64_h
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class LUnbox : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Unbox);
|
||||
|
||||
explicit LUnbox(const LAllocation& input) {
|
||||
setOperand(0, input);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
|
||||
MUnbox* mir() const {
|
||||
return mir_->toUnbox();
|
||||
}
|
||||
const char* extraName() const {
|
||||
return StringFromMIRType(mir()->type());
|
||||
}
|
||||
};
|
||||
|
||||
class LUnboxFloatingPoint : public LUnbox
|
||||
{
|
||||
MIRType type_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(UnboxFloatingPoint);
|
||||
|
||||
LUnboxFloatingPoint(const LAllocation& input, MIRType type)
|
||||
: LUnbox(input),
|
||||
type_(type)
|
||||
{ }
|
||||
|
||||
MIRType type() const {
|
||||
return type_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips64_LIR_mips64_h */
|
||||
@@ -0,0 +1,16 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips64_LOpcodes_mips64_h__
|
||||
#define jit_mips64_LOpcodes_mips64_h__
|
||||
|
||||
#include "jit/shared/LOpcodes-shared.h"
|
||||
|
||||
#define LIR_CPU_OPCODE_LIST(_) \
|
||||
_(ModMaskI) \
|
||||
_(UDivOrMod)
|
||||
|
||||
#endif // jit_mips64_LOpcodes_mips64_h__
|
||||
@@ -0,0 +1,128 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/mips64/Lowering-mips64.h"
|
||||
|
||||
#include "jit/mips64/Assembler-mips64.h"
|
||||
|
||||
#include "jit/MIR.h"
|
||||
|
||||
#include "jit/shared/Lowering-shared-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1,
|
||||
Register reg2, bool useAtStart)
|
||||
{
|
||||
MOZ_ASSERT(mir->type() == MIRType_Value);
|
||||
|
||||
ensureDefined(mir);
|
||||
lir->setOperand(n, LUse(reg1, mir->virtualRegister(), useAtStart));
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::visitBox(MBox* box)
|
||||
{
|
||||
MDefinition* opd = box->getOperand(0);
|
||||
|
||||
// If the operand is a constant, emit near its uses.
|
||||
if (opd->isConstant() && box->canEmitAtUses()) {
|
||||
emitAtUses(box);
|
||||
return;
|
||||
}
|
||||
|
||||
if (opd->isConstant()) {
|
||||
define(new(alloc()) LValue(opd->toConstant()->value()), box, LDefinition(LDefinition::BOX));
|
||||
} else {
|
||||
LBox* ins = new(alloc()) LBox(useRegister(opd), opd->type());
|
||||
define(ins, box, LDefinition(LDefinition::BOX));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::visitUnbox(MUnbox* unbox)
|
||||
{
|
||||
MDefinition* box = unbox->getOperand(0);
|
||||
|
||||
if (box->type() == MIRType_ObjectOrNull) {
|
||||
LUnboxObjectOrNull* lir = new(alloc()) LUnboxObjectOrNull(useRegisterAtStart(box));
|
||||
if (unbox->fallible())
|
||||
assignSnapshot(lir, unbox->bailoutKind());
|
||||
defineReuseInput(lir, unbox, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(box->type() == MIRType_Value);
|
||||
|
||||
LUnbox* lir;
|
||||
if (IsFloatingPointType(unbox->type())) {
|
||||
lir = new(alloc()) LUnboxFloatingPoint(useRegisterAtStart(box), unbox->type());
|
||||
} else if (unbox->fallible()) {
|
||||
// If the unbox is fallible, load the Value in a register first to
|
||||
// avoid multiple loads.
|
||||
lir = new(alloc()) LUnbox(useRegisterAtStart(box));
|
||||
} else {
|
||||
lir = new(alloc()) LUnbox(useAtStart(box));
|
||||
}
|
||||
|
||||
if (unbox->fallible())
|
||||
assignSnapshot(lir, unbox->bailoutKind());
|
||||
|
||||
define(lir, unbox);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::visitReturn(MReturn* ret)
|
||||
{
|
||||
MDefinition* opd = ret->getOperand(0);
|
||||
MOZ_ASSERT(opd->type() == MIRType_Value);
|
||||
|
||||
LReturn* ins = new(alloc()) LReturn;
|
||||
ins->setOperand(0, useFixed(opd, JSReturnReg));
|
||||
add(ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::defineUntypedPhi(MPhi* phi, size_t lirIndex)
|
||||
{
|
||||
defineTypedPhi(phi, lirIndex);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition,
|
||||
LBlock* block, size_t lirIndex)
|
||||
{
|
||||
lowerTypedPhiInput(phi, inputPosition, block, lirIndex);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::lowerTruncateDToInt32(MTruncateToInt32* ins)
|
||||
{
|
||||
MDefinition* opd = ins->input();
|
||||
MOZ_ASSERT(opd->type() == MIRType_Double);
|
||||
|
||||
define(new(alloc())
|
||||
LTruncateDToInt32(useRegister(opd), tempDouble()), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::lowerTruncateFToInt32(MTruncateToInt32* ins)
|
||||
{
|
||||
MDefinition* opd = ins->input();
|
||||
MOZ_ASSERT(opd->type() == MIRType_Float32);
|
||||
|
||||
define(new(alloc())
|
||||
LTruncateFToInt32(useRegister(opd), tempFloat32()), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS64::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(), temp(), temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips64_Lowering_mips64_h
|
||||
#define jit_mips64_Lowering_mips64_h
|
||||
|
||||
#include "jit/mips-shared/Lowering-mips-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
|
||||
{
|
||||
protected:
|
||||
LIRGeneratorMIPS64(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph)
|
||||
: LIRGeneratorMIPSShared(gen, graph, lirGraph)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
// Adds a box input to an instruction, setting operand |n| to the type and
|
||||
// |n+1| to the payload.
|
||||
void useBoxFixed(LInstruction* lir, size_t n, MDefinition* mir, Register reg1, Register reg2,
|
||||
bool useAtStart = false);
|
||||
|
||||
inline LDefinition tempToUnbox() {
|
||||
return temp();
|
||||
}
|
||||
|
||||
void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
|
||||
void defineUntypedPhi(MPhi* phi, size_t lirIndex);
|
||||
|
||||
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
||||
void lowerTruncateFToInt32(MTruncateToInt32* ins);
|
||||
|
||||
public:
|
||||
void visitBox(MBox* box);
|
||||
void visitUnbox(MUnbox* unbox);
|
||||
void visitReturn(MReturn* ret);
|
||||
void visitRandom(MRandom* ins);
|
||||
};
|
||||
|
||||
typedef LIRGeneratorMIPS64 LIRGeneratorSpecific;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips64_Lowering_mips64_h */
|
||||
@@ -0,0 +1,109 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips64_MacroAssembler_mips64_inl_h
|
||||
#define jit_mips64_MacroAssembler_mips64_inl_h
|
||||
|
||||
#include "jit/mips64/MacroAssembler-mips64.h"
|
||||
|
||||
#include "jit/mips-shared/MacroAssembler-mips-shared-inl.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
//{{{ check_macroassembler_style
|
||||
// ===============================================================
|
||||
// Logical instructions
|
||||
|
||||
void
|
||||
MacroAssembler::andPtr(Register src, Register dest)
|
||||
{
|
||||
ma_and(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_and(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
ma_li(ScratchRegister, ImmWord(imm.value));
|
||||
ma_and(dest.reg, ScratchRegister);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::orPtr(Register src, Register dest)
|
||||
{
|
||||
ma_or(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_or(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
ma_or(dest.reg, src.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xorPtr(Register src, Register dest)
|
||||
{
|
||||
ma_xor(dest, src);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xorPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_xor(dest, imm);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Shift functions
|
||||
|
||||
void
|
||||
MacroAssembler::lshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_dsll(dest, dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::lshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
ma_dsll(dest.reg, dest.reg, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtr(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_dsrl(dest, dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshiftPtrArithmetic(Imm32 imm, Register dest)
|
||||
{
|
||||
ma_dsra(dest, dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::rshift64(Imm32 imm, Register64 dest)
|
||||
{
|
||||
ma_dsrl(dest.reg, dest.reg, imm);
|
||||
}
|
||||
|
||||
//}}} check_macroassembler_style
|
||||
// ===============================================================
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips64_MacroAssembler_mips64_inl_h */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/mips64/MoveEmitter-mips64.h"
|
||||
|
||||
#include "jit/MacroAssembler-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
void
|
||||
MoveEmitterMIPS64::breakCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slotId)
|
||||
{
|
||||
// There is some pattern:
|
||||
// (A -> B)
|
||||
// (B -> A)
|
||||
//
|
||||
// This case handles (A -> B), which we reach first. We save B, then allow
|
||||
// the original move to continue.
|
||||
switch (type) {
|
||||
case MoveOp::FLOAT32:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchFloat32Reg;
|
||||
masm.loadFloat32(getAdjustedAddress(to), temp);
|
||||
masm.storeFloat32(temp, cycleSlot(slotId));
|
||||
} else {
|
||||
masm.storeFloat32(to.floatReg(), cycleSlot(slotId));
|
||||
}
|
||||
break;
|
||||
case MoveOp::DOUBLE:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchDoubleReg;
|
||||
masm.loadDouble(getAdjustedAddress(to), temp);
|
||||
masm.storeDouble(temp, cycleSlot(slotId));
|
||||
} else {
|
||||
masm.storeDouble(to.floatReg(), cycleSlot(slotId));
|
||||
}
|
||||
break;
|
||||
case MoveOp::INT32:
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.load32(getAdjustedAddress(to), temp);
|
||||
masm.store32(temp, cycleSlot(0));
|
||||
} else {
|
||||
// Second scratch register should not be moved by MoveEmitter.
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.store32(to.reg(), cycleSlot(0));
|
||||
}
|
||||
break;
|
||||
case MoveOp::GENERAL:
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.loadPtr(getAdjustedAddress(to), temp);
|
||||
masm.storePtr(temp, cycleSlot(0));
|
||||
} else {
|
||||
// Second scratch register should not be moved by MoveEmitter.
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.storePtr(to.reg(), cycleSlot(0));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected move type");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MoveEmitterMIPS64::completeCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slotId)
|
||||
{
|
||||
// There is some pattern:
|
||||
// (A -> B)
|
||||
// (B -> A)
|
||||
//
|
||||
// This case handles (B -> A), which we reach last. We emit a move from the
|
||||
// saved value of B, to A.
|
||||
switch (type) {
|
||||
case MoveOp::FLOAT32:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchFloat32Reg;
|
||||
masm.loadFloat32(cycleSlot(slotId), temp);
|
||||
masm.storeFloat32(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
masm.loadFloat32(cycleSlot(slotId), to.floatReg());
|
||||
}
|
||||
break;
|
||||
case MoveOp::DOUBLE:
|
||||
if (to.isMemory()) {
|
||||
FloatRegister temp = ScratchDoubleReg;
|
||||
masm.loadDouble(cycleSlot(slotId), temp);
|
||||
masm.storeDouble(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
masm.loadDouble(cycleSlot(slotId), to.floatReg());
|
||||
}
|
||||
break;
|
||||
case MoveOp::INT32:
|
||||
MOZ_ASSERT(slotId == 0);
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.load32(cycleSlot(0), temp);
|
||||
masm.store32(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
// Second scratch register should not be moved by MoveEmitter.
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.load32(cycleSlot(0), to.reg());
|
||||
}
|
||||
break;
|
||||
case MoveOp::GENERAL:
|
||||
MOZ_ASSERT(slotId == 0);
|
||||
if (to.isMemory()) {
|
||||
Register temp = tempReg();
|
||||
masm.loadPtr(cycleSlot(0), temp);
|
||||
masm.storePtr(temp, getAdjustedAddress(to));
|
||||
} else {
|
||||
// Second scratch register should not be moved by MoveEmitter.
|
||||
MOZ_ASSERT(to.reg() != spilledReg_);
|
||||
masm.loadPtr(cycleSlot(0), to.reg());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected move type");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MoveEmitterMIPS64::emitDoubleMove(const MoveOperand& from, const MoveOperand& to)
|
||||
{
|
||||
// Ensure that we can use ScratchDoubleReg in memory move.
|
||||
MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchDoubleReg);
|
||||
MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchDoubleReg);
|
||||
|
||||
if (from.isFloatReg()) {
|
||||
if (to.isFloatReg()) {
|
||||
masm.moveDouble(from.floatReg(), to.floatReg());
|
||||
} else if (to.isGeneralReg()) {
|
||||
masm.moveFromDouble(from.floatReg(), to.reg());
|
||||
} else {
|
||||
MOZ_ASSERT(to.isMemory());
|
||||
masm.storeDouble(from.floatReg(), getAdjustedAddress(to));
|
||||
}
|
||||
} else if (to.isFloatReg()) {
|
||||
if (from.isMemory())
|
||||
masm.loadDouble(getAdjustedAddress(from), to.floatReg());
|
||||
else
|
||||
masm.moveToDouble(from.reg(), to.floatReg());
|
||||
} else {
|
||||
MOZ_ASSERT(from.isMemory());
|
||||
MOZ_ASSERT(to.isMemory());
|
||||
masm.loadDouble(getAdjustedAddress(from), ScratchDoubleReg);
|
||||
masm.storeDouble(ScratchDoubleReg, getAdjustedAddress(to));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_mips64_MoveEmitter_mips64_h
|
||||
#define jit_mips64_MoveEmitter_mips64_h
|
||||
|
||||
#include "jit/mips-shared/MoveEmitter-mips-shared.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class MoveEmitterMIPS64 : public MoveEmitterMIPSShared
|
||||
{
|
||||
void emitDoubleMove(const MoveOperand& from, const MoveOperand& to);
|
||||
void breakCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slot);
|
||||
void completeCycle(const MoveOperand& from, const MoveOperand& to,
|
||||
MoveOp::Type type, uint32_t slot);
|
||||
|
||||
public:
|
||||
MoveEmitterMIPS64(MacroAssembler& masm)
|
||||
: MoveEmitterMIPSShared(masm)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef MoveEmitterMIPS64 MoveEmitter;
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif /* jit_mips64_MoveEmitter_mips64_h */
|
||||
@@ -0,0 +1,191 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsiter.h"
|
||||
|
||||
#include "jit/BaselineCompiler.h"
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/Linker.h"
|
||||
#include "jit/SharedICHelpers.h"
|
||||
|
||||
#include "jsboolinlines.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
// ICBinaryArith_Int32
|
||||
|
||||
bool
|
||||
ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
// Guard that R0 is an integer and R1 is an integer.
|
||||
Label failure;
|
||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
||||
masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
|
||||
|
||||
// Add R0 and R1. Don't need to explicitly unbox, just use R2's valueReg.
|
||||
Register scratchReg = R2.valueReg();
|
||||
|
||||
Label goodMul, divTest1, divTest2;
|
||||
switch(op_) {
|
||||
case JSOP_ADD:
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
masm.ma_addTestOverflow(scratchReg, ExtractTemp0, ExtractTemp1, &failure);
|
||||
masm.boxValue(JSVAL_TYPE_INT32, scratchReg, R0.valueReg());
|
||||
break;
|
||||
case JSOP_SUB:
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
masm.ma_subTestOverflow(scratchReg, ExtractTemp0, ExtractTemp1, &failure);
|
||||
masm.boxValue(JSVAL_TYPE_INT32, scratchReg, R0.valueReg());
|
||||
break;
|
||||
case JSOP_MUL: {
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
masm.ma_mul_branch_overflow(scratchReg, ExtractTemp0, ExtractTemp1, &failure);
|
||||
|
||||
masm.ma_b(scratchReg, Imm32(0), &goodMul, Assembler::NotEqual, ShortJump);
|
||||
|
||||
// Result is -0 if operands have different signs.
|
||||
masm.as_xor(t8, ExtractTemp0, ExtractTemp1);
|
||||
masm.ma_b(t8, Imm32(0), &failure, Assembler::LessThan, ShortJump);
|
||||
|
||||
masm.bind(&goodMul);
|
||||
masm.boxValue(JSVAL_TYPE_INT32, scratchReg, R0.valueReg());
|
||||
break;
|
||||
}
|
||||
case JSOP_DIV:
|
||||
case JSOP_MOD: {
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
// Check for INT_MIN / -1, it results in a double.
|
||||
masm.ma_b(ExtractTemp0, Imm32(INT_MIN), &divTest1, Assembler::NotEqual, ShortJump);
|
||||
masm.ma_b(ExtractTemp1, Imm32(-1), &failure, Assembler::Equal, ShortJump);
|
||||
masm.bind(&divTest1);
|
||||
|
||||
// Check for division by zero
|
||||
masm.ma_b(ExtractTemp1, Imm32(0), &failure, Assembler::Equal, ShortJump);
|
||||
|
||||
// Check for 0 / X with X < 0 (results in -0).
|
||||
masm.ma_b(ExtractTemp0, Imm32(0), &divTest2, Assembler::NotEqual, ShortJump);
|
||||
masm.ma_b(ExtractTemp1, Imm32(0), &failure, Assembler::LessThan, ShortJump);
|
||||
masm.bind(&divTest2);
|
||||
|
||||
masm.as_div(ExtractTemp0, ExtractTemp1);
|
||||
|
||||
if (op_ == JSOP_DIV) {
|
||||
// Result is a double if the remainder != 0.
|
||||
masm.as_mfhi(scratchReg);
|
||||
masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::NotEqual, ShortJump);
|
||||
masm.as_mflo(scratchReg);
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
} else {
|
||||
Label done;
|
||||
// If X % Y == 0 and X < 0, the result is -0.
|
||||
masm.as_mfhi(scratchReg);
|
||||
masm.ma_b(scratchReg, Imm32(0), &done, Assembler::NotEqual, ShortJump);
|
||||
masm.ma_b(ExtractTemp0, Imm32(0), &failure, Assembler::LessThan, ShortJump);
|
||||
masm.bind(&done);
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSOP_BITOR:
|
||||
masm.as_or(R0.valueReg() , R0.valueReg(), R1.valueReg());
|
||||
break;
|
||||
case JSOP_BITXOR:
|
||||
masm.as_xor(scratchReg, R0.valueReg(), R1.valueReg());
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
break;
|
||||
case JSOP_BITAND:
|
||||
masm.as_and(R0.valueReg() , R0.valueReg(), R1.valueReg());
|
||||
break;
|
||||
case JSOP_LSH:
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
// MIPS will only use 5 lowest bits in R1 as shift offset.
|
||||
masm.ma_sll(scratchReg, ExtractTemp0, ExtractTemp1);
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
break;
|
||||
case JSOP_RSH:
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
masm.ma_sra(scratchReg, ExtractTemp0, ExtractTemp1);
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
break;
|
||||
case JSOP_URSH:
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
masm.unboxInt32(R1, ExtractTemp1);
|
||||
masm.ma_srl(scratchReg, ExtractTemp0, ExtractTemp1);
|
||||
if (allowDouble_) {
|
||||
Label toUint;
|
||||
masm.ma_b(scratchReg, Imm32(0), &toUint, Assembler::LessThan, ShortJump);
|
||||
|
||||
// Move result and box for return.
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&toUint);
|
||||
masm.convertUInt32ToDouble(scratchReg, FloatReg1);
|
||||
masm.boxDouble(FloatReg1, R0);
|
||||
} else {
|
||||
masm.ma_b(scratchReg, Imm32(0), &failure, Assembler::LessThan, ShortJump);
|
||||
// Move result for return.
|
||||
masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unhandled op for BinaryArith_Int32.");
|
||||
}
|
||||
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
// Failure case - jump to next stub
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
Label failure;
|
||||
masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
|
||||
|
||||
switch (op) {
|
||||
case JSOP_BITNOT:
|
||||
masm.not32(R0.valueReg());
|
||||
masm.tagValue(JSVAL_TYPE_INT32, R0.valueReg(), R0);
|
||||
break;
|
||||
case JSOP_NEG:
|
||||
masm.unboxInt32(R0, ExtractTemp0);
|
||||
// Guard against 0 and MIN_INT, both result in a double.
|
||||
masm.branchTest32(Assembler::Zero, ExtractTemp0, Imm32(INT32_MAX), &failure);
|
||||
|
||||
masm.neg32(ExtractTemp0);
|
||||
masm.tagValue(JSVAL_TYPE_INT32, ExtractTemp0, R0);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unexpected op");
|
||||
return false;
|
||||
}
|
||||
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user