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:
2022-10-28 23:01:13 +08:00
parent 5e85317f94
commit a128924c0d
144 changed files with 15303 additions and 2188 deletions
+2 -2
View File
@@ -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:*&]+)"
+2 -2
View File
@@ -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();
}
};
+1 -1
View File
@@ -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 {
+16
View File
@@ -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);
+84
View File
@@ -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 */
+76 -72
View File
@@ -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
+3 -1
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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;
+9 -7
View File
@@ -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
+30 -9
View File
@@ -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
View File
@@ -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);
+3
View File
@@ -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()
+70
View File
@@ -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()
{
+17 -1
View File
@@ -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
+23 -23
View File
@@ -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);
+113 -41
View File
@@ -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());
+1 -1
View File
@@ -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);
-2
View File
@@ -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;
-4
View File
@@ -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);
-1
View File
@@ -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));
-1
View File
@@ -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");
-1
View File
@@ -165,7 +165,6 @@ class PackedScopeCoordinate
F(FORIN) \
F(FOROF) \
F(FORHEAD) \
F(FRESHENBLOCK) \
F(ARGSBODY) \
F(SPREAD) \
F(MUTATEPROTO) \
+53 -127
View File
@@ -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());
+1
View File
@@ -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);
+36 -15
View File
@@ -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;
`);
+4 -4
View File
@@ -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');
+6 -2
View File
@@ -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"); }
+23 -18
View File
@@ -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;
+1 -1
View File
@@ -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"
+3 -1
View File
@@ -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 =
+41 -4
View File
@@ -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()
{
+8 -1
View File
@@ -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
{
+7 -4
View File
@@ -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);
}
+5
View File
@@ -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
+5 -47
View File
@@ -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)
{
+2 -2
View File
@@ -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);
+6 -3
View File
@@ -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
View File
@@ -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);
+6 -1
View File
@@ -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:
+2
View File
@@ -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"
+1 -1
View File
@@ -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_;
+2
View File
@@ -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
+5
View File
@@ -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
View File
@@ -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)
+2
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+13 -4
View File
@@ -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);
}
// ===============================================================
-89
View File
@@ -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
View File
@@ -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,
+2
View File
@@ -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
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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
+2
View File
@@ -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
+1 -1
View File
@@ -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());
+1 -1
View File
@@ -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));
+2
View File
@@ -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
+2
View File
@@ -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
+1
View File
@@ -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"
+42
View File
@@ -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
+2
View File
@@ -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);
+108 -3
View File
@@ -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()
{
+8
View File
@@ -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
+44
View File
@@ -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);
}
}
+2
View File
@@ -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);
+105 -2
View File
@@ -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()
{
+8
View File
@@ -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);
+2 -59
View File
@@ -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
+11 -147
View File
@@ -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;
+28
View File
@@ -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 */
+47
View File
@@ -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
+282
View File
@@ -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);
}
+75
View File
@@ -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 */
+52
View File
@@ -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 */
+16
View File
@@ -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__
+128
View File
@@ -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));
}
+50
View File
@@ -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
+155
View File
@@ -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));
}
}
+34
View File
@@ -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 */
+191
View File
@@ -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