mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1204422 - IonMonkey: MIPS32: Make more MacroAssembler functions can be shared. r=arai (80c0d8b68) - Bug 774364 - Part 3: Move Math.random() to macro assembler. r=sstang,hev,nbp, f=rankov (fd583477e) - Bug 1190454 part 1 - PCCounts use uint64_t instead of a double to count the number of hits. r=evilpie (296eb512b) - Bug 1190454 part 2 - Only compute code coverage of jump targets. r=bhackett (1ba5bb320) - Bug 1190454 part 3 - PCCounts: Collect throw/catch hit counts. r=bhackett (bde4a31b6) - Bug 1190454 part 0 - Remove unnecessary use of AppendArrayJSONMProperties. r=bhackett (de089baf9) - Bug 1189112 - Part 1: Use TraceableVector to simplify tracing of ScriptsAndCountsVector; r=nbp (79f881588) - Bug 1189112 - Part 2: simplify rooting of ScriptsAndCountsVector with PersistentRooted; r=nbp (1b76e8e46) - Bug 1193032 - Part 1: Rename GCRuntime::sliceBudget to defaultTimeBudget; r=jonco (42b59d6b4) - Bug 1193032 - Part 2: Make unlimited SliceBudget initialization explicit; r=ehoogeveen,r=mccr8 (76229de4a) - Bug 1164294 - Implement a linear-time ephemeron marking algorithm, r=terrence, r=jonco (60d4a6791) - Bug 1190454 part 4 - Use mozilla::Vector in js::ScriptCounts. r=bhackett (dda4cd822) - Bug 1190454 part 5 - Update GetPCCountJSON to consider jumpTargets and Throws. r=bhackett (ab0f0d6c4) - Bug 1140059 - Stop leaking mThreadNameFilters in the profiler. (r=mstange) (eaf075fd4) - Bug 1191289 part 1 - Add a JSFriendApi function to produce LCOV information about the current compartment. r=bhackett (c321eebfa) - Bug 1199155. Part 1 - move dom/media/DecodedStream.* to dom/media/mediasink/ and implement the interface of MediaSink. r=roc. (e3f5ac7bb)
This commit is contained in:
@@ -1553,7 +1553,7 @@ nsJSContext::RunCycleCollectorSlice()
|
||||
|
||||
// Decide how long we want to budget for this slice. By default,
|
||||
// use an unlimited budget.
|
||||
js::SliceBudget budget;
|
||||
js::SliceBudget budget = js::SliceBudget::unlimited();
|
||||
|
||||
if (sIncrementalCC) {
|
||||
if (gCCStats.mBeginTime.IsNull()) {
|
||||
|
||||
+12
-9
@@ -31,8 +31,16 @@ struct JS_PUBLIC_API(WorkBudget)
|
||||
* to run for unlimited time, and others are bounded. To reduce the number of
|
||||
* gettimeofday calls, we only check the time every 1000 operations.
|
||||
*/
|
||||
struct JS_PUBLIC_API(SliceBudget)
|
||||
class JS_PUBLIC_API(SliceBudget)
|
||||
{
|
||||
static const int64_t unlimitedDeadline = INT64_MAX;
|
||||
static const intptr_t unlimitedStartCounter = INTPTR_MAX;
|
||||
|
||||
bool checkOverBudget();
|
||||
|
||||
SliceBudget();
|
||||
|
||||
public:
|
||||
// Memory of the originally requested budget. If isUnlimited, neither of
|
||||
// these are in use. If deadline==0, then workBudget is valid. Otherwise
|
||||
// timeBudget is valid.
|
||||
@@ -44,10 +52,11 @@ struct JS_PUBLIC_API(SliceBudget)
|
||||
|
||||
static const intptr_t CounterReset = 1000;
|
||||
|
||||
static const int64_t Unlimited = -1;
|
||||
static const int64_t UnlimitedTimeBudget = -1;
|
||||
static const int64_t UnlimitedWorkBudget = -1;
|
||||
|
||||
/* Use to create an unlimited budget. */
|
||||
SliceBudget();
|
||||
static SliceBudget unlimited() { return SliceBudget(); }
|
||||
|
||||
/* Instantiate as SliceBudget(TimeBudget(n)). */
|
||||
explicit SliceBudget(TimeBudget time);
|
||||
@@ -75,12 +84,6 @@ struct JS_PUBLIC_API(SliceBudget)
|
||||
bool isUnlimited() const { return deadline == unlimitedDeadline; }
|
||||
|
||||
int describe(char* buffer, size_t maxlen) const;
|
||||
|
||||
private:
|
||||
bool checkOverBudget();
|
||||
|
||||
static const int64_t unlimitedDeadline = INT64_MAX;
|
||||
static const intptr_t unlimitedStartCounter = INTPTR_MAX;
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
+34
-17
@@ -140,46 +140,63 @@ class MutableTraceableVectorOperations
|
||||
void erase(T* aBegin, T* aEnd) { vec().erase(aBegin, aEnd); }
|
||||
};
|
||||
|
||||
template <typename A, size_t B, typename C, typename D>
|
||||
class RootedBase<TraceableVector<A,B,C,D>>
|
||||
: public MutableTraceableVectorOperations<JS::Rooted<TraceableVector<A,B,C,D>>, A,B,C,D>
|
||||
template <typename T, size_t N, typename AP, typename TP>
|
||||
class RootedBase<TraceableVector<T,N,AP,TP>>
|
||||
: public MutableTraceableVectorOperations<JS::Rooted<TraceableVector<T,N,AP,TP>>, T,N,AP,TP>
|
||||
{
|
||||
using Vec = TraceableVector<A,B,C,D>;
|
||||
using Vec = TraceableVector<T,N,AP,TP>;
|
||||
|
||||
friend class TraceableVectorOperations<JS::Rooted<Vec>, A,B,C,D>;
|
||||
friend class TraceableVectorOperations<JS::Rooted<Vec>, T,N,AP,TP>;
|
||||
const Vec& extract() const { return *static_cast<const JS::Rooted<Vec>*>(this)->address(); }
|
||||
|
||||
friend class MutableTraceableVectorOperations<JS::Rooted<Vec>, A,B,C,D>;
|
||||
friend class MutableTraceableVectorOperations<JS::Rooted<Vec>, T,N,AP,TP>;
|
||||
Vec& extract() { return *static_cast<JS::Rooted<Vec>*>(this)->address(); }
|
||||
};
|
||||
|
||||
template <typename A, size_t B, typename C, typename D>
|
||||
class MutableHandleBase<TraceableVector<A,B,C,D>>
|
||||
: public MutableTraceableVectorOperations<JS::MutableHandle<TraceableVector<A,B,C,D>>, A,B,C,D>
|
||||
template <typename T, size_t N, typename AP, typename TP>
|
||||
class MutableHandleBase<TraceableVector<T,N,AP,TP>>
|
||||
: public MutableTraceableVectorOperations<JS::MutableHandle<TraceableVector<T,N,AP,TP>>,
|
||||
T,N,AP,TP>
|
||||
{
|
||||
using Vec = TraceableVector<A,B,C,D>;
|
||||
using Vec = TraceableVector<T,N,AP,TP>;
|
||||
|
||||
friend class TraceableVectorOperations<JS::MutableHandle<Vec>, A,B,C,D>;
|
||||
friend class TraceableVectorOperations<JS::MutableHandle<Vec>, T,N,AP,TP>;
|
||||
const Vec& extract() const {
|
||||
return *static_cast<const JS::MutableHandle<Vec>*>(this)->address();
|
||||
}
|
||||
|
||||
friend class MutableTraceableVectorOperations<JS::MutableHandle<Vec>, A,B,C,D>;
|
||||
friend class MutableTraceableVectorOperations<JS::MutableHandle<Vec>, T,N,AP,TP>;
|
||||
Vec& extract() { return *static_cast<JS::MutableHandle<Vec>*>(this)->address(); }
|
||||
};
|
||||
|
||||
template <typename A, size_t B, typename C, typename D>
|
||||
class HandleBase<TraceableVector<A,B,C,D>>
|
||||
: public TraceableVectorOperations<JS::Handle<TraceableVector<A,B,C,D>>, A,B,C,D>
|
||||
template <typename T, size_t N, typename AP, typename TP>
|
||||
class HandleBase<TraceableVector<T,N,AP,TP>>
|
||||
: public TraceableVectorOperations<JS::Handle<TraceableVector<T,N,AP,TP>>, T,N,AP,TP>
|
||||
{
|
||||
using Vec = TraceableVector<A,B,C,D>;
|
||||
using Vec = TraceableVector<T,N,AP,TP>;
|
||||
|
||||
friend class TraceableVectorOperations<JS::Handle<Vec>, A,B,C,D>;
|
||||
friend class TraceableVectorOperations<JS::Handle<Vec>, T,N,AP,TP>;
|
||||
const Vec& extract() const {
|
||||
return *static_cast<const JS::Handle<Vec>*>(this)->address();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t N, typename AP, typename TP>
|
||||
class PersistentRootedBase<TraceableVector<T,N,AP,TP>>
|
||||
: public MutableTraceableVectorOperations<JS::PersistentRooted<TraceableVector<T,N,AP,TP>>,
|
||||
T,N,AP,TP>
|
||||
{
|
||||
using Vec = TraceableVector<T,N,AP,TP>;
|
||||
|
||||
friend class TraceableVectorOperations<JS::PersistentRooted<Vec>, T,N,AP,TP>;
|
||||
const Vec& extract() const {
|
||||
return *static_cast<const JS::PersistentRooted<Vec>*>(this)->address();
|
||||
}
|
||||
|
||||
friend class MutableTraceableVectorOperations<JS::PersistentRooted<Vec>, T,N,AP,TP>;
|
||||
Vec& extract() { return *static_cast<JS::PersistentRooted<Vec>*>(this)->address(); }
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // js_TraceableVector_h
|
||||
|
||||
+24
-8
@@ -27,9 +27,21 @@ GCTraceKindToAscii(JS::TraceKind kind);
|
||||
} // namespace JS
|
||||
|
||||
enum WeakMapTraceKind {
|
||||
DoNotTraceWeakMaps = 0,
|
||||
TraceWeakMapValues = 1,
|
||||
TraceWeakMapKeysValues = 2
|
||||
// Do true ephemeron marking with an iterative weak marking phase.
|
||||
DoNotTraceWeakMaps,
|
||||
|
||||
// Do true ephemeron marking with a weak key lookup marking phase. This is
|
||||
// expected to be constant for the lifetime of a JSTracer; it does not
|
||||
// change when switching from "plain" marking to weak marking.
|
||||
ExpandWeakMaps,
|
||||
|
||||
// Trace through to all values, irrespective of whether the keys are live
|
||||
// or not. Used for non-marking tracers.
|
||||
TraceWeakMapValues,
|
||||
|
||||
// Trace through to all keys and values, irrespective of whether the keys
|
||||
// are live or not. Used for non-marking tracers.
|
||||
TraceWeakMapKeysValues
|
||||
};
|
||||
|
||||
class JS_PUBLIC_API(JSTracer)
|
||||
@@ -38,16 +50,18 @@ class JS_PUBLIC_API(JSTracer)
|
||||
// Return the runtime set on the tracer.
|
||||
JSRuntime* runtime() const { return runtime_; }
|
||||
|
||||
// Return the weak map tracing behavior set on this tracer.
|
||||
WeakMapTraceKind eagerlyTraceWeakMaps() const { return eagerlyTraceWeakMaps_; }
|
||||
// Return the weak map tracing behavior currently set on this tracer.
|
||||
WeakMapTraceKind weakMapAction() const { return weakMapAction_; }
|
||||
|
||||
// An intermediate state on the road from C to C++ style dispatch.
|
||||
enum class TracerKindTag {
|
||||
Marking,
|
||||
WeakMarking, // In weak marking phase: looking up every marked obj/script.
|
||||
Tenuring,
|
||||
Callback
|
||||
};
|
||||
bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking; }
|
||||
bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
|
||||
bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
|
||||
bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
|
||||
bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
|
||||
inline JS::CallbackTracer* asCallbackTracer();
|
||||
@@ -55,13 +69,15 @@ class JS_PUBLIC_API(JSTracer)
|
||||
protected:
|
||||
JSTracer(JSRuntime* rt, TracerKindTag tag,
|
||||
WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
|
||||
: runtime_(rt), tag_(tag), eagerlyTraceWeakMaps_(weakTraceKind)
|
||||
: runtime_(rt), weakMapAction_(weakTraceKind), tag_(tag)
|
||||
{}
|
||||
|
||||
private:
|
||||
JSRuntime* runtime_;
|
||||
WeakMapTraceKind weakMapAction_;
|
||||
|
||||
protected:
|
||||
TracerKindTag tag_;
|
||||
WeakMapTraceKind eagerlyTraceWeakMaps_;
|
||||
};
|
||||
|
||||
namespace JS {
|
||||
|
||||
@@ -657,7 +657,7 @@ StartGC(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
SliceBudget budget;
|
||||
auto budget = SliceBudget::unlimited();
|
||||
if (args.length() >= 1) {
|
||||
uint32_t work = 0;
|
||||
if (!ToUint32(cx, args[0], &work))
|
||||
@@ -699,7 +699,7 @@ GCSlice(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
SliceBudget budget;
|
||||
auto budget = SliceBudget::unlimited();
|
||||
if (args.length() == 1) {
|
||||
uint32_t work = 0;
|
||||
if (!ToUint32(cx, args[0], &work))
|
||||
@@ -2660,6 +2660,56 @@ SetARMHwCapFlags(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetLcovInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() > 1) {
|
||||
JS_ReportError(cx, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject global(cx);
|
||||
if (args.hasDefined(0)) {
|
||||
global = ToObject(cx, args[0]);
|
||||
if (!global) {
|
||||
JS_ReportError(cx, "First argument should be an object");
|
||||
return false;
|
||||
}
|
||||
global = CheckedUnwrap(global);
|
||||
if (!global) {
|
||||
JS_ReportError(cx, "Permission denied to access global");
|
||||
return false;
|
||||
}
|
||||
if (!global->is<GlobalObject>()) {
|
||||
JS_ReportError(cx, "Argument must be a global object");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
global = JS::CurrentGlobalOrNull(cx);
|
||||
}
|
||||
|
||||
size_t length = 0;
|
||||
char* content = nullptr;
|
||||
{
|
||||
AutoCompartment ac(cx, global);
|
||||
content = js::GetCodeCoverageSummary(cx, &length);
|
||||
}
|
||||
|
||||
if (!content)
|
||||
return false;
|
||||
|
||||
JSString* str = JS_NewStringCopyN(cx, content, length);
|
||||
free(content);
|
||||
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
JS_FN_HELP("gc", ::GC, 0, 0,
|
||||
"gc([obj] | 'compartment' [, 'shrinking'])",
|
||||
@@ -3095,6 +3145,11 @@ gc::ZealModeHelpText),
|
||||
" On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n"
|
||||
" flags is available by calling this function with \"help\" as the flag's name"),
|
||||
|
||||
JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
|
||||
"getLcovInfo(global)",
|
||||
" Generate LCOV tracefile for the given compartment. If no global are provided then\n"
|
||||
" the current global is used as the default one.\n"),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
|
||||
@@ -238,6 +238,10 @@ function ignoreGCFunction(mangled)
|
||||
if (fun.indexOf("void nsCOMPtr<T>::Assert_NoQueryNeeded()") >= 0)
|
||||
return true;
|
||||
|
||||
// These call through an 'op' function pointer.
|
||||
if (fun.indexOf("js::WeakMap<Key, Value, HashPolicy>::getDelegate(") >= 0)
|
||||
return true;
|
||||
|
||||
// XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC.
|
||||
if (/refillFreeList/.test(fun) && /\(js::AllowGC\)0u/.test(fun))
|
||||
return true;
|
||||
|
||||
@@ -672,7 +672,8 @@ class OrderedHashMap
|
||||
|
||||
public:
|
||||
Entry() : key(), value() {}
|
||||
Entry(const Key& k, const Value& v) : key(k), value(v) {}
|
||||
template <typename V>
|
||||
Entry(const Key& k, V&& v) : key(k), value(Forward<V>(v)) {}
|
||||
Entry(Entry&& rhs) : key(Move(rhs.key)), value(Move(rhs.value)) {}
|
||||
|
||||
const Key key;
|
||||
@@ -707,7 +708,8 @@ class OrderedHashMap
|
||||
Range all() { return impl.all(); }
|
||||
const Entry* get(const Key& key) const { return impl.get(key); }
|
||||
Entry* get(const Key& key) { return impl.get(key); }
|
||||
bool put(const Key& key, const Value& value) { return impl.put(Entry(key, value)); }
|
||||
template <typename V>
|
||||
bool put(const Key& key, V&& value) { return impl.put(Entry(key, Forward<V>(value))); }
|
||||
bool remove(const Key& key, bool* foundp) { return impl.remove(key, foundp); }
|
||||
bool clear() { return impl.clear(); }
|
||||
|
||||
|
||||
@@ -775,7 +775,7 @@ class GCRuntime
|
||||
uint64_t majorGCCount() const { return majorGCNumber; }
|
||||
void incMajorGcNumber() { ++majorGCNumber; }
|
||||
|
||||
int64_t defaultSliceBudget() const { return sliceBudget; }
|
||||
int64_t defaultSliceBudget() const { return defaultTimeBudget_; }
|
||||
|
||||
bool isIncrementalGc() const { return isIncremental; }
|
||||
bool isFullGc() const { return isFull; }
|
||||
@@ -1162,7 +1162,7 @@ class GCRuntime
|
||||
bool interFrameGC;
|
||||
|
||||
/* Default budget for incremental GC slice. See js/SliceBudget.h. */
|
||||
int64_t sliceBudget;
|
||||
int64_t defaultTimeBudget_;
|
||||
|
||||
/*
|
||||
* We disable incremental GC if we encounter a js::Class with a trace hook
|
||||
|
||||
+111
-11
@@ -613,6 +613,74 @@ DispatchToTracer(JSTracer* trc, T* thingp, const char* name)
|
||||
|
||||
/*** GC Marking Interface *************************************************************************/
|
||||
|
||||
namespace js {
|
||||
|
||||
typedef bool DoNothingMarkingType;
|
||||
|
||||
template <typename T>
|
||||
struct LinearlyMarkedEphemeronKeyType {
|
||||
typedef DoNothingMarkingType Type;
|
||||
};
|
||||
|
||||
// For now, we only handle JSObject* keys, but the linear time algorithm can be
|
||||
// easily extended by adding in more types here, then making
|
||||
// GCMarker::traverse<T> call markPotentialEphemeronKey.
|
||||
template <>
|
||||
struct LinearlyMarkedEphemeronKeyType<JSObject*> {
|
||||
typedef JSObject* Type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct LinearlyMarkedEphemeronKeyType<JSScript*> {
|
||||
typedef JSScript* Type;
|
||||
};
|
||||
|
||||
void
|
||||
GCMarker::markEphemeronValues(gc::Cell* markedCell, WeakEntryVector& values)
|
||||
{
|
||||
size_t initialLen = values.length();
|
||||
for (size_t i = 0; i < initialLen; i++)
|
||||
values[i].weakmap->maybeMarkEntry(this, markedCell, values[i].key);
|
||||
|
||||
// The vector should not be appended to during iteration because the key is
|
||||
// already marked, and even in cases where we have a multipart key, we
|
||||
// should only be inserting entries for the unmarked portions.
|
||||
MOZ_ASSERT(values.length() == initialLen);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
GCMarker::markPotentialEphemeronKeyHelper(T markedThing)
|
||||
{
|
||||
if (!isWeakMarkingTracer())
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(gc::TenuredCell::fromPointer(markedThing)->zone()->isGCMarking());
|
||||
MOZ_ASSERT(!gc::TenuredCell::fromPointer(markedThing)->zone()->isGCSweeping());
|
||||
|
||||
auto weakValues = weakKeys.get(JS::GCCellPtr(markedThing));
|
||||
if (!weakValues)
|
||||
return;
|
||||
|
||||
markEphemeronValues(markedThing, weakValues->value);
|
||||
weakValues->value.clear(); // If key address is reused, it should do nothing
|
||||
}
|
||||
|
||||
template <>
|
||||
void
|
||||
GCMarker::markPotentialEphemeronKeyHelper(bool)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
GCMarker::markPotentialEphemeronKey(T* thing)
|
||||
{
|
||||
markPotentialEphemeronKeyHelper<typename LinearlyMarkedEphemeronKeyType<T*>::Type>(thing);
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
template <typename T>
|
||||
static inline bool
|
||||
MustSkipMarking(T thing)
|
||||
@@ -711,7 +779,6 @@ js::GCMarker::markAndTraceChildren(T* thing)
|
||||
namespace js {
|
||||
template <> void GCMarker::traverse(BaseShape* thing) { markAndTraceChildren(thing); }
|
||||
template <> void GCMarker::traverse(JS::Symbol* thing) { markAndTraceChildren(thing); }
|
||||
template <> void GCMarker::traverse(JSScript* thing) { markAndTraceChildren(thing); }
|
||||
} // namespace js
|
||||
|
||||
// Shape, BaseShape, String, and Symbol are extremely common, but have simple
|
||||
@@ -735,18 +802,22 @@ template <> void GCMarker::traverse(Shape* thing) { markAndScan(thing); }
|
||||
// Object and ObjectGroup are extremely common and can contain arbitrarily
|
||||
// nested graphs, so are not trivially inlined. In this case we use a mark
|
||||
// stack to control recursion. JitCode shares none of these properties, but is
|
||||
// included for historical reasons.
|
||||
// included for historical reasons. JSScript normally cannot recurse, but may
|
||||
// be used as a weakmap key and thereby recurse into weakmapped values.
|
||||
template <typename T>
|
||||
void
|
||||
js::GCMarker::markAndPush(StackTag tag, T* thing)
|
||||
{
|
||||
if (mark(thing))
|
||||
pushTaggedPtr(tag, thing);
|
||||
if (!mark(thing))
|
||||
return;
|
||||
pushTaggedPtr(tag, thing);
|
||||
markPotentialEphemeronKey(thing);
|
||||
}
|
||||
namespace js {
|
||||
template <> void GCMarker::traverse(JSObject* thing) { markAndPush(ObjectTag, thing); }
|
||||
template <> void GCMarker::traverse(ObjectGroup* thing) { markAndPush(GroupTag, thing); }
|
||||
template <> void GCMarker::traverse(jit::JitCode* thing) { markAndPush(JitCodeTag, thing); }
|
||||
template <> void GCMarker::traverse(JSScript* thing) { markAndPush(ScriptTag, thing); }
|
||||
} // namespace js
|
||||
|
||||
namespace js {
|
||||
@@ -1250,6 +1321,10 @@ GCMarker::processMarkStackTop(SliceBudget& budget)
|
||||
return reinterpret_cast<jit::JitCode*>(addr)->traceChildren(this);
|
||||
}
|
||||
|
||||
case ScriptTag: {
|
||||
return reinterpret_cast<JSScript*>(addr)->traceChildren(this);
|
||||
}
|
||||
|
||||
case SavedValueArrayTag: {
|
||||
MOZ_ASSERT(!(addr & CellMask));
|
||||
JSObject* obj = reinterpret_cast<JSObject*>(addr);
|
||||
@@ -1303,6 +1378,7 @@ GCMarker::processMarkStackTop(SliceBudget& budget)
|
||||
return;
|
||||
}
|
||||
|
||||
markPotentialEphemeronKey(obj);
|
||||
ObjectGroup* group = obj->groupFromGC();
|
||||
traverseEdge(obj, group);
|
||||
|
||||
@@ -1563,11 +1639,13 @@ MarkStack::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
/*** GCMarker *************************************************************************************/
|
||||
|
||||
/*
|
||||
* DoNotTraceWeakMaps: the GC is recomputing the liveness of WeakMap entries,
|
||||
* so we delay visting entries.
|
||||
* ExpandWeakMaps: the GC is recomputing the liveness of WeakMap entries by
|
||||
* expanding each live WeakMap into its constituent key->value edges, a table
|
||||
* of which will be consulted in a later phase whenever marking a potential
|
||||
* key.
|
||||
*/
|
||||
GCMarker::GCMarker(JSRuntime* rt)
|
||||
: JSTracer(rt, JSTracer::TracerKindTag::Marking, DoNotTraceWeakMaps),
|
||||
: JSTracer(rt, JSTracer::TracerKindTag::Marking, ExpandWeakMaps),
|
||||
stack(size_t(-1)),
|
||||
color(BLACK),
|
||||
unmarkedArenaStackTop(nullptr),
|
||||
@@ -1580,7 +1658,7 @@ GCMarker::GCMarker(JSRuntime* rt)
|
||||
bool
|
||||
GCMarker::init(JSGCMode gcMode)
|
||||
{
|
||||
return stack.init(gcMode);
|
||||
return stack.init(gcMode) && weakKeys.init();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1589,10 +1667,10 @@ GCMarker::start()
|
||||
MOZ_ASSERT(!started);
|
||||
started = true;
|
||||
color = BLACK;
|
||||
linearWeakMarkingDisabled_ = false;
|
||||
|
||||
MOZ_ASSERT(!unmarkedArenaStackTop);
|
||||
MOZ_ASSERT(markLaterArenas == 0);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1608,6 +1686,7 @@ GCMarker::stop()
|
||||
|
||||
/* Free non-ballast stack memory. */
|
||||
stack.reset();
|
||||
weakKeys.clear();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1632,6 +1711,27 @@ GCMarker::reset()
|
||||
MOZ_ASSERT(!markLaterArenas);
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::enterWeakMarkingMode()
|
||||
{
|
||||
MOZ_ASSERT(tag_ == TracerKindTag::Marking);
|
||||
if (linearWeakMarkingDisabled_)
|
||||
return;
|
||||
|
||||
// During weak marking mode, we maintain a table mapping weak keys to
|
||||
// entries in known-live weakmaps.
|
||||
if (weakMapAction() == ExpandWeakMaps) {
|
||||
tag_ = TracerKindTag::WeakMarking;
|
||||
|
||||
for (GCCompartmentGroupIter c(runtime()); !c.done(); c.next()) {
|
||||
for (WeakMapBase* m = c->gcWeakMapList; m; m = m->next) {
|
||||
if (m->marked)
|
||||
m->markEphemeronEntries(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCMarker::markDelayedChildren(ArenaHeader* aheader)
|
||||
{
|
||||
@@ -2320,8 +2420,8 @@ struct AssertNonGrayTracer : public JS::CallbackTracer {
|
||||
struct UnmarkGrayTracer : public JS::CallbackTracer
|
||||
{
|
||||
/*
|
||||
* We set eagerlyTraceWeakMaps to false because the cycle collector will fix
|
||||
* up any color mismatches involving weakmaps when it runs.
|
||||
* We set weakMapAction to DoNotTraceWeakMaps because the cycle collector
|
||||
* will fix up any color mismatches involving weakmaps when it runs.
|
||||
*/
|
||||
explicit UnmarkGrayTracer(JSRuntime* rt)
|
||||
: JS::CallbackTracer(rt, DoNotTraceWeakMaps),
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
#define gc_Marking_h
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "ds/OrderedHashTable.h"
|
||||
#include "gc/Heap.h"
|
||||
#include "gc/Tracer.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "js/TracingAPI.h"
|
||||
|
||||
@@ -132,6 +136,35 @@ class MarkStack
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
};
|
||||
|
||||
class WeakMapBase;
|
||||
|
||||
namespace gc {
|
||||
|
||||
struct WeakKeyTableHashPolicy {
|
||||
typedef JS::GCCellPtr Lookup;
|
||||
static HashNumber hash(const Lookup& v) { return mozilla::HashGeneric(v.asCell()); }
|
||||
static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; }
|
||||
static bool isEmpty(const JS::GCCellPtr& v) { return !v; }
|
||||
static void makeEmpty(JS::GCCellPtr* vp) { *vp = nullptr; }
|
||||
};
|
||||
|
||||
struct WeakMarkable {
|
||||
WeakMapBase* weakmap;
|
||||
JS::GCCellPtr key;
|
||||
|
||||
WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr keyArg)
|
||||
: weakmap(weakmapArg), key(keyArg) {}
|
||||
};
|
||||
|
||||
typedef Vector<WeakMarkable, 2, js::SystemAllocPolicy> WeakEntryVector;
|
||||
|
||||
typedef OrderedHashMap<JS::GCCellPtr,
|
||||
WeakEntryVector,
|
||||
WeakKeyTableHashPolicy,
|
||||
js::SystemAllocPolicy> WeakKeyTable;
|
||||
|
||||
} /* namespace gc */
|
||||
|
||||
class GCMarker : public JSTracer
|
||||
{
|
||||
public:
|
||||
@@ -173,6 +206,23 @@ class GCMarker : public JSTracer
|
||||
}
|
||||
uint32_t markColor() const { return color; }
|
||||
|
||||
void enterWeakMarkingMode();
|
||||
|
||||
void leaveWeakMarkingMode() {
|
||||
MOZ_ASSERT_IF(weakMapAction() == ExpandWeakMaps && !linearWeakMarkingDisabled_, tag_ == TracerKindTag::WeakMarking);
|
||||
tag_ = TracerKindTag::Marking;
|
||||
|
||||
// Table is expensive to maintain when not in weak marking mode, so
|
||||
// we'll rebuild it upon entry rather than allow it to contain stale
|
||||
// data.
|
||||
weakKeys.clear();
|
||||
}
|
||||
|
||||
void abortLinearWeakMarking() {
|
||||
leaveWeakMarkingMode();
|
||||
linearWeakMarkingDisabled_ = true;
|
||||
}
|
||||
|
||||
void delayMarkingArena(gc::ArenaHeader* aheader);
|
||||
void delayMarkingChildren(const void* thing);
|
||||
void markDelayedChildren(gc::ArenaHeader* aheader);
|
||||
@@ -195,6 +245,14 @@ class GCMarker : public JSTracer
|
||||
bool shouldCheckCompartments() { return strictCompartmentChecking; }
|
||||
#endif
|
||||
|
||||
void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry);
|
||||
|
||||
/*
|
||||
* Mapping from not yet marked keys to a vector of all values that the key
|
||||
* maps to in any live weak map.
|
||||
*/
|
||||
gc::WeakKeyTable weakKeys;
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
void checkZone(void* p);
|
||||
@@ -213,6 +271,7 @@ class GCMarker : public JSTracer
|
||||
GroupTag,
|
||||
SavedValueArrayTag,
|
||||
JitCodeTag,
|
||||
ScriptTag,
|
||||
LastTag = JitCodeTag
|
||||
};
|
||||
|
||||
@@ -230,6 +289,8 @@ class GCMarker : public JSTracer
|
||||
template <typename T> void markAndTraceChildren(T* thing);
|
||||
template <typename T> void markAndPush(StackTag tag, T* thing);
|
||||
template <typename T> void markAndScan(T* thing);
|
||||
template <typename T> void markPotentialEphemeronKeyHelper(T oldThing);
|
||||
template <typename T> void markPotentialEphemeronKey(T* oldThing);
|
||||
void eagerlyMarkChildren(JSLinearString* str);
|
||||
void eagerlyMarkChildren(JSRope* rope);
|
||||
void eagerlyMarkChildren(JSString* str);
|
||||
@@ -287,6 +348,12 @@ class GCMarker : public JSTracer
|
||||
/* Pointer to the top of the stack of arenas we are delaying marking on. */
|
||||
js::gc::ArenaHeader* unmarkedArenaStackTop;
|
||||
|
||||
/*
|
||||
* If the weakKeys table OOMs, disable the linear algorithm and fall back
|
||||
* to iterating until the next GC.
|
||||
*/
|
||||
bool linearWeakMarkingDisabled_;
|
||||
|
||||
/* Count of arenas that are currently in the stack. */
|
||||
mozilla::DebugOnly<size_t> markLaterArenas;
|
||||
|
||||
|
||||
@@ -343,12 +343,6 @@ js::gc::GCRuntime::markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark)
|
||||
MarkPersistentRootedChains(trc);
|
||||
}
|
||||
|
||||
if (rt->scriptAndCountsVector) {
|
||||
ScriptAndCountsVector& vec = *rt->scriptAndCountsVector;
|
||||
for (size_t i = 0; i < vec.length(); i++)
|
||||
TraceRoot(trc, &vec[i].script, "scriptAndCountsVector");
|
||||
}
|
||||
|
||||
if (!rt->isBeingDestroyed() && !rt->isHeapMinorCollecting()) {
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_RUNTIME_DATA);
|
||||
|
||||
|
||||
+2
-2
@@ -23,11 +23,11 @@ namespace js {
|
||||
// purposes as well.
|
||||
//
|
||||
// One commonly misunderstood subtlety of the tracing architecture is the role
|
||||
// of graph verticies versus graph edges. Graph verticies are the heap
|
||||
// of graph vertices versus graph edges. Graph vertices are the heap
|
||||
// allocations -- GC things -- that are returned by Allocate. Graph edges are
|
||||
// pointers -- including tagged pointers like Value and jsid -- that link the
|
||||
// allocations into a complex heap. The tracing API deals *only* with edges.
|
||||
// Any action taken on the target of a graph edge is independent to the tracing
|
||||
// Any action taken on the target of a graph edge is independent of the tracing
|
||||
// itself.
|
||||
//
|
||||
// Another common misunderstanding relates to the role of the JSTracer. The
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
// |jit-test| --code-coverage;
|
||||
|
||||
// Currently the Jit integration has a few issues, let's keep this test
|
||||
// case deterministic.
|
||||
//
|
||||
// - Baseline OSR increments the loop header twice.
|
||||
// - Ion is not updating any counter yet.
|
||||
//
|
||||
if (getJitCompilerOptions()["ion.warmup.trigger"] != 30)
|
||||
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||
if (getJitCompilerOptions()["baseline.warmup.trigger"] != 10)
|
||||
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||
|
||||
function checkLcov(fun) {
|
||||
var keys = [ "TN", "SF", "FN", "FNDA", "FNF", "FNH", "BRDA", "BRF", "BRH", "DA", "LF", "LH" ];
|
||||
function startsWithKey(s) {
|
||||
for (k of keys) {
|
||||
if (s.startsWith(k))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Extract the body of the function, as the code to be executed.
|
||||
var source = fun.toSource();
|
||||
source = source.slice(source.indexOf('{') + 1, source.lastIndexOf('}'));
|
||||
|
||||
// Extract comment starting with the previous keys, as a reference of the
|
||||
// output expected from getLcovInfo.
|
||||
var lcovRef = [];
|
||||
var currLine = 0;
|
||||
var currFun = "<badfunction>";
|
||||
for (var line of source.split('\n')) {
|
||||
currLine++;
|
||||
for (var comment of line.split("//").slice(1)) {
|
||||
if (!startsWithKey(comment))
|
||||
continue;
|
||||
comment = comment.trim();
|
||||
if (comment.startsWith("FN:"))
|
||||
currFun = comment.split(',')[1];
|
||||
comment = comment.replace('$', currLine);
|
||||
comment = comment.replace('%', currFun);
|
||||
lcovRef.push(comment);
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the code, and generate the Lcov result from the execution.
|
||||
var g = newGlobal();
|
||||
g.eval(source);
|
||||
var lcovResRaw = getLcovInfo(g);
|
||||
|
||||
// Check that all the lines are present the result.
|
||||
var lcovRes = lcovResRaw.split('\n');
|
||||
for (ref of lcovRef) {
|
||||
if (lcovRes.indexOf(ref) == -1) {
|
||||
print("Cannot find `" + ref + "` in the following Lcov result:\n", lcovResRaw);
|
||||
print("In the following source:\n", source);
|
||||
assertEq(true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
",".split(','); //DA:$,1
|
||||
//FNF:1
|
||||
//FNH:1
|
||||
//LF:1
|
||||
//LH:1
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
function f() { //FN:$,f
|
||||
",".split(','); //DA:$,0
|
||||
}
|
||||
",".split(','); //DA:$,1
|
||||
//FNF:2
|
||||
//FNH:1
|
||||
//LF:2
|
||||
//LH:1
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
function f() { //FN:$,f //FNDA:1,%
|
||||
",".split(','); //DA:$,1
|
||||
}
|
||||
f(); //DA:$,1
|
||||
//FNF:2
|
||||
//FNH:2
|
||||
//LF:2
|
||||
//LH:2
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
var l = ",".split(','); //DA:$,1
|
||||
if (l.length == 3) //DA:$,1
|
||||
l.push(''); //DA:$,0
|
||||
else
|
||||
l.pop(); //DA:$,1
|
||||
//FNF:1
|
||||
//FNH:1
|
||||
//LF:4
|
||||
//LH:3
|
||||
//BRF:1
|
||||
//BRH:1
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
var l = ",".split(','); //DA:$,1
|
||||
if (l.length == 2) //DA:$,1
|
||||
l.push(''); //DA:$,1
|
||||
else
|
||||
l.pop(); //DA:$,0
|
||||
//FNF:1
|
||||
//FNH:1
|
||||
//LF:4
|
||||
//LH:3
|
||||
//BRF:1
|
||||
//BRH:1
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
var l = ",".split(','); //DA:$,1
|
||||
if (l.length == 2) //DA:$,1
|
||||
l.push(''); //DA:$,1
|
||||
else {
|
||||
if (l.length == 1) //DA:$,0
|
||||
l.pop(); //DA:$,0
|
||||
}
|
||||
//FNF:1
|
||||
//FNH:1
|
||||
//LF:5
|
||||
//LH:3
|
||||
//BRF:2
|
||||
//BRH:1
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
function f(i) { //FN:$,f //FNDA:2,%
|
||||
var x = 0; //DA:$,2
|
||||
while (i--) { // Currently OSR wrongly count the loop header twice.
|
||||
// So instead of DA:$,12 , we have DA:$,13 .
|
||||
x += i; //DA:$,10
|
||||
x = x / 2; //DA:$,10
|
||||
}
|
||||
return x; //DA:$,2
|
||||
//BRF:1
|
||||
//BRH:1
|
||||
}
|
||||
|
||||
f(5); //DA:$,1
|
||||
f(5); //DA:$,1
|
||||
//FNF:2
|
||||
//FNH:2
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
try { //DA:$,1
|
||||
var l = ",".split(','); //DA:$,1
|
||||
if (l.length == 2) { //DA:$,1 // BRDA:$,0
|
||||
l.push(''); //DA:$,1
|
||||
throw l; //DA:$,1
|
||||
}
|
||||
l.pop(); //DA:$,0
|
||||
} catch (x) { //DA:$,1
|
||||
x.pop(); //DA:$,1
|
||||
}
|
||||
//FNF:1
|
||||
//FNH:1
|
||||
//LF:9 // Expected LF:8 , Apparently if the first statement is a try, the
|
||||
// statement following the "try{" statement is visited twice.
|
||||
//LH:8 // Expected LH:7
|
||||
//BRF:1
|
||||
//BRH:1
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
var l = ",".split(','); //DA:$,1
|
||||
try { //DA:$,1
|
||||
try { //DA:$,1
|
||||
if (l.length == 2) { //DA:$,1 // BRDA:$,0
|
||||
l.push(''); //DA:$,1
|
||||
throw l; //DA:$,1
|
||||
}
|
||||
l.pop(); //DA:$,0 // BRDA:$,-
|
||||
} finally { //DA:$,1
|
||||
l.pop(); //DA:$,1
|
||||
}
|
||||
} catch (x) { //DA:$,1
|
||||
}
|
||||
//FNF:1
|
||||
//FNH:1
|
||||
//LF:10
|
||||
//LH:9
|
||||
//BRF:2
|
||||
//BRH:1
|
||||
});
|
||||
|
||||
checkLcov(function () { //FN:$,top-level //FNDA:1,%
|
||||
function f() { //FN:$,f //FNDA:1,%
|
||||
throw 1; //DA:$,1
|
||||
f(); //DA:$,0
|
||||
}
|
||||
var l = ",".split(','); //DA:$,1
|
||||
try { //DA:$,1
|
||||
f(); //DA:$,1
|
||||
f(); //DA:$,0
|
||||
} catch (x) { //DA:$,1
|
||||
}
|
||||
//FNF:2
|
||||
//FNH:2
|
||||
//LF:7
|
||||
//LH:5
|
||||
//BRF:0
|
||||
//BRH:0
|
||||
});
|
||||
@@ -0,0 +1,193 @@
|
||||
// These tests will be using object literals as keys, and we want some of them
|
||||
// to be dead after being inserted into a WeakMap. That means we must wrap
|
||||
// everything in functions because it seems like the toplevel script hangs onto
|
||||
// its object literals.
|
||||
|
||||
// All reachable keys should be found, and the rest should be swept.
|
||||
function basicSweeping() {
|
||||
var wm1 = new WeakMap();
|
||||
wm1.set({'name': 'obj1'}, {'name': 'val1'});
|
||||
var hold = {'name': 'obj2'};
|
||||
wm1.set(hold, {'name': 'val2'});
|
||||
wm1.set({'name': 'obj3'}, {'name': 'val3'});
|
||||
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
|
||||
assertEq(wm1.get(hold).name, 'val2');
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
|
||||
}
|
||||
|
||||
basicSweeping();
|
||||
|
||||
// Keep values alive even when they are only referenced by (live) WeakMap values.
|
||||
function weakGraph() {
|
||||
var wm1 = new WeakMap();
|
||||
var obj1 = {'name': 'obj1'};
|
||||
var obj2 = {'name': 'obj2'};
|
||||
var obj3 = {'name': 'obj3'};
|
||||
var obj4 = {'name': 'obj4'};
|
||||
var clear = {'name': ''}; // Make the interpreter forget about the last obj created
|
||||
|
||||
wm1.set(obj2, obj3);
|
||||
wm1.set(obj3, obj1);
|
||||
wm1.set(obj4, obj1); // This edge will be cleared
|
||||
obj1 = obj3 = obj4 = undefined;
|
||||
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
|
||||
assertEq(obj2.name, "obj2");
|
||||
assertEq(wm1.get(obj2).name, "obj3");
|
||||
assertEq(wm1.get(wm1.get(obj2)).name, "obj1");
|
||||
print(nondeterministicGetWeakMapKeys(wm1).map(o => o.name).join(","));
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 2);
|
||||
}
|
||||
|
||||
weakGraph();
|
||||
|
||||
// ...but the weakmap itself has to stay alive, too.
|
||||
function deadWeakMap() {
|
||||
var wm1 = new WeakMap();
|
||||
var obj1 = makeFinalizeObserver();
|
||||
var obj2 = {'name': 'obj2'};
|
||||
var obj3 = {'name': 'obj3'};
|
||||
var obj4 = {'name': 'obj4'};
|
||||
var clear = {'name': ''}; // Make the interpreter forget about the last obj created
|
||||
|
||||
wm1.set(obj2, obj3);
|
||||
wm1.set(obj3, obj1);
|
||||
wm1.set(obj4, obj1); // This edge will be cleared
|
||||
var initialCount = finalizeCount();
|
||||
obj1 = obj3 = obj4 = undefined;
|
||||
wm1 = undefined;
|
||||
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
|
||||
assertEq(obj2.name, "obj2");
|
||||
assertEq(finalizeCount(), initialCount + 1);
|
||||
}
|
||||
|
||||
deadWeakMap();
|
||||
|
||||
// WeakMaps do not strongly reference their keys or values. (WeakMaps hold a
|
||||
// collection of (strong) references to *edges* from keys to values. If the
|
||||
// WeakMap is not live, then its edges are of course not live either. An edge
|
||||
// holds neither its key nor its value live; it just holds a strong ref from
|
||||
// the key to the value. So if the key is live, the value is live too, but the
|
||||
// edge itself has no references to anything.)
|
||||
function deadKeys() {
|
||||
var wm1 = new WeakMap();
|
||||
var obj1 = makeFinalizeObserver();
|
||||
var obj2 = {'name': 'obj2'};
|
||||
var obj3 = makeFinalizeObserver();
|
||||
var clear = {}; // Make the interpreter forget about the last obj created
|
||||
|
||||
wm1.set(obj1, obj1);
|
||||
wm1.set(obj3, obj2);
|
||||
obj1 = obj3 = undefined;
|
||||
var initialCount = finalizeCount();
|
||||
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
|
||||
assertEq(finalizeCount(), initialCount + 2);
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 0);
|
||||
}
|
||||
|
||||
deadKeys();
|
||||
|
||||
// The weakKeys table has to grow if it encounters enough new unmarked weakmap
|
||||
// keys. Trigger this to happen during weakmap marking.
|
||||
//
|
||||
// There's some trickiness involved in getting it to test the right thing,
|
||||
// because if a key is marked before the weakmap, then it won't get entered
|
||||
// into the weakKeys table. This chains through multiple weakmap layers to
|
||||
// ensure that the objects can't get marked before the weakmaps.
|
||||
function weakKeysRealloc() {
|
||||
var wm1 = new WeakMap;
|
||||
var wm2 = new WeakMap;
|
||||
var wm3 = new WeakMap;
|
||||
var obj1 = {'name': 'obj1'};
|
||||
var obj2 = {'name': 'obj2'};
|
||||
wm1.set(obj1, wm2);
|
||||
wm2.set(obj2, wm3);
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
wm3.set(Object.create(null), wm2);
|
||||
}
|
||||
wm3.set(Object.create(null), makeFinalizeObserver());
|
||||
wm2 = undefined;
|
||||
wm3 = undefined;
|
||||
obj2 = undefined;
|
||||
|
||||
var initialCount = finalizeCount();
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
assertEq(finalizeCount(), initialCount + 1);
|
||||
}
|
||||
|
||||
weakKeysRealloc();
|
||||
|
||||
// The weakKeys table is populated during regular marking. When a key is later
|
||||
// deleted, both it and its delegate should be removed from weakKeys.
|
||||
// Otherwise, it will hold its value live if it gets marked, and our table
|
||||
// traversals will include non-keys, etc.
|
||||
function deletedKeys() {
|
||||
var wm = new WeakMap;
|
||||
var g = newGlobal();
|
||||
|
||||
for (var i = 0; i < 1000; i++)
|
||||
wm.set(g.Object.create(null), i);
|
||||
|
||||
startgc(100, 'shrinking');
|
||||
for (var key of nondeterministicGetWeakMapKeys(wm)) {
|
||||
if (wm.get(key) % 2)
|
||||
wm.delete(key);
|
||||
}
|
||||
|
||||
gc();
|
||||
}
|
||||
|
||||
deletedKeys();
|
||||
|
||||
// Test adding keys during incremental GC.
|
||||
function incrementalAdds() {
|
||||
var initialCount = finalizeCount();
|
||||
|
||||
var wm1 = new WeakMap;
|
||||
var wm2 = new WeakMap;
|
||||
var wm3 = new WeakMap;
|
||||
var obj1 = {'name': 'obj1'};
|
||||
var obj2 = {'name': 'obj2'};
|
||||
wm1.set(obj1, wm2);
|
||||
wm2.set(obj2, wm3);
|
||||
for (var i = 0; i < 10000; i++) {
|
||||
wm3.set(Object.create(null), wm2);
|
||||
}
|
||||
wm3.set(Object.create(null), makeFinalizeObserver());
|
||||
obj2 = undefined;
|
||||
|
||||
var obj3 = [];
|
||||
startgc(100, 'shrinking');
|
||||
var M = 10;
|
||||
var N = 800;
|
||||
for (var j = 0; j < M; j++) {
|
||||
for (var i = 0; i < N; i++)
|
||||
wm3.set(Object.create(null), makeFinalizeObserver()); // Should be swept
|
||||
for (var i = 0; i < N; i++) {
|
||||
obj3.push({'name': 'obj3'});
|
||||
wm1.set(obj3[obj3.length - 1], makeFinalizeObserver()); // Should not be swept
|
||||
}
|
||||
gcslice();
|
||||
}
|
||||
|
||||
wm2 = undefined;
|
||||
wm3 = undefined;
|
||||
|
||||
gc();
|
||||
print("initialCount = " + initialCount);
|
||||
assertEq(finalizeCount(), initialCount + 1 + M * N);
|
||||
}
|
||||
|
||||
incrementalAdds();
|
||||
@@ -0,0 +1,128 @@
|
||||
// These tests will be using object literals as keys, and we want some of them
|
||||
// to be dead after being inserted into a WeakMap. That means we must wrap
|
||||
// everything in functions because it seems like the toplevel script hangs onto
|
||||
// its object literals.
|
||||
|
||||
// Cross-compartment WeakMap keys work by storing a cross-compartment wrapper
|
||||
// in the WeakMap, and the actual "delegate" object in the target compartment
|
||||
// is the thing whose liveness is checked.
|
||||
|
||||
var g2 = newGlobal();
|
||||
g2.eval('function genObj(name) { return {"name": name} }');
|
||||
|
||||
function basicSweeping() {
|
||||
var wm1 = new WeakMap();
|
||||
wm1.set({'name': 'obj1'}, {'name': 'val1'});
|
||||
var hold = g2.genObj('obj2');
|
||||
wm1.set(hold, {'name': 'val2'});
|
||||
wm1.set({'name': 'obj3'}, {'name': 'val3'});
|
||||
var obj4 = g2.genObj('obj4');
|
||||
wm1.set(obj4, {'name': 'val3'});
|
||||
obj4 = undefined;
|
||||
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
assertEq(wm1.get(hold).name, 'val2');
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
|
||||
}
|
||||
|
||||
basicSweeping();
|
||||
|
||||
// Same, but behind an additional WM layer, to avoid ordering problems (not
|
||||
// that I've checked that basicSweeping even has any problems.)
|
||||
|
||||
function basicSweeping2() {
|
||||
var wm1 = new WeakMap();
|
||||
wm1.set({'name': 'obj1'}, {'name': 'val1'});
|
||||
var hold = g2.genObj('obj2');
|
||||
wm1.set(hold, {'name': 'val2'});
|
||||
wm1.set({'name': 'obj3'}, {'name': 'val3'});
|
||||
var obj4 = g2.genObj('obj4');
|
||||
wm1.set(obj4, {'name': 'val3'});
|
||||
obj4 = undefined;
|
||||
|
||||
var base1 = {'name': 'base1'};
|
||||
var base2 = {'name': 'base2'};
|
||||
var wm_base1 = new WeakMap();
|
||||
var wm_base2 = new WeakMap();
|
||||
wm_base1.set(base1, wm_base2);
|
||||
wm_base2.set(base2, wm1);
|
||||
wm1 = wm_base2 = undefined;
|
||||
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm_base1).length, 1);
|
||||
wm_base2 = wm_base1.get(base1);
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm_base2).length, 1);
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm_base1)[0], base1);
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm_base2)[0], base2);
|
||||
wm_base2 = wm_base1.get(base1);
|
||||
wm1 = wm_base2.get(base2);
|
||||
assertEq(wm1.get(hold).name, 'val2');
|
||||
assertEq(nondeterministicGetWeakMapKeys(wm1).length, 1);
|
||||
}
|
||||
|
||||
basicSweeping2();
|
||||
|
||||
// Scatter the weakmap, the keys, and the values among different compartments.
|
||||
|
||||
function tripleZoneMarking() {
|
||||
var g1 = newGlobal();
|
||||
var g2 = newGlobal();
|
||||
var g3 = newGlobal();
|
||||
|
||||
var wm = g1.eval("new WeakMap()");
|
||||
var key = g2.eval("({'name': 'obj1'})");
|
||||
var value = g3.eval("({'name': 'val1'})");
|
||||
g1 = g2 = g3 = undefined;
|
||||
wm.set(key, value);
|
||||
|
||||
// Make all of it only reachable via a weakmap in the main test compartment,
|
||||
// so that all of this happens during weak marking mode. Use the weakmap as
|
||||
// its own key, so we know that the weakmap will get traced before the key
|
||||
// and therefore will populate the weakKeys table and all of that jazz.
|
||||
var base_wm = new WeakMap();
|
||||
base_wm.set(base_wm, [ wm, key ]);
|
||||
|
||||
wm = key = value = undefined;
|
||||
|
||||
startgc(100000, 'shrinking');
|
||||
gcslice();
|
||||
|
||||
var keys = nondeterministicGetWeakMapKeys(base_wm);
|
||||
assertEq(keys.length, 1);
|
||||
var [ wm, key ] = base_wm.get(keys[0]);
|
||||
assertEq(key.name, "obj1");
|
||||
value = wm.get(key);
|
||||
assertEq(value.name, "val1");
|
||||
}
|
||||
|
||||
tripleZoneMarking();
|
||||
|
||||
function enbugger() {
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger;
|
||||
g.eval("function debuggee_f() { return 1; }");
|
||||
g.eval("function debuggee_g() { return 1; }");
|
||||
dbg.addDebuggee(g);
|
||||
var [ s ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_f");
|
||||
var [ s2 ] = dbg.findScripts({global: g}).filter(s => s.displayName == "debuggee_g");
|
||||
g.eval("debuggee_f = null");
|
||||
gc();
|
||||
dbg.removeAllDebuggees();
|
||||
gc();
|
||||
assertEq(s.displayName, "debuggee_f");
|
||||
|
||||
var wm = new WeakMap;
|
||||
var obj = Object.create(null);
|
||||
var obj2 = Object.create(null);
|
||||
wm.set(obj, s);
|
||||
wm.set(obj2, obj);
|
||||
wm.set(s2, obj2);
|
||||
s = s2 = obj = obj2 = null;
|
||||
|
||||
gc();
|
||||
}
|
||||
|
||||
enbugger();
|
||||
@@ -806,17 +806,12 @@ BaselineCompiler::emitDebugTrap()
|
||||
void
|
||||
BaselineCompiler::emitCoverage(jsbytecode* pc)
|
||||
{
|
||||
double* counterAddr = &script->getPCCounts(pc).numExec();
|
||||
AllocatableRegisterSet regs(RegisterSet::Volatile());
|
||||
Register counterAddrReg = R2.scratchReg();
|
||||
FloatRegister counter = regs.takeAnyFloat();
|
||||
FloatRegister one = regs.takeAnyFloat();
|
||||
PCCounts* counts = script->maybeGetPCCounts(pc);
|
||||
if (!counts)
|
||||
return;
|
||||
|
||||
masm.movePtr(ImmPtr(counterAddr), counterAddrReg);
|
||||
masm.loadDouble(Address(counterAddrReg, 0), counter);
|
||||
masm.loadConstantDouble(1.0, one);
|
||||
masm.addDouble(one, counter);
|
||||
masm.storeDouble(counter, Address(counterAddrReg, 0));
|
||||
uint64_t* counterAddr = &counts->numExec();
|
||||
masm.inc64(AbsoluteAddress(counterAddr));
|
||||
}
|
||||
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
|
||||
@@ -10269,5 +10269,128 @@ CodeGenerator::visitNewTarget(LNewTarget *ins)
|
||||
masm.bind(&done);
|
||||
}
|
||||
|
||||
// Out-of-line math_random_no_outparam call for LRandom.
|
||||
class OutOfLineRandom : public OutOfLineCodeBase<CodeGenerator>
|
||||
{
|
||||
LRandom* lir_;
|
||||
|
||||
public:
|
||||
explicit OutOfLineRandom(LRandom* lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
void accept(CodeGenerator* codegen) {
|
||||
codegen->visitOutOfLineRandom(this);
|
||||
}
|
||||
|
||||
LRandom* lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
|
||||
static const uint64_t RNG_HIGH_MASK = (0xFFFFFFFFFFFFFFFFULL >>
|
||||
(RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
|
||||
static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
|
||||
|
||||
void
|
||||
CodeGenerator::visitRandom(LRandom* ins)
|
||||
{
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
Register JSCompartmentReg = ToRegister(ins->temp1());
|
||||
#ifdef JS_PUNBOX64
|
||||
Register64 rngStateReg = Register64(ToRegister(ins->tempMaybeEAX()));
|
||||
Register64 highReg = Register64(ToRegister(ins->tempMaybeEDX()));
|
||||
#else
|
||||
Register64 rngStateReg = Register64(ToRegister(ins->temp2()), ToRegister(ins->temp3()));
|
||||
Register64 highReg = Register64(ToRegister(ins->tempMaybeEAX()), ToRegister(ins->tempMaybeEDX()));
|
||||
#endif
|
||||
// tempReg is used only on x86.
|
||||
Register tempReg = ToRegister(ins->tempMaybeEAX());
|
||||
|
||||
// rngState = cx->compartment()->rngState;
|
||||
masm.loadJSContext(JSCompartmentReg);
|
||||
masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
|
||||
masm.load64(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
|
||||
addOutOfLineCode(ool, ins->mir());
|
||||
masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
|
||||
|
||||
// rngState = rngState * RNG_MULTIPLIER;
|
||||
masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
|
||||
|
||||
// rngState += RNG_ADDEND;
|
||||
masm.add64(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// rngState &= RNG_MASK;
|
||||
masm.and64(Imm64(RNG_MASK), rngStateReg);
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
|
||||
|
||||
// high = (rngState >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
|
||||
masm.move64(rngStateReg, highReg);
|
||||
masm.lshift64(Imm32(RNG_LOW_BITS - (RNG_STATE_WIDTH - RNG_HIGH_BITS)), highReg);
|
||||
masm.and64(Imm64(RNG_HIGH_MASK), highReg);
|
||||
#ifdef JS_CODEGEN_X86
|
||||
// eax and edx are overwritten by mul64 on x86.
|
||||
masm.push64(highReg);
|
||||
#endif
|
||||
|
||||
// rngState = rngState * RNG_MULTIPLIER;
|
||||
masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
|
||||
|
||||
// rngState += RNG_ADDEND;
|
||||
masm.add64(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// rngState &= RNG_MASK;
|
||||
masm.and64(Imm64(RNG_MASK), rngStateReg);
|
||||
|
||||
// cx->compartment()->rngState = rngState;
|
||||
masm.store64(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
|
||||
|
||||
// low = rngState >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
|
||||
const Register64& lowReg = rngStateReg;
|
||||
masm.rshift64(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
|
||||
|
||||
// output = double(high | low);
|
||||
#ifdef JS_CODEGEN_X86
|
||||
masm.pop64(highReg);
|
||||
#endif
|
||||
masm.or64(highReg, lowReg);
|
||||
masm.convertUInt64ToDouble(lowReg, tempReg, output);
|
||||
|
||||
// output = output * RNG_DSCALE_INV;
|
||||
masm.mulDoublePtr(ImmPtr(&RNG_DSCALE_INV), tempReg, output);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitOutOfLineRandom(OutOfLineRandom* ool)
|
||||
{
|
||||
LRandom* ins = ool->lir();
|
||||
Register temp1 = ToRegister(ins->tempMaybeEAX());
|
||||
Register temp2 = ToRegister(ins->tempMaybeEDX());
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
|
||||
LiveRegisterSet regs;
|
||||
setReturnDoubleRegs(®s);
|
||||
saveVolatile(regs);
|
||||
|
||||
masm.loadJSContext(temp1);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp1);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
restoreVolatile(regs);
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
@@ -45,6 +45,7 @@ class OutOfLineIsCallable;
|
||||
class OutOfLineRegExpExec;
|
||||
class OutOfLineRegExpTest;
|
||||
class OutOfLineLambdaArrow;
|
||||
class OutOfLineRandom;
|
||||
|
||||
class CodeGenerator : public CodeGeneratorSpecific
|
||||
{
|
||||
@@ -381,6 +382,9 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
|
||||
void visitRecompileCheck(LRecompileCheck* ins);
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void visitOutOfLineRandom(OutOfLineRandom* ool);
|
||||
|
||||
IonScriptCounts* extractScriptCounts() {
|
||||
IonScriptCounts* counts = scriptCounts_;
|
||||
scriptCounts_ = nullptr; // prevent delete in dtor
|
||||
|
||||
@@ -692,6 +692,17 @@ HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFrom
|
||||
{
|
||||
MOZ_ASSERT(frame.isBaselineJS());
|
||||
|
||||
bool frameOk = false;
|
||||
RootedScript script(cx, frame.baselineFrame()->script());
|
||||
|
||||
if (script->hasScriptCounts()) {
|
||||
PCCounts* counts = script->getThrowCounts(pc);
|
||||
// If we failed to allocate, then skip the increment and continue to
|
||||
// handle the exception.
|
||||
if (counts)
|
||||
counts->numExec()++;
|
||||
}
|
||||
|
||||
// We may be propagating a forced return from the interrupt
|
||||
// callback, which cannot easily force a return.
|
||||
if (cx->isPropagatingForcedReturn()) {
|
||||
@@ -700,10 +711,7 @@ HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFrom
|
||||
return;
|
||||
}
|
||||
|
||||
bool frameOk = false;
|
||||
RootedScript script(cx, frame.baselineFrame()->script());
|
||||
|
||||
again:
|
||||
again:
|
||||
if (cx->isExceptionPending()) {
|
||||
if (!cx->isClosingGenerator()) {
|
||||
switch (Debugger::onExceptionUnwind(cx, frame.baselineFrame())) {
|
||||
@@ -732,8 +740,12 @@ again:
|
||||
ScopeIter si(cx, frame.baselineFrame(), pc);
|
||||
if (!ProcessTryNotesBaseline(cx, frame, si, rfe, &pc))
|
||||
goto again;
|
||||
if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
|
||||
if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) {
|
||||
// No need to increment the PCCounts number of execution here,
|
||||
// as the interpreter increments any PCCounts if present.
|
||||
MOZ_ASSERT_IF(script->hasScriptCounts(), script->maybeGetPCCounts(pc));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
frameOk = HandleClosingGeneratorReturn(cx, frame.baselineFrame(), frameOk);
|
||||
|
||||
@@ -680,6 +680,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
inline void andPtr(Register src, Register dest) PER_ARCH;
|
||||
inline void andPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
inline void and64(Imm64 imm, Register64 dest) PER_ARCH;
|
||||
|
||||
inline void or32(Register src, Register dest) PER_SHARED_ARCH;
|
||||
inline void or32(Imm32 imm, Register dest) PER_SHARED_ARCH;
|
||||
inline void or32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
|
||||
@@ -687,6 +689,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||
inline void orPtr(Register src, Register dest) PER_ARCH;
|
||||
inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
|
||||
|
||||
inline void or64(Register64 src, Register64 dest) PER_ARCH;
|
||||
|
||||
inline void xor32(Register src, Register dest) DEFINED_ON(x86_shared);
|
||||
inline void xor32(Imm32 imm, Register dest) PER_SHARED_ARCH;
|
||||
|
||||
|
||||
@@ -93,6 +93,26 @@ struct Register {
|
||||
}
|
||||
};
|
||||
|
||||
struct Register64
|
||||
{
|
||||
#ifdef JS_PUNBOX64
|
||||
Register reg;
|
||||
#else
|
||||
Register high;
|
||||
Register low;
|
||||
#endif
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
explicit MOZ_CONSTEXPR Register64(Register r)
|
||||
: reg(r)
|
||||
{}
|
||||
#else
|
||||
MOZ_CONSTEXPR Register64(Register h, Register l)
|
||||
: high(h), low(l)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
||||
class RegisterDump
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "jit/CodeGenerator.h"
|
||||
@@ -2656,16 +2655,12 @@ CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier* ins)
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM::visitRandom(LRandom* ins)
|
||||
CodeGeneratorARM::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
Register temp = ToRegister(ins->temp());
|
||||
Register temp2 = ToRegister(ins->temp2());
|
||||
|
||||
masm.loadJSContext(temp);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
MOZ_ASSERT(ReturnFloat32Reg.code_ == FloatRegisters::s0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.code_ == FloatRegisters::s0);
|
||||
FloatRegister s1 = {FloatRegisters::s1, VFPRegister::Single};
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(s1);
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
||||
|
||||
void generateInvalidateEpilogue();
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
|
||||
// Generating a result.
|
||||
template<typename S, typename T>
|
||||
|
||||
@@ -497,23 +497,6 @@ class LAsmJSAtomicBinopCallout : public LCallInstructionHelper<1, 2, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LCallInstructionHelper<1, 0, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition& temp, const LDefinition& temp2) {
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -741,6 +741,10 @@ LIRGeneratorARM::visitSubstr(MSubstr* ins)
|
||||
void
|
||||
LIRGeneratorARM::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
|
||||
defineReturn(lir, ins);
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
||||
@@ -63,6 +63,13 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
ma_and(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
and32(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
|
||||
and32(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Register src, Register dest)
|
||||
{
|
||||
@@ -96,6 +103,13 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
ma_orr(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
or32(src.low, dest.low);
|
||||
or32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
|
||||
@@ -90,6 +90,19 @@ MacroAssemblerARM::convertUInt32ToDouble(Register src, FloatRegister dest_)
|
||||
as_vcvt(dest, dest.uintOverlay());
|
||||
}
|
||||
|
||||
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
|
||||
{
|
||||
convertUInt32ToDouble(src.high, dest);
|
||||
movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), ScratchRegister);
|
||||
loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
convertUInt32ToDouble(src.low, ScratchDoubleReg);
|
||||
addDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARM::convertUInt32ToFloat32(Register src, FloatRegister dest_)
|
||||
{
|
||||
@@ -3362,6 +3375,22 @@ template void
|
||||
MacroAssemblerARMCompat::storeUnboxedValue(ConstantOrRegister value, MIRType valueType, const BaseIndex& dest,
|
||||
MIRType slotType);
|
||||
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
|
||||
Register temp, Label* label)
|
||||
{
|
||||
if (cond == Assembler::Zero) {
|
||||
MOZ_ASSERT(lhs.low == rhs.low);
|
||||
MOZ_ASSERT(lhs.high == rhs.high);
|
||||
mov(lhs.low, ScratchRegister);
|
||||
asMasm().or32(lhs.high, ScratchRegister);
|
||||
branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported condition");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::moveValue(const Value& val, Register type, Register data)
|
||||
{
|
||||
|
||||
@@ -987,6 +987,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
subPtr(imm, lhs);
|
||||
branch32(cond, lhs, Imm32(0), label);
|
||||
}
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label);
|
||||
void moveValue(const Value& val, Register type, Register data);
|
||||
|
||||
CodeOffsetJump jumpWithPatch(RepatchLabel* label, Condition cond = Always,
|
||||
@@ -1203,6 +1204,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
|
||||
void addPtr(Register src, Register dest);
|
||||
void addPtr(const Address& src, Register dest);
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
ma_add(imm, dest.low, SetCC);
|
||||
ma_adc(Imm32(0), dest.high, LeaveCC);
|
||||
}
|
||||
void not32(Register reg);
|
||||
|
||||
void move32(Imm32 imm, Register dest);
|
||||
void move32(Register src, Register dest);
|
||||
@@ -1212,6 +1218,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void movePtr(ImmPtr imm, Register dest);
|
||||
void movePtr(AsmJSImmPtr imm, Register dest);
|
||||
void movePtr(ImmGCPtr imm, Register dest);
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
move32(src.low, dest.low);
|
||||
move32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void load8SignExtend(const Address& address, Register dest);
|
||||
void load8SignExtend(const BaseIndex& src, Register dest);
|
||||
@@ -1228,6 +1238,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void load32(const Address& address, Register dest);
|
||||
void load32(const BaseIndex& address, Register dest);
|
||||
void load32(AbsoluteAddress address, Register dest);
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
load32(address, dest.low);
|
||||
load32(Address(address.base, address.offset + 4), dest.high);
|
||||
}
|
||||
|
||||
void loadPtr(const Address& address, Register dest);
|
||||
void loadPtr(const BaseIndex& src, Register dest);
|
||||
@@ -1294,6 +1308,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
|
||||
void store32_NoSecondScratch(Imm32 src, const Address& address);
|
||||
|
||||
void store64(Register64 src, Address address) {
|
||||
store32(src.low, address);
|
||||
store32(src.high, Address(address.base, address.offset + 4));
|
||||
}
|
||||
|
||||
template <typename T> void storePtr(ImmWord imm, T address);
|
||||
template <typename T> void storePtr(ImmPtr imm, T address);
|
||||
template <typename T> void storePtr(ImmGCPtr imm, T address);
|
||||
@@ -1642,6 +1661,39 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void mulBy3(const Register& src, const Register& dest) {
|
||||
as_add(dest, src, lsl(src, 1));
|
||||
}
|
||||
void mul64(Imm64 imm, const Register64& dest) {
|
||||
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
||||
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
||||
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
||||
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
||||
|
||||
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
||||
ma_mov(Imm32(imm.value & 0xFFFFFFFFL), ScratchRegister);
|
||||
as_mul(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
// high:low = LOW(dest) * LOW(imm);
|
||||
as_umull(secondScratchReg_, ScratchRegister, dest.low, ScratchRegister);
|
||||
|
||||
// HIGH(dest) += high;
|
||||
as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
|
||||
|
||||
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
||||
if (((imm.value >> 32) & 0xFFFFFFFFL) == 5)
|
||||
as_add(secondScratchReg_, dest.low, lsl(dest.low, 2));
|
||||
else
|
||||
MOZ_CRASH("Not supported imm");
|
||||
as_add(dest.high, dest.high, O2Reg(secondScratchReg_));
|
||||
|
||||
// LOW(dest) = low;
|
||||
ma_mov(ScratchRegister, dest.low);
|
||||
}
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
movePtr(imm, ScratchRegister);
|
||||
loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void setStackArg(Register reg, uint32_t arg);
|
||||
|
||||
@@ -1670,9 +1722,19 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
ma_asr(imm, dest, dest);
|
||||
}
|
||||
void rshift64(Imm32 imm, Register64 dest) {
|
||||
as_mov(dest.low, lsr(dest.low, imm.value));
|
||||
as_orr(dest.low, dest.low, lsl(dest.high, 32 - imm.value));
|
||||
as_mov(dest.high, lsr(dest.high, imm.value));
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_lsl(imm, dest, dest);
|
||||
}
|
||||
void lshift64(Imm32 imm, Register64 dest) {
|
||||
as_mov(dest.high, lsl(dest.high, imm.value));
|
||||
as_orr(dest.high, dest.high, lsr(dest.low, 32 - imm.value));
|
||||
as_mov(dest.low, lsl(dest.low, imm.value));
|
||||
}
|
||||
|
||||
// If source is a double, load it into dest. If source is int32, convert it
|
||||
// to double. Else, branch to failure.
|
||||
|
||||
@@ -608,12 +608,6 @@ CodeGeneratorARM64::generateInvalidateEpilogue()
|
||||
MOZ_CRASH("generateInvalidateEpilogue");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::visitRandom(LRandom* ins)
|
||||
{
|
||||
MOZ_CRASH("visitRandom");
|
||||
}
|
||||
|
||||
template <class U>
|
||||
Register
|
||||
getBase(U* mir)
|
||||
@@ -732,3 +726,14 @@ CodeGeneratorARM64::visitNegF(LNegF* ins)
|
||||
{
|
||||
MOZ_CRASH("visitNegF");
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorARM64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
MOZ_ASSERT(ReturnFloat32Reg.code_ == FloatRegisters::s0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.code_ == FloatRegisters::d0);
|
||||
FloatRegister s1 = {FloatRegisters::s1, FloatRegisters::Single};
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(s1);
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
|
||||
|
||||
void generateInvalidateEpilogue();
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
|
||||
protected:
|
||||
void postAsmJSCall(LAsmJSCall* lir) {
|
||||
|
||||
@@ -407,23 +407,6 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LCallInstructionHelper<1, 0, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition& temp, const LDefinition& temp2) {
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -304,5 +304,8 @@ LIRGeneratorARM64::visitSubstr(MSubstr* ins)
|
||||
void
|
||||
LIRGeneratorARM64::visitRandom(MRandom* ins)
|
||||
{
|
||||
MOZ_CRASH("visitRandom");
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
||||
@@ -73,6 +73,15 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
And(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const Register scratch = temps.AcquireX().asUnsized();
|
||||
mov(ImmWord(imm.value), scratch);
|
||||
andPtr(scratch, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Imm32 imm, Register dest)
|
||||
{
|
||||
@@ -108,6 +117,12 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
Orr(ARMRegister(dest, 64), ARMRegister(dest, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
orPtr(src.reg, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
|
||||
@@ -784,6 +784,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
BufferOffset load = movePatchablePtr(ImmPtr(imm.value), dest);
|
||||
writeDataRelocation(imm, load);
|
||||
}
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
movePtr(src.reg, dest.reg);
|
||||
}
|
||||
|
||||
void mov(ImmWord imm, Register dest) {
|
||||
movePtr(imm, dest);
|
||||
@@ -989,6 +992,10 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Str(scratch32, MemOperand(ARMRegister(address.base, 64), address.offset));
|
||||
}
|
||||
|
||||
void store64(Register64 src, Address address) {
|
||||
storePtr(src.reg, address);
|
||||
}
|
||||
|
||||
// SIMD.
|
||||
void loadInt32x1(const Address& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
||||
void loadInt32x1(const BaseIndex& addr, FloatRegister dest) { MOZ_CRASH("NYI"); }
|
||||
@@ -1068,6 +1075,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
void rshiftPtr(Imm32 imm, Register src, Register dest) {
|
||||
Lsr(ARMRegister(dest, 64), ARMRegister(src, 64), imm.value);
|
||||
}
|
||||
void rshift64(Imm32 imm, Register64 dest) {
|
||||
rshiftPtr(imm, dest.reg);
|
||||
}
|
||||
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
Asr(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
@@ -1075,6 +1085,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
Lsl(ARMRegister(dest, 64), ARMRegister(dest, 64), imm.value);
|
||||
}
|
||||
void lshift64(Imm32 imm, Register64 dest) {
|
||||
lshiftPtr(imm, dest.reg);
|
||||
}
|
||||
|
||||
void testPtr(Register lhs, Register rhs) {
|
||||
Tst(ARMRegister(lhs, 64), Operand(ARMRegister(rhs, 64)));
|
||||
@@ -1162,7 +1175,7 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
}
|
||||
|
||||
void loadDouble(const Address& src, FloatRegister dest) {
|
||||
Ldr(ARMFPRegister(dest, 64), MemOperand(ARMRegister(src.base,64), src.offset));
|
||||
Ldr(ARMFPRegister(dest, 64), MemOperand(src));
|
||||
}
|
||||
void loadDouble(const BaseIndex& src, FloatRegister dest) {
|
||||
ARMRegister base(src.base, 64);
|
||||
@@ -1306,6 +1319,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
movePtr(ImmWord((uintptr_t)address.addr), scratch64.asUnsized());
|
||||
ldr(ARMRegister(dest, 32), MemOperand(scratch64));
|
||||
}
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
loadPtr(address, dest.reg);
|
||||
}
|
||||
|
||||
void load8SignExtend(const Address& address, Register dest) {
|
||||
Ldrsb(ARMRegister(dest, 32), MemOperand(ARMRegister(address.base, 64), address.offset));
|
||||
@@ -1366,6 +1382,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Adds(scratch32, scratch32, Operand(imm.value));
|
||||
Str(scratch32, MemOperand(ARMRegister(dest.base, 64), dest.offset));
|
||||
}
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
Add(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), Operand(imm.value));
|
||||
}
|
||||
|
||||
void sub32(Imm32 imm, Register dest) {
|
||||
Sub(ARMRegister(dest, 32), ARMRegister(dest, 32), Operand(imm.value));
|
||||
@@ -1914,6 +1933,9 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Cmp(ARMRegister(value.valueReg(), 64), Operand(scratch64));
|
||||
B(label, cond);
|
||||
}
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
|
||||
branchTestPtr(cond, lhs.reg, rhs.reg, label);
|
||||
}
|
||||
|
||||
void compareDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs) {
|
||||
Fcmp(ARMFPRegister(lhs, 64), ARMFPRegister(rhs, 64));
|
||||
@@ -2961,6 +2983,27 @@ class MacroAssemblerCompat : public vixl::MacroAssembler
|
||||
Add(xdest, xsrc, Operand(xsrc, vixl::LSL, 1));
|
||||
}
|
||||
|
||||
void mul64(Imm64 imm, const Register64& dest) {
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const ARMRegister scratch64 = temps.AcquireX();
|
||||
MOZ_ASSERT(dest.reg != scratch64.asUnsized());
|
||||
mov(ImmWord(imm.value), scratch64.asUnsized());
|
||||
Mul(ARMRegister(dest.reg, 64), ARMRegister(dest.reg, 64), scratch64);
|
||||
}
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
|
||||
Ucvtf(ARMFPRegister(dest, 64), ARMRegister(src.reg, 64));
|
||||
}
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
vixl::UseScratchRegisterScope temps(this);
|
||||
const Register scratch = temps.AcquireX().asUnsized();
|
||||
MOZ_ASSERT(temp != scratch);
|
||||
movePtr(imm, scratch);
|
||||
const ARMFPRegister scratchDouble = temps.AcquireD();
|
||||
Ldr(scratchDouble, MemOperand(Address(scratch, 0)));
|
||||
fmul(ARMFPRegister(dest, 64), ARMFPRegister(dest, 64), scratchDouble);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void branchAdd32(Condition cond, T src, Register dest, Label* label) {
|
||||
adds32(src, dest);
|
||||
|
||||
@@ -284,23 +284,6 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LCallInstructionHelper<1, 0, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition& temp, const LDefinition& temp2) {
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -415,6 +415,10 @@ LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayEleme
|
||||
void
|
||||
LIRGeneratorMIPSShared::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
|
||||
defineReturn(lir, ins);
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jscompartment.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "jit/CodeGenerator.h"
|
||||
@@ -2113,16 +2112,10 @@ CodeGeneratorMIPS::visitNegF(LNegF* ins)
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorMIPS::visitRandom(LRandom* ins)
|
||||
CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
Register temp = ToRegister(ins->temp());
|
||||
Register temp2 = ToRegister(ins->temp2());
|
||||
|
||||
masm.loadJSContext(temp);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
MOZ_ASSERT(ReturnFloat32Reg.code_ == ReturnDoubleReg.code_);
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(ReturnDoubleReg.singleOverlay(1));
|
||||
regs->add(ReturnDoubleReg);
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ class CodeGeneratorMIPS : public CodeGeneratorShared
|
||||
|
||||
void generateInvalidateEpilogue();
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
|
||||
protected:
|
||||
void visitEffectiveAddress(LEffectiveAddress* ins);
|
||||
|
||||
@@ -173,3 +173,14 @@ LIRGeneratorMIPS::lowerTruncateFToInt32(MTruncateToInt32* ins)
|
||||
|
||||
define(new(alloc()) LTruncateFToInt32(useRegister(opd), LDefinition::BogusTemp()), ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGeneratorMIPS::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom *lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
||||
@@ -61,6 +61,13 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
ma_and(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
and32(Imm32(imm.value & LOW_32_MASK), dest.low);
|
||||
and32(Imm32((imm.value >> 32) & LOW_32_MASK), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or32(Register src, Register dest)
|
||||
{
|
||||
@@ -93,6 +100,13 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
ma_or(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
or32(src.low, dest.low);
|
||||
or32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xor32(Imm32 imm, Register dest)
|
||||
{
|
||||
|
||||
@@ -47,16 +47,15 @@ MacroAssemblerMIPSCompat::convertInt32ToDouble(Register src, FloatRegister dest)
|
||||
void
|
||||
MacroAssemblerMIPSCompat::convertInt32ToDouble(const Address& src, FloatRegister dest)
|
||||
{
|
||||
ma_lw(ScratchRegister, src);
|
||||
as_mtc1(ScratchRegister, dest);
|
||||
ma_ls(dest, src);
|
||||
as_cvtdw(dest, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::convertInt32ToDouble(const BaseIndex& src, FloatRegister dest)
|
||||
{
|
||||
computeScaledAddress(src, SecondScratchReg);
|
||||
convertInt32ToDouble(Address(SecondScratchReg, src.offset), dest);
|
||||
computeScaledAddress(src, ScratchRegister);
|
||||
convertInt32ToDouble(Address(ScratchRegister, src.offset), dest);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -78,6 +77,63 @@ MacroAssemblerMIPSCompat::convertUInt32ToDouble(Register src, FloatRegister dest
|
||||
as_addd(dest, dest, SecondScratchDoubleReg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::mul64(Imm64 imm, const Register64& dest)
|
||||
{
|
||||
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
||||
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
||||
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
||||
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
||||
|
||||
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
||||
ma_li(ScratchRegister, Imm32(imm.value & LOW_32_MASK));
|
||||
as_multu(dest.high, ScratchRegister);
|
||||
as_mflo(dest.high);
|
||||
|
||||
// mfhi:mflo = LOW(dest) * LOW(imm);
|
||||
as_multu(dest.low, ScratchRegister);
|
||||
|
||||
// HIGH(dest) += mfhi;
|
||||
as_mfhi(ScratchRegister);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
if (((imm.value >> 32) & LOW_32_MASK) == 5) {
|
||||
// Optimized case for Math.random().
|
||||
|
||||
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
||||
as_sll(ScratchRegister, dest.low, 2);
|
||||
as_addu(ScratchRegister, ScratchRegister, dest.low);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
// LOW(dest) = mflo;
|
||||
as_mflo(dest.low);
|
||||
} else {
|
||||
// tmp = mflo
|
||||
as_mflo(SecondScratchReg);
|
||||
|
||||
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
||||
ma_li(ScratchRegister, Imm32((imm.value >> 32) & LOW_32_MASK));
|
||||
as_multu(dest.low, ScratchRegister);
|
||||
as_mflo(ScratchRegister);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
|
||||
// LOW(dest) = tmp;
|
||||
ma_move(dest.low, SecondScratchReg);
|
||||
}
|
||||
}
|
||||
|
||||
static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
|
||||
{
|
||||
convertUInt32ToDouble(src.high, dest);
|
||||
loadConstantDouble(TO_DOUBLE_HIGH_SCALE, ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
convertUInt32ToDouble(src.low, ScratchDoubleReg);
|
||||
addDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::convertUInt32ToFloat32(Register src, FloatRegister dest)
|
||||
{
|
||||
@@ -203,8 +259,7 @@ MacroAssemblerMIPSCompat::convertInt32ToFloat32(Register src, FloatRegister dest
|
||||
void
|
||||
MacroAssemblerMIPSCompat::convertInt32ToFloat32(const Address& src, FloatRegister dest)
|
||||
{
|
||||
ma_lw(ScratchRegister, src);
|
||||
as_mtc1(ScratchRegister, dest);
|
||||
ma_ls(dest, src);
|
||||
as_cvtsw(dest, dest);
|
||||
}
|
||||
|
||||
@@ -266,7 +321,7 @@ void
|
||||
MacroAssemblerMIPS::ma_li(Register dest, ImmGCPtr ptr)
|
||||
{
|
||||
writeDataRelocation(ptr);
|
||||
ma_liPatchable(dest, Imm32(uintptr_t(ptr.value)));
|
||||
ma_liPatchable(dest, ImmPtr(ptr.value));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -295,6 +350,11 @@ MacroAssemblerMIPS::ma_li(Register dest, Imm32 imm)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_li(Register dest, ImmWord imm)
|
||||
{
|
||||
ma_li(dest, Imm32(uint32_t(imm.value)));
|
||||
}
|
||||
|
||||
// This method generates lui and ori instruction pair that can be modified by
|
||||
// UpdateLuiOriValue, either during compilation (eg. Assembler::bind), or
|
||||
@@ -310,7 +370,13 @@ MacroAssemblerMIPS::ma_liPatchable(Register dest, Imm32 imm)
|
||||
void
|
||||
MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmPtr imm)
|
||||
{
|
||||
return ma_liPatchable(dest, Imm32(int32_t(imm.value)));
|
||||
ma_liPatchable(dest, ImmWord(uintptr_t(imm.value)));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPS::ma_liPatchable(Register dest, ImmWord imm)
|
||||
{
|
||||
ma_liPatchable(dest, Imm32(int32_t(imm.value)));
|
||||
}
|
||||
|
||||
// Shifts
|
||||
@@ -1586,7 +1652,7 @@ MacroAssemblerMIPSCompat::movePtr(Register src, Register dest)
|
||||
void
|
||||
MacroAssemblerMIPSCompat::movePtr(ImmWord imm, Register dest)
|
||||
{
|
||||
ma_li(dest, Imm32(imm.value));
|
||||
ma_li(dest, imm);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1604,7 +1670,7 @@ void
|
||||
MacroAssemblerMIPSCompat::movePtr(AsmJSImmPtr imm, Register dest)
|
||||
{
|
||||
append(AsmJSAbsoluteLink(CodeOffsetLabel(nextOffset().getOffset()), imm.kind()));
|
||||
ma_liPatchable(dest, Imm32(-1));
|
||||
ma_liPatchable(dest, ImmWord(-1));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1658,7 +1724,7 @@ MacroAssemblerMIPSCompat::load16SignExtend(const BaseIndex& src, Register dest)
|
||||
void
|
||||
MacroAssemblerMIPSCompat::load32(const Address& address, Register dest)
|
||||
{
|
||||
ma_lw(dest, address);
|
||||
ma_load(dest, address, SizeWord);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1670,33 +1736,41 @@ MacroAssemblerMIPSCompat::load32(const BaseIndex& address, Register dest)
|
||||
void
|
||||
MacroAssemblerMIPSCompat::load32(AbsoluteAddress address, Register dest)
|
||||
{
|
||||
ma_li(ScratchRegister, Imm32((uint32_t)address.addr));
|
||||
as_lw(dest, ScratchRegister, 0);
|
||||
movePtr(ImmPtr(address.addr), ScratchRegister);
|
||||
load32(Address(ScratchRegister, 0), dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::load32(AsmJSAbsoluteAddress address, Register dest)
|
||||
{
|
||||
movePtr(AsmJSImmPtr(address.kind()), ScratchRegister);
|
||||
load32(Address(ScratchRegister, 0), dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadPtr(const Address& address, Register dest)
|
||||
{
|
||||
ma_lw(dest, address);
|
||||
ma_load(dest, address, SizeWord);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadPtr(const BaseIndex& src, Register dest)
|
||||
{
|
||||
load32(src, dest);
|
||||
ma_load(dest, src, SizeWord);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadPtr(AbsoluteAddress address, Register dest)
|
||||
{
|
||||
ma_li(ScratchRegister, Imm32((uint32_t)address.addr));
|
||||
as_lw(dest, ScratchRegister, 0);
|
||||
movePtr(ImmPtr(address.addr), ScratchRegister);
|
||||
loadPtr(Address(ScratchRegister, 0), dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::loadPtr(AsmJSAbsoluteAddress address, Register dest)
|
||||
{
|
||||
movePtr(AsmJSImmPtr(address.kind()), ScratchRegister);
|
||||
loadPtr(Address(ScratchRegister, 0x0), dest);
|
||||
loadPtr(Address(ScratchRegister, 0), dest);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1798,20 +1872,21 @@ MacroAssemblerMIPSCompat::store16(Register src, const BaseIndex& address)
|
||||
void
|
||||
MacroAssemblerMIPSCompat::store32(Register src, AbsoluteAddress address)
|
||||
{
|
||||
storePtr(src, address);
|
||||
movePtr(ImmPtr(address.addr), ScratchRegister);
|
||||
store32(src, Address(ScratchRegister, 0));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::store32(Register src, const Address& address)
|
||||
{
|
||||
storePtr(src, address);
|
||||
ma_store(src, address, SizeWord);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::store32(Imm32 src, const Address& address)
|
||||
{
|
||||
move32(src, SecondScratchReg);
|
||||
storePtr(SecondScratchReg, address);
|
||||
ma_store(SecondScratchReg, address, SizeWord);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1830,8 +1905,8 @@ template <typename T>
|
||||
void
|
||||
MacroAssemblerMIPSCompat::storePtr(ImmWord imm, T address)
|
||||
{
|
||||
ma_li(SecondScratchReg, Imm32(imm.value));
|
||||
ma_sw(SecondScratchReg, address);
|
||||
ma_li(SecondScratchReg, imm);
|
||||
ma_store(SecondScratchReg, address, SizeWord);
|
||||
}
|
||||
|
||||
template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmWord imm, Address address);
|
||||
@@ -1851,8 +1926,7 @@ template <typename T>
|
||||
void
|
||||
MacroAssemblerMIPSCompat::storePtr(ImmGCPtr imm, T address)
|
||||
{
|
||||
ma_li(SecondScratchReg, imm);
|
||||
ma_sw(SecondScratchReg, address);
|
||||
storePtr(ImmWord(uintptr_t(imm.value)), address);
|
||||
}
|
||||
|
||||
template void MacroAssemblerMIPSCompat::storePtr<Address>(ImmGCPtr imm, Address address);
|
||||
@@ -1861,7 +1935,7 @@ template void MacroAssemblerMIPSCompat::storePtr<BaseIndex>(ImmGCPtr imm, BaseIn
|
||||
void
|
||||
MacroAssemblerMIPSCompat::storePtr(Register src, const Address& address)
|
||||
{
|
||||
ma_sw(src, address);
|
||||
ma_store(src, address, SizeWord);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1873,8 +1947,8 @@ MacroAssemblerMIPSCompat::storePtr(Register src, const BaseIndex& address)
|
||||
void
|
||||
MacroAssemblerMIPSCompat::storePtr(Register src, AbsoluteAddress dest)
|
||||
{
|
||||
ma_li(ScratchRegister, Imm32((uint32_t)dest.addr));
|
||||
as_sw(src, ScratchRegister, 0);
|
||||
movePtr(ImmPtr(dest.addr), ScratchRegister);
|
||||
storePtr(src, Address(ScratchRegister, 0));
|
||||
}
|
||||
|
||||
// Note: this function clobbers the input register.
|
||||
@@ -2571,6 +2645,20 @@ MacroAssemblerMIPSCompat::branchTestBooleanTruthy(bool b, const ValueOperand& op
|
||||
ma_b(operand.payloadReg(), operand.payloadReg(), label, b ? NonZero : Zero);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::branchTest64(Condition cond, Register64 lhs, Register64 rhs,
|
||||
Register temp, Label* label)
|
||||
{
|
||||
if (cond == Assembler::Zero) {
|
||||
MOZ_ASSERT(lhs.low == rhs.low);
|
||||
MOZ_ASSERT(lhs.high == rhs.high);
|
||||
as_or(ScratchRegister, lhs.low, lhs.high);
|
||||
branchTestPtr(cond, ScratchRegister, ScratchRegister, label);
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported condition");
|
||||
}
|
||||
}
|
||||
|
||||
Register
|
||||
MacroAssemblerMIPSCompat::extractObject(const Address& address, Register scratch)
|
||||
{
|
||||
@@ -2834,7 +2922,7 @@ void
|
||||
MacroAssemblerMIPSCompat::pushValue(ValueOperand val)
|
||||
{
|
||||
// Allocate stack slots for type and payload. One for each.
|
||||
ma_subu(StackPointer, StackPointer, Imm32(sizeof(Value)));
|
||||
subPtr(Imm32(sizeof(Value)), StackPointer);
|
||||
// Store type and payload.
|
||||
storeValue(val, Address(StackPointer, 0));
|
||||
}
|
||||
@@ -3004,7 +3092,7 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
|
||||
{
|
||||
// Reserve space for exception information.
|
||||
int size = (sizeof(ResumeFromException) + ABIStackAlignment) & ~(ABIStackAlignment - 1);
|
||||
ma_subu(StackPointer, StackPointer, Imm32(size));
|
||||
subPtr(Imm32(size), StackPointer);
|
||||
ma_move(a0, StackPointer); // Use a0 since it is a first function argument
|
||||
|
||||
// Call the handler.
|
||||
@@ -3019,7 +3107,7 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
|
||||
Label bailout;
|
||||
|
||||
// Already clobbered a0, so use it...
|
||||
ma_lw(a0, Address(StackPointer, offsetof(ResumeFromException, kind)));
|
||||
load32(Address(StackPointer, offsetof(ResumeFromException, kind)), a0);
|
||||
branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, a0, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
@@ -3032,7 +3120,7 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
|
||||
// and return from the entry frame.
|
||||
bind(&entryFrame);
|
||||
moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
|
||||
ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer)));
|
||||
loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer);
|
||||
|
||||
// We're going to be returning by the ion calling convention
|
||||
ma_pop(ra);
|
||||
@@ -3042,9 +3130,9 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
|
||||
// If we found a catch handler, this must be a baseline frame. Restore
|
||||
// state and jump to the catch block.
|
||||
bind(&catch_);
|
||||
ma_lw(a0, Address(StackPointer, offsetof(ResumeFromException, target)));
|
||||
ma_lw(BaselineFrameReg, Address(StackPointer, offsetof(ResumeFromException, framePointer)));
|
||||
ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer)));
|
||||
loadPtr(Address(StackPointer, offsetof(ResumeFromException, target)), a0);
|
||||
loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg);
|
||||
loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer);
|
||||
jump(a0);
|
||||
|
||||
// If we found a finally block, this must be a baseline frame. Push
|
||||
@@ -3054,9 +3142,9 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
|
||||
ValueOperand exception = ValueOperand(a1, a2);
|
||||
loadValue(Address(sp, offsetof(ResumeFromException, exception)), exception);
|
||||
|
||||
ma_lw(a0, Address(sp, offsetof(ResumeFromException, target)));
|
||||
ma_lw(BaselineFrameReg, Address(sp, offsetof(ResumeFromException, framePointer)));
|
||||
ma_lw(sp, Address(sp, offsetof(ResumeFromException, stackPointer)));
|
||||
loadPtr(Address(sp, offsetof(ResumeFromException, target)), a0);
|
||||
loadPtr(Address(sp, offsetof(ResumeFromException, framePointer)), BaselineFrameReg);
|
||||
loadPtr(Address(sp, offsetof(ResumeFromException, stackPointer)), sp);
|
||||
|
||||
pushValue(BooleanValue(true));
|
||||
pushValue(exception);
|
||||
@@ -3065,8 +3153,8 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
|
||||
// Only used in debug mode. Return BaselineFrame->returnValue() to the
|
||||
// caller.
|
||||
bind(&return_);
|
||||
ma_lw(BaselineFrameReg, Address(StackPointer, offsetof(ResumeFromException, framePointer)));
|
||||
ma_lw(StackPointer, Address(StackPointer, offsetof(ResumeFromException, stackPointer)));
|
||||
loadPtr(Address(StackPointer, offsetof(ResumeFromException, framePointer)), BaselineFrameReg);
|
||||
loadPtr(Address(StackPointer, offsetof(ResumeFromException, stackPointer)), StackPointer);
|
||||
loadValue(Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue()),
|
||||
JSReturnOperand);
|
||||
ma_move(StackPointer, BaselineFrameReg);
|
||||
@@ -3088,9 +3176,9 @@ MacroAssemblerMIPSCompat::handleFailureWithHandlerTail(void* handler)
|
||||
// If we are bailing out to baseline to handle an exception, jump to
|
||||
// the bailout tail stub.
|
||||
bind(&bailout);
|
||||
ma_lw(a2, Address(sp, offsetof(ResumeFromException, bailoutInfo)));
|
||||
loadPtr(Address(sp, offsetof(ResumeFromException, bailoutInfo)), a2);
|
||||
ma_li(ReturnReg, Imm32(BAILOUT_RETURN_OK));
|
||||
ma_lw(a1, Address(sp, offsetof(ResumeFromException, target)));
|
||||
loadPtr(Address(sp, offsetof(ResumeFromException, target)), a1);
|
||||
jump(a1);
|
||||
}
|
||||
|
||||
@@ -3255,7 +3343,7 @@ MacroAssembler::Push(const Imm32 imm)
|
||||
void
|
||||
MacroAssembler::Push(const ImmWord imm)
|
||||
{
|
||||
ma_li(ScratchRegister, Imm32(imm.value));
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_push(ScratchRegister);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
}
|
||||
@@ -3299,7 +3387,7 @@ void
|
||||
MacroAssembler::reserveStack(uint32_t amount)
|
||||
{
|
||||
if (amount)
|
||||
ma_subu(StackPointer, StackPointer, Imm32(amount));
|
||||
subPtr(Imm32(amount), StackPointer);
|
||||
adjustFrame(amount);
|
||||
}
|
||||
|
||||
@@ -3345,7 +3433,7 @@ MacroAssembler::call(JitCode* c)
|
||||
{
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
|
||||
ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
|
||||
ma_liPatchable(ScratchRegister, ImmPtr(c->raw()));
|
||||
callJitNoProfiler(ScratchRegister);
|
||||
}
|
||||
|
||||
@@ -3353,18 +3441,18 @@ void
|
||||
MacroAssembler::callAndPushReturnAddress(Register callee)
|
||||
{
|
||||
// Push return address during jalr delay slot.
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
|
||||
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
|
||||
as_jalr(callee);
|
||||
as_sw(ra, StackPointer, 0);
|
||||
storePtr(ra, Address(StackPointer, 0));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::callAndPushReturnAddress(Label* label)
|
||||
{
|
||||
// Push return address during bal delay slot.
|
||||
as_addiu(StackPointer, StackPointer, -sizeof(intptr_t));
|
||||
subPtr(Imm32(sizeof(intptr_t)), StackPointer);
|
||||
ma_bal(label, DontFillDelaySlot);
|
||||
as_sw(ra, StackPointer, 0);
|
||||
storePtr(ra, Address(StackPointer, 0));
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
@@ -3379,9 +3467,9 @@ MacroAssembler::setupUnalignedABICall(Register scratch)
|
||||
ma_move(scratch, StackPointer);
|
||||
|
||||
// Force sp to be aligned
|
||||
ma_subu(StackPointer, StackPointer, Imm32(sizeof(uint32_t)));
|
||||
subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
|
||||
ma_and(StackPointer, StackPointer, Imm32(~(ABIStackAlignment - 1)));
|
||||
as_sw(scratch, StackPointer, 0);
|
||||
storePtr(scratch, Address(StackPointer, 0));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -3405,9 +3493,9 @@ MacroAssembler::callWithABIPre(uint32_t* stackAdjust, bool callFromAsmJS)
|
||||
reserveStack(stackForCall);
|
||||
|
||||
// Save $ra because call is going to clobber it. Restore it in
|
||||
// callWithABIPost. NOTE: This is needed for calls from BaselineIC.
|
||||
// callWithABIPost. NOTE: This is needed for calls from SharedIC.
|
||||
// Maybe we can do this differently.
|
||||
ma_sw(ra, Address(StackPointer, stackForCall - sizeof(intptr_t)));
|
||||
storePtr(ra, Address(StackPointer, stackForCall - sizeof(intptr_t)));
|
||||
|
||||
// Position all arguments.
|
||||
{
|
||||
@@ -3427,11 +3515,11 @@ void
|
||||
MacroAssembler::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
|
||||
{
|
||||
// Restore ra value (as stored in callWithABIPre()).
|
||||
ma_lw(ra, Address(StackPointer, stackAdjust - sizeof(intptr_t)));
|
||||
loadPtr(Address(StackPointer, stackAdjust - sizeof(intptr_t)), ra);
|
||||
|
||||
if (dynamicAlignment_) {
|
||||
// Restore sp value from stack (as stored in setupUnalignedABICall()).
|
||||
ma_lw(StackPointer, Address(StackPointer, stackAdjust));
|
||||
loadPtr(Address(StackPointer, stackAdjust), StackPointer);
|
||||
// Use adjustFrame instead of freeStack because we already restored sp.
|
||||
adjustFrame(-stackAdjust);
|
||||
} else {
|
||||
@@ -3461,7 +3549,7 @@ void
|
||||
MacroAssembler::callWithABINoProfiler(const Address& fun, MoveOp::Type result)
|
||||
{
|
||||
// Load the callee in t9, as above.
|
||||
ma_lw(t9, Address(fun.base, fun.offset));
|
||||
loadPtr(Address(fun.base, fun.offset), t9);
|
||||
uint32_t stackAdjust;
|
||||
callWithABIPre(&stackAdjust);
|
||||
call(t9);
|
||||
|
||||
@@ -65,6 +65,10 @@ static Register CallReg = t9;
|
||||
static const int defaultShift = 3;
|
||||
static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
|
||||
|
||||
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
|
||||
{
|
||||
protected:
|
||||
@@ -88,7 +92,9 @@ class MacroAssemblerMIPS : public Assembler
|
||||
|
||||
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);
|
||||
@@ -185,7 +191,7 @@ class MacroAssemblerMIPS : public Assembler
|
||||
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, Imm32(uint32_t(imm.value)), l, c, jumpKind);
|
||||
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);
|
||||
@@ -206,7 +212,7 @@ class MacroAssemblerMIPS : public Assembler
|
||||
void ma_b(Address addr, ImmGCPtr imm, Label* l, Condition c, JumpKind jumpKind = LongJump);
|
||||
void ma_b(Address addr, Register rhs, Label* l, Condition c, JumpKind jumpKind = LongJump) {
|
||||
MOZ_ASSERT(rhs != ScratchRegister);
|
||||
ma_lw(ScratchRegister, addr);
|
||||
ma_load(ScratchRegister, addr, SizeWord);
|
||||
ma_b(ScratchRegister, rhs, l, c, jumpKind);
|
||||
}
|
||||
|
||||
@@ -327,7 +333,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void computeEffectiveAddress(const BaseIndex& address, Register dest) {
|
||||
computeScaledAddress(address, dest);
|
||||
if (address.offset) {
|
||||
ma_addu(dest, dest, Imm32(address.offset));
|
||||
addPtr(Imm32(address.offset), dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,10 +342,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
}
|
||||
|
||||
void mov(Register src, Register dest) {
|
||||
as_or(dest, src, zero);
|
||||
as_ori(dest, src, 0);
|
||||
}
|
||||
void mov(ImmWord imm, Register dest) {
|
||||
ma_li(dest, Imm32(imm.value));
|
||||
ma_li(dest, imm);
|
||||
}
|
||||
void mov(ImmPtr imm, Register dest) {
|
||||
mov(ImmWord(uintptr_t(imm.value)), dest);
|
||||
@@ -354,7 +360,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void branch(JitCode* c) {
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
addPendingJump(bo, ImmPtr(c->raw()), Relocation::JITCODE);
|
||||
ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
|
||||
ma_liPatchable(ScratchRegister, ImmPtr(c->raw()));
|
||||
as_jr(ScratchRegister);
|
||||
as_nop();
|
||||
}
|
||||
@@ -372,8 +378,8 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
}
|
||||
void retn(Imm32 n) {
|
||||
// pc <- [sp]; sp += n
|
||||
ma_lw(ra, Address(StackPointer, 0));
|
||||
ma_addu(StackPointer, StackPointer, n);
|
||||
loadPtr(Address(StackPointer, 0), ra);
|
||||
addPtr(n, StackPointer);
|
||||
as_jr(ra);
|
||||
as_nop();
|
||||
}
|
||||
@@ -382,7 +388,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
ma_push(ScratchRegister);
|
||||
}
|
||||
void push(ImmWord imm) {
|
||||
ma_li(ScratchRegister, Imm32(imm.value));
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_push(ScratchRegister);
|
||||
}
|
||||
void push(ImmGCPtr imm) {
|
||||
@@ -390,7 +396,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
ma_push(ScratchRegister);
|
||||
}
|
||||
void push(const Address& address) {
|
||||
ma_lw(ScratchRegister, address);
|
||||
loadPtr(address, ScratchRegister);
|
||||
ma_push(ScratchRegister);
|
||||
}
|
||||
void push(Register reg) {
|
||||
@@ -428,7 +434,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
|
||||
CodeOffsetLabel movWithPatch(ImmWord imm, Register dest) {
|
||||
CodeOffsetLabel label = CodeOffsetLabel(currentOffset());
|
||||
ma_liPatchable(dest, Imm32(imm.value));
|
||||
ma_liPatchable(dest, imm);
|
||||
return label;
|
||||
}
|
||||
CodeOffsetLabel movWithPatch(ImmPtr imm, Register dest) {
|
||||
@@ -443,7 +449,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
as_nop();
|
||||
}
|
||||
void jump(const Address& address) {
|
||||
ma_lw(ScratchRegister, address);
|
||||
loadPtr(address, ScratchRegister);
|
||||
as_jr(ScratchRegister);
|
||||
as_nop();
|
||||
}
|
||||
@@ -560,11 +566,11 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
}
|
||||
}
|
||||
void branch32(Condition cond, const Address& lhs, Register rhs, Label* label) {
|
||||
ma_lw(ScratchRegister, lhs);
|
||||
ma_b(ScratchRegister, rhs, label, cond);
|
||||
load32(lhs, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, rhs, label, cond);
|
||||
}
|
||||
void branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label) {
|
||||
ma_lw(SecondScratchReg, lhs);
|
||||
load32(lhs, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, rhs, label, cond);
|
||||
}
|
||||
void branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label) {
|
||||
@@ -572,7 +578,8 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
ma_b(SecondScratchReg, rhs, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, const Address& lhs, Register rhs, Label* label) {
|
||||
branch32(cond, lhs, rhs, label);
|
||||
loadPtr(lhs, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, rhs, label, cond);
|
||||
}
|
||||
|
||||
void branchPrivatePtr(Condition cond, const Address& lhs, ImmPtr ptr, Label* label) {
|
||||
@@ -654,45 +661,54 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
branchTest32(cond, lhs, ScratchRegister, label);
|
||||
}
|
||||
void branchTest32(Condition cond, const Address& address, Imm32 imm, Label* label) {
|
||||
ma_lw(SecondScratchReg, address);
|
||||
load32(address, SecondScratchReg);
|
||||
branchTest32(cond, SecondScratchReg, imm, label);
|
||||
}
|
||||
void branchTest32(Condition cond, AbsoluteAddress address, Imm32 imm, Label* label) {
|
||||
loadPtr(address, ScratchRegister);
|
||||
load32(address, ScratchRegister);
|
||||
branchTest32(cond, ScratchRegister, imm, label);
|
||||
}
|
||||
void branchTestPtr(Condition cond, Register lhs, Register rhs, Label* label) {
|
||||
branchTest32(cond, lhs, rhs, label);
|
||||
MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
|
||||
if (lhs == rhs) {
|
||||
ma_b(lhs, rhs, label, cond);
|
||||
} else {
|
||||
as_and(ScratchRegister, lhs, rhs);
|
||||
ma_b(ScratchRegister, ScratchRegister, label, cond);
|
||||
}
|
||||
}
|
||||
void branchTestPtr(Condition cond, Register lhs, const Imm32 rhs, Label* label) {
|
||||
branchTest32(cond, lhs, rhs, label);
|
||||
ma_li(ScratchRegister, rhs);
|
||||
branchTestPtr(cond, lhs, ScratchRegister, label);
|
||||
}
|
||||
void branchTestPtr(Condition cond, const Address& lhs, Imm32 imm, Label* label) {
|
||||
branchTest32(cond, lhs, imm, label);
|
||||
loadPtr(lhs, SecondScratchReg);
|
||||
branchTestPtr(cond, SecondScratchReg, imm, label);
|
||||
}
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
|
||||
Label* label);
|
||||
void branchPtr(Condition cond, Register lhs, Register rhs, Label* label) {
|
||||
ma_b(lhs, rhs, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, Register lhs, ImmGCPtr ptr, Label* label) {
|
||||
ma_li(ScratchRegister, ptr);
|
||||
ma_b(lhs, ScratchRegister, label, cond);
|
||||
ma_b(lhs, ptr, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, Register lhs, ImmWord imm, Label* label) {
|
||||
ma_b(lhs, Imm32(imm.value), label, cond);
|
||||
ma_b(lhs, imm, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, Register lhs, ImmPtr imm, Label* label) {
|
||||
branchPtr(cond, lhs, ImmWord(uintptr_t(imm.value)), label);
|
||||
ma_b(lhs, imm, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, Register lhs, AsmJSImmPtr imm, Label* label) {
|
||||
movePtr(imm, ScratchRegister);
|
||||
branchPtr(cond, lhs, ScratchRegister, label);
|
||||
movePtr(imm, SecondScratchReg);
|
||||
ma_b(lhs, SecondScratchReg, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
|
||||
ma_b(lhs, imm, label, cond);
|
||||
}
|
||||
void decBranchPtr(Condition cond, Register lhs, Imm32 imm, Label* label) {
|
||||
subPtr(imm, lhs);
|
||||
branch32(cond, lhs, Imm32(0), label);
|
||||
branchPtr(cond, lhs, Imm32(0), label);
|
||||
}
|
||||
|
||||
// higher level tag testing code
|
||||
@@ -736,43 +752,41 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
return off;
|
||||
}
|
||||
void branchPtr(Condition cond, Address addr, ImmGCPtr ptr, Label* label) {
|
||||
ma_lw(SecondScratchReg, addr);
|
||||
ma_li(ScratchRegister, ptr);
|
||||
ma_b(SecondScratchReg, ScratchRegister, label, cond);
|
||||
loadPtr(addr, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, ptr, label, cond);
|
||||
}
|
||||
|
||||
void branchPtr(Condition cond, Address addr, ImmWord ptr, Label* label) {
|
||||
ma_lw(SecondScratchReg, addr);
|
||||
ma_b(SecondScratchReg, Imm32(ptr.value), label, cond);
|
||||
loadPtr(addr, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, ptr, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label* label) {
|
||||
branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label);
|
||||
loadPtr(addr, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, ptr, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, AbsoluteAddress addr, Register ptr, Label* label) {
|
||||
loadPtr(addr, ScratchRegister);
|
||||
ma_b(ScratchRegister, ptr, label, cond);
|
||||
loadPtr(addr, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, ptr, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, AbsoluteAddress addr, ImmWord ptr, Label* label) {
|
||||
loadPtr(addr, ScratchRegister);
|
||||
ma_b(ScratchRegister, Imm32(ptr.value), label, cond);
|
||||
loadPtr(addr, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, ptr, label, cond);
|
||||
}
|
||||
void branchPtr(Condition cond, AsmJSAbsoluteAddress addr, Register ptr,
|
||||
Label* label) {
|
||||
loadPtr(addr, ScratchRegister);
|
||||
ma_b(ScratchRegister, ptr, label, cond);
|
||||
void branchPtr(Condition cond, AsmJSAbsoluteAddress addr, Register ptr, Label* label) {
|
||||
loadPtr(addr, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, ptr, label, cond);
|
||||
}
|
||||
void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label* label) {
|
||||
loadPtr(lhs, SecondScratchReg); // ma_b might use scratch
|
||||
load32(lhs, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, rhs, label, cond);
|
||||
}
|
||||
void branch32(Condition cond, AbsoluteAddress lhs, Register rhs, Label* label) {
|
||||
loadPtr(lhs, ScratchRegister);
|
||||
ma_b(ScratchRegister, rhs, label, cond);
|
||||
load32(lhs, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, rhs, label, cond);
|
||||
}
|
||||
void branch32(Condition cond, AsmJSAbsoluteAddress addr, Imm32 imm,
|
||||
Label* label) {
|
||||
loadPtr(addr, ScratchRegister);
|
||||
ma_b(ScratchRegister, imm, label, cond);
|
||||
void branch32(Condition cond, AsmJSAbsoluteAddress addr, Imm32 imm, Label* label) {
|
||||
load32(addr, SecondScratchReg);
|
||||
ma_b(SecondScratchReg, imm, label, cond);
|
||||
}
|
||||
|
||||
void loadUnboxedValue(Address address, MIRType type, AnyRegister dest) {
|
||||
@@ -797,7 +811,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void storeUnboxedPayload(ValueOperand value, T address, size_t nbytes) {
|
||||
switch (nbytes) {
|
||||
case 4:
|
||||
storePtr(value.payloadReg(), address);
|
||||
store32(value.payloadReg(), address);
|
||||
return;
|
||||
case 1:
|
||||
store8(value.payloadReg(), address);
|
||||
@@ -1098,6 +1112,11 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void add32(Register src, Register dest);
|
||||
void add32(Imm32 imm, Register dest);
|
||||
void add32(Imm32 imm, const Address& dest);
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
as_addiu(dest.low, dest.low, imm.value);
|
||||
as_sltiu(ScratchRegister, dest.low, imm.value);
|
||||
as_addu(dest.high, dest.high, ScratchRegister);
|
||||
}
|
||||
void sub32(Imm32 imm, Register dest);
|
||||
void sub32(Register src, Register dest);
|
||||
|
||||
@@ -1137,6 +1156,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
|
||||
void move32(Imm32 imm, Register dest);
|
||||
void move32(Register src, Register dest);
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
move32(src.low, dest.low);
|
||||
move32(src.high, dest.high);
|
||||
}
|
||||
|
||||
void movePtr(Register src, Register dest);
|
||||
void movePtr(ImmWord imm, Register dest);
|
||||
@@ -1159,6 +1182,11 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void load32(const Address& address, Register dest);
|
||||
void load32(const BaseIndex& address, Register dest);
|
||||
void load32(AbsoluteAddress address, Register dest);
|
||||
void load32(AsmJSAbsoluteAddress address, Register dest);
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
load32(Address(address.base, address.offset + LOW_32_OFFSET), dest.low);
|
||||
load32(Address(address.base, address.offset + HIGH_32_OFFSET), dest.high);
|
||||
}
|
||||
|
||||
void loadPtr(const Address& address, Register dest);
|
||||
void loadPtr(const BaseIndex& src, Register dest);
|
||||
@@ -1229,6 +1257,11 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
store32(src, address);
|
||||
}
|
||||
|
||||
void store64(Register64 src, Address address) {
|
||||
store32(src.low, Address(address.base, address.offset + LOW_32_OFFSET));
|
||||
store32(src.high, Address(address.base, address.offset + HIGH_32_OFFSET));
|
||||
}
|
||||
|
||||
template <typename T> void storePtr(ImmWord imm, T address);
|
||||
template <typename T> void storePtr(ImmPtr imm, T address);
|
||||
template <typename T> void storePtr(ImmGCPtr imm, T address);
|
||||
@@ -1298,6 +1331,15 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
as_addu(dest, dest, src);
|
||||
}
|
||||
|
||||
void mul64(Imm64 imm, const Register64& dest);
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
movePtr(imm, ScratchRegister);
|
||||
loadDouble(Address(ScratchRegister, 0), ScratchDoubleReg);
|
||||
mulDouble(ScratchDoubleReg, dest);
|
||||
}
|
||||
|
||||
void breakpoint();
|
||||
|
||||
void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
|
||||
@@ -1318,9 +1360,21 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
ma_sra(dest, dest, imm);
|
||||
}
|
||||
void rshift64(Imm32 imm, Register64 dest) {
|
||||
as_srl(dest.low, dest.low, imm.value);
|
||||
as_sll(ScratchRegister, dest.high, 32 - imm.value);
|
||||
as_or(dest.low, dest.low, ScratchRegister);
|
||||
as_srl(dest.high, dest.high, imm.value);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
ma_sll(dest, dest, imm);
|
||||
}
|
||||
void lshift64(Imm32 imm, Register64 dest) {
|
||||
as_sll(dest.high, dest.high, imm.value);
|
||||
as_srl(ScratchRegister, dest.low, 32 - imm.value);
|
||||
as_or(dest.high, dest.high, ScratchRegister);
|
||||
as_sll(dest.low, dest.low, imm.value);
|
||||
}
|
||||
|
||||
// If source is a double, load it into dest. If source is int32,
|
||||
// convert it to double. Else, branch to failure.
|
||||
@@ -1366,7 +1420,7 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
|
||||
|
||||
BufferOffset ma_BoundsCheck(Register bounded) {
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
ma_liPatchable(bounded, Imm32(0));
|
||||
ma_liPatchable(bounded, ImmWord(0));
|
||||
return bo;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ class CodeGeneratorNone : public CodeGeneratorShared
|
||||
ValueOperand ToOutValue(LInstruction*) { MOZ_CRASH(); }
|
||||
ValueOperand ToTempValue(LInstruction*, size_t) { MOZ_CRASH(); }
|
||||
void generateInvalidateEpilogue() { MOZ_CRASH(); }
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs) { MOZ_CRASH(); }
|
||||
};
|
||||
|
||||
typedef CodeGeneratorNone CodeGeneratorSpecific;
|
||||
|
||||
@@ -112,7 +112,6 @@ class LModPowTwoI : public LInstructionHelper<1, 1, 0>
|
||||
class LGuardShape : public LInstruction {};
|
||||
class LGuardObjectGroup : public LInstruction {};
|
||||
class LMulI : public LInstruction {};
|
||||
class LRandom : public LInstructionHelper<1, 0, 5> {};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
@@ -87,7 +87,6 @@ class LIRGeneratorNone : public LIRGeneratorShared
|
||||
void visitSimdValueX4(MSimdValueX4* lir) { MOZ_CRASH(); }
|
||||
void visitSubstr(MSubstr*) { MOZ_CRASH(); }
|
||||
void visitSimdBinaryArith(js::jit::MSimdBinaryArith*) { MOZ_CRASH(); }
|
||||
void visitRandom(MRandom* ) { MOZ_CRASH(); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -125,6 +125,15 @@ struct ImmWord
|
||||
{ }
|
||||
};
|
||||
|
||||
// Used for 64-bit immediates which do not require relocation.
|
||||
struct Imm64
|
||||
{
|
||||
uint64_t value;
|
||||
|
||||
explicit Imm64(uint64_t value) : value(value)
|
||||
{ }
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
static inline bool
|
||||
IsCompilingAsmJS()
|
||||
|
||||
@@ -7145,6 +7145,57 @@ class LArrowNewTarget : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
#ifdef JS_PUNBOX64
|
||||
# define LRANDOM_NUM_TEMPS 3
|
||||
#else
|
||||
# define LRANDOM_NUM_TEMPS 5
|
||||
#endif
|
||||
|
||||
class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition &tempMaybeEAX, const LDefinition &tempMaybeEDX,
|
||||
const LDefinition &temp1
|
||||
#ifndef JS_PUNBOX64
|
||||
, const LDefinition &temp2, const LDefinition &temp3
|
||||
#endif
|
||||
)
|
||||
{
|
||||
setTemp(0, tempMaybeEAX);
|
||||
setTemp(1, tempMaybeEDX);
|
||||
setTemp(2, temp1);
|
||||
#ifndef JS_PUNBOX64
|
||||
setTemp(3, temp2);
|
||||
setTemp(4, temp3);
|
||||
#endif
|
||||
}
|
||||
// On x86, following 2 methods return eax and edx necessary for mull.
|
||||
// On others, following 2 methods return ordinary temporary registers.
|
||||
const LDefinition* tempMaybeEAX() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* tempMaybeEDX() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *temp1() {
|
||||
return getTemp(2);
|
||||
}
|
||||
#ifndef JS_PUNBOX64
|
||||
const LDefinition *temp2() {
|
||||
return getTemp(3);
|
||||
}
|
||||
const LDefinition *temp3() {
|
||||
return getTemp(4);
|
||||
}
|
||||
#endif
|
||||
|
||||
MRandom* mir() const {
|
||||
return mir_->toRandom();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -841,129 +841,3 @@ CodeGeneratorX64::visitTruncateFToInt32(LTruncateFToInt32* ins)
|
||||
// call a stub if it fails.
|
||||
emitTruncateFloat32(input, output, ins->mir());
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
// Out-of-line math_random_no_outparam call for LRandom.
|
||||
class OutOfLineRandom : public OutOfLineCodeBase<CodeGeneratorX64>
|
||||
{
|
||||
LRandom* lir_;
|
||||
|
||||
public:
|
||||
explicit OutOfLineRandom(LRandom* lir)
|
||||
: lir_(lir)
|
||||
{ }
|
||||
|
||||
void accept(CodeGeneratorX64* codegen) {
|
||||
codegen->visitOutOfLineRandom(this);
|
||||
}
|
||||
|
||||
LRandom* lir() const {
|
||||
return lir_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
|
||||
|
||||
void
|
||||
CodeGeneratorX64::visitRandom(LRandom* ins)
|
||||
{
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
|
||||
Register JSCompartmentReg = ToRegister(ins->temp());
|
||||
Register rngStateReg = ToRegister(ins->temp2());
|
||||
Register highReg = ToRegister(ins->temp3());
|
||||
Register lowReg = ToRegister(ins->temp4());
|
||||
Register rngMaskReg = ToRegister(ins->temp5());
|
||||
|
||||
// rngState = cx->compartment()->rngState
|
||||
masm.loadJSContext(JSCompartmentReg);
|
||||
masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
|
||||
masm.loadPtr(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
|
||||
addOutOfLineCode(ool, ins->mir());
|
||||
masm.branchTestPtr(Assembler::Zero, rngStateReg, rngStateReg, ool->entry());
|
||||
|
||||
// nextstate = rngState * RNG_MULTIPLIER;
|
||||
Register& rngMultiplierReg = lowReg;
|
||||
masm.movq(ImmWord(RNG_MULTIPLIER), rngMultiplierReg);
|
||||
masm.imulq(rngMultiplierReg, rngStateReg);
|
||||
|
||||
// nextstate += RNG_ADDEND;
|
||||
masm.addq(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// nextstate &= RNG_MASK;
|
||||
masm.movq(ImmWord(RNG_MASK), rngMaskReg);
|
||||
masm.andq(rngMaskReg, rngStateReg);
|
||||
|
||||
// rngState = nextstate
|
||||
|
||||
// if rngState == 0, escape from inlined code and call
|
||||
// math_random_no_outparam.
|
||||
masm.j(Assembler::Zero, ool->entry());
|
||||
|
||||
// high = (nextstate >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
|
||||
masm.movq(rngStateReg, highReg);
|
||||
masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_HIGH_BITS), highReg);
|
||||
masm.shlq(Imm32(RNG_LOW_BITS), highReg);
|
||||
|
||||
// nextstate = rngState * RNG_MULTIPLIER;
|
||||
masm.imulq(rngMultiplierReg, rngStateReg);
|
||||
|
||||
// nextstate += RNG_ADDEND;
|
||||
masm.addq(Imm32(RNG_ADDEND), rngStateReg);
|
||||
|
||||
// nextstate &= RNG_MASK;
|
||||
masm.andq(rngMaskReg, rngStateReg);
|
||||
|
||||
// low = nextstate >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
|
||||
masm.movq(rngStateReg, lowReg);
|
||||
masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
|
||||
|
||||
// output = double(high | low);
|
||||
masm.orq(highReg, lowReg);
|
||||
masm.vcvtsi2sdq(lowReg, output);
|
||||
|
||||
// output = output * RNG_DSCALE_INV;
|
||||
Register& rngDscaleInvReg = lowReg;
|
||||
masm.movq(ImmPtr(&RNG_DSCALE_INV), rngDscaleInvReg);
|
||||
masm.vmulsd(Operand(rngDscaleInvReg, 0), output, output);
|
||||
|
||||
// cx->compartment()->rngState = nextstate
|
||||
masm.storePtr(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX64::visitOutOfLineRandom(OutOfLineRandom* ool)
|
||||
{
|
||||
LRandom* ins = ool->lir();
|
||||
Register temp = ToRegister(ins->temp());
|
||||
Register temp2 = ToRegister(ins->temp2());
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
|
||||
LiveRegisterSet regs;
|
||||
regs.add(ReturnFloat32Reg);
|
||||
regs.add(ReturnDoubleReg);
|
||||
regs.add(ReturnInt32x4Reg);
|
||||
regs.add(ReturnFloat32x4Reg);
|
||||
saveVolatile(regs);
|
||||
|
||||
masm.loadJSContext(temp);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
restoreVolatile(regs);
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
class OutOfLineRandom;
|
||||
|
||||
class CodeGeneratorX64 : public CodeGeneratorX86Shared
|
||||
{
|
||||
CodeGeneratorX64* thisFromCtor() {
|
||||
@@ -61,8 +59,6 @@ class CodeGeneratorX64 : public CodeGeneratorX86Shared
|
||||
void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
|
||||
void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir);
|
||||
void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir);
|
||||
void visitRandom(LRandom* ins);
|
||||
void visitOutOfLineRandom(OutOfLineRandom* ool);
|
||||
};
|
||||
|
||||
typedef CodeGeneratorX64 CodeGeneratorSpecific;
|
||||
|
||||
@@ -99,41 +99,6 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 1>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LInstructionHelper<1, 0, 5>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3,
|
||||
const LDefinition &temp4, const LDefinition &temp5)
|
||||
{
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
setTemp(2, temp3);
|
||||
setTemp(3, temp4);
|
||||
setTemp(4, temp5);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition *temp3() {
|
||||
return getTemp(2);
|
||||
}
|
||||
const LDefinition *temp4() {
|
||||
return getTemp(3);
|
||||
}
|
||||
const LDefinition *temp5() {
|
||||
return getTemp(4);
|
||||
}
|
||||
|
||||
MRandom* mir() const {
|
||||
return mir_->toRandom();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -337,8 +337,6 @@ void
|
||||
LIRGeneratorX64::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom* lir = new(alloc()) LRandom(temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
|
||||
@@ -29,6 +29,13 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
andq(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
|
||||
andq(ScratchReg, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::orPtr(Register src, Register dest)
|
||||
{
|
||||
@@ -41,6 +48,12 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
orq(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
orq(src.reg, dest.reg);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xorPtr(Register src, Register dest)
|
||||
{
|
||||
|
||||
@@ -604,6 +604,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
void addPtr(const Address& src, Register dest) {
|
||||
addq(Operand(src), dest);
|
||||
}
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
addq(imm, dest.reg);
|
||||
}
|
||||
void subPtr(Imm32 imm, Register dest) {
|
||||
subq(imm, dest);
|
||||
}
|
||||
@@ -619,6 +622,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
void mulBy3(const Register& src, const Register& dest) {
|
||||
lea(Operand(src, src, TimesTwo), dest);
|
||||
}
|
||||
void mul64(Imm64 imm, const Register64& dest) {
|
||||
movq(ImmWord(uintptr_t(imm.value)), ScratchReg);
|
||||
imulq(ScratchReg, dest.reg);
|
||||
}
|
||||
|
||||
void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label* label) {
|
||||
if (X86Encoding::IsAddressImmediate(lhs.addr)) {
|
||||
@@ -741,6 +748,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
j(cond, label);
|
||||
}
|
||||
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
|
||||
branchTestPtr(cond, lhs.reg, rhs.reg, label);
|
||||
}
|
||||
|
||||
void movePtr(Register src, Register dest) {
|
||||
movq(src, dest);
|
||||
}
|
||||
@@ -759,6 +770,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
void movePtr(ImmGCPtr imm, Register dest) {
|
||||
movq(imm, dest);
|
||||
}
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
movq(src.reg, dest.reg);
|
||||
}
|
||||
void loadPtr(AbsoluteAddress address, Register dest) {
|
||||
if (X86Encoding::IsAddressImmediate(address.addr)) {
|
||||
movq(Operand(address), dest);
|
||||
@@ -790,6 +804,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
load32(Address(scratch, 0x0), dest);
|
||||
}
|
||||
}
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
movq(Operand(address), dest.reg);
|
||||
}
|
||||
template <typename T>
|
||||
void storePtr(ImmWord imm, T address) {
|
||||
if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
|
||||
@@ -837,15 +854,24 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
store32(src, Address(scratch, 0x0));
|
||||
}
|
||||
}
|
||||
void store64(Register64 src, Address address) {
|
||||
movq(src.reg, Operand(address));
|
||||
}
|
||||
void rshiftPtr(Imm32 imm, Register dest) {
|
||||
shrq(imm, dest);
|
||||
}
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
sarq(imm, dest);
|
||||
}
|
||||
void rshift64(Imm32 imm, Register64 dest) {
|
||||
shrq(imm, dest.reg);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
shlq(imm, dest);
|
||||
}
|
||||
void lshift64(Imm32 imm, Register64 dest) {
|
||||
shlq(imm, dest.reg);
|
||||
}
|
||||
|
||||
void splitTag(Register src, Register dest) {
|
||||
if (src != dest)
|
||||
@@ -1370,6 +1396,15 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
||||
vcvtsq2ss(src, dest, dest);
|
||||
}
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest) {
|
||||
vcvtsi2sdq(src.reg, dest);
|
||||
}
|
||||
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
movq(imm, ScratchReg);
|
||||
vmulsd(Operand(ScratchReg, 0), dest, dest);
|
||||
}
|
||||
|
||||
void inc64(AbsoluteAddress dest) {
|
||||
if (X86Encoding::IsAddressImmediate(dest.addr)) {
|
||||
addPtr(Imm32(1), Operand(dest));
|
||||
|
||||
@@ -3640,5 +3640,18 @@ CodeGeneratorX86Shared::visitMemoryBarrier(LMemoryBarrier* ins)
|
||||
masm.storeLoadFence();
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX86Shared::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
MOZ_ASSERT(ReturnFloat32Reg.encoding() == X86Encoding::xmm0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.encoding() == X86Encoding::xmm0);
|
||||
MOZ_ASSERT(ReturnInt32x4Reg.encoding() == X86Encoding::xmm0);
|
||||
MOZ_ASSERT(ReturnFloat32x4Reg.encoding() == X86Encoding::xmm0);
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(ReturnDoubleReg);
|
||||
regs->add(ReturnInt32x4Reg);
|
||||
regs->add(ReturnFloat32x4Reg);
|
||||
}
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
@@ -300,6 +300,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
// Generating no result.
|
||||
template<typename S, typename T>
|
||||
void atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value, const T& mem);
|
||||
|
||||
void setReturnDoubleRegs(LiveRegisterSet* regs);
|
||||
};
|
||||
|
||||
// An out-of-line bailout thunk.
|
||||
|
||||
@@ -175,6 +175,7 @@ enum TwoByteOpcodeID {
|
||||
OP2_ANDPD_VpdWpd = 0x54,
|
||||
OP2_ORPD_VpdWpd = 0x56,
|
||||
OP2_XORPD_VpdWpd = 0x57,
|
||||
OP2_PUNPCKLDQ = 0x62,
|
||||
OP2_PCMPGTD_VdqWdq = 0x66,
|
||||
OP2_MOVD_VdEd = 0x6E,
|
||||
OP2_MOVDQ_VsdWsd = 0x6F,
|
||||
@@ -186,11 +187,14 @@ enum TwoByteOpcodeID {
|
||||
OP2_PSRLDQ_Vd = 0x73,
|
||||
OP2_PCMPEQW = 0x75,
|
||||
OP2_PCMPEQD_VdqWdq = 0x76,
|
||||
OP2_HADDPD = 0x7C,
|
||||
OP2_MOVD_EdVd = 0x7E,
|
||||
OP2_MOVQ_VdWd = 0x7E,
|
||||
OP2_MOVDQ_WdqVdq = 0x7F,
|
||||
OP2_JCC_rel32 = 0x80,
|
||||
OP_SETCC = 0x90,
|
||||
OP2_SHLD = 0xA4,
|
||||
OP2_SHRD = 0xAC,
|
||||
OP_FENCE = 0xAE,
|
||||
OP2_IMUL_GvEv = 0xAF,
|
||||
OP2_CMPXCHG_GvEb = 0xB0,
|
||||
|
||||
@@ -51,6 +51,7 @@ static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloat32x4Reg = FloatRegister(X86Enc
|
||||
static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloat32Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Single);
|
||||
static MOZ_CONSTEXPR_VAR FloatRegister ScratchDoubleReg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Double);
|
||||
static MOZ_CONSTEXPR_VAR FloatRegister ScratchSimdReg = xmm7;
|
||||
static MOZ_CONSTEXPR_VAR FloatRegister ScratchInt32x4Reg = FloatRegister(X86Encoding::xmm7, FloatRegisters::Int32x4);
|
||||
|
||||
// Avoid ebp, which is the FramePointer, which is unavailable in some modes.
|
||||
static MOZ_CONSTEXPR_VAR Register ArgumentsRectifierReg = esi;
|
||||
@@ -375,6 +376,66 @@ class Assembler : public AssemblerX86Shared
|
||||
append(AsmJSAbsoluteLink(CodeOffsetLabel(src.offset()), lhs.kind()));
|
||||
}
|
||||
|
||||
void adcl(Imm32 imm, Register dest) {
|
||||
masm.adcl_ir(imm.value, dest.encoding());
|
||||
}
|
||||
|
||||
void mull(Register multiplier) {
|
||||
masm.mull_r(multiplier.encoding());
|
||||
}
|
||||
|
||||
void shldl(const Imm32 imm, Register src, Register dest) {
|
||||
masm.shldl_irr(imm.value, src.encoding(), dest.encoding());
|
||||
}
|
||||
void shrdl(const Imm32 imm, Register src, Register dest) {
|
||||
masm.shrdl_irr(imm.value, src.encoding(), dest.encoding());
|
||||
}
|
||||
|
||||
void vhaddpd(FloatRegister src, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
MOZ_ASSERT(src.size() == 16);
|
||||
MOZ_ASSERT(dest.size() == 16);
|
||||
masm.vhaddpd_rr(src.encoding(), dest.encoding());
|
||||
}
|
||||
void vsubpd(const Operand& src1, FloatRegister src0, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
MOZ_ASSERT(src0.size() == 16);
|
||||
MOZ_ASSERT(dest.size() == 16);
|
||||
switch (src1.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.vsubpd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding());
|
||||
break;
|
||||
case Operand::MEM_ADDRESS32:
|
||||
masm.vsubpd_mr(src1.address(), src0.encoding(), dest.encoding());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
|
||||
void vpunpckldq(FloatRegister src1, FloatRegister src0, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
MOZ_ASSERT(src0.size() == 16);
|
||||
MOZ_ASSERT(src1.size() == 16);
|
||||
MOZ_ASSERT(dest.size() == 16);
|
||||
masm.vpunpckldq_rr(src1.encoding(), src0.encoding(), dest.encoding());
|
||||
}
|
||||
void vpunpckldq(const Operand& src1, FloatRegister src0, FloatRegister dest) {
|
||||
MOZ_ASSERT(HasSSE2());
|
||||
MOZ_ASSERT(src0.size() == 16);
|
||||
MOZ_ASSERT(dest.size() == 16);
|
||||
switch (src1.kind()) {
|
||||
case Operand::MEM_REG_DISP:
|
||||
masm.vpunpckldq_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding());
|
||||
break;
|
||||
case Operand::MEM_ADDRESS32:
|
||||
masm.vpunpckldq_mr(src1.address(), src0.encoding(), dest.encoding());
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
|
||||
void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) {
|
||||
JmpSrc src = masm.jmp();
|
||||
addPendingJump(src, target, reloc);
|
||||
|
||||
@@ -20,6 +20,14 @@ class BaseAssemblerX86 : public BaseAssembler
|
||||
|
||||
// Arithmetic operations:
|
||||
|
||||
void adcl_ir(int32_t imm, RegisterID dst)
|
||||
{
|
||||
spew("adcl $%d, %s", imm, GPReg32Name(dst));
|
||||
MOZ_ASSERT(CAN_SIGN_EXTEND_8_32(imm));
|
||||
m_formatter.oneByteOp(OP_GROUP1_EvIb, dst, GROUP1_OP_ADC);
|
||||
m_formatter.immediate8s(imm);
|
||||
}
|
||||
|
||||
void adcl_im(int32_t imm, const void* addr)
|
||||
{
|
||||
spew("adcl %d, %p", imm, addr);
|
||||
@@ -71,6 +79,22 @@ class BaseAssemblerX86 : public BaseAssembler
|
||||
}
|
||||
}
|
||||
|
||||
void shldl_irr(int32_t imm, RegisterID src, RegisterID dst)
|
||||
{
|
||||
MOZ_ASSERT(imm < 32);
|
||||
spew("shldl $%d, %s, %s", imm, GPReg32Name(src), GPReg32Name(dst));
|
||||
m_formatter.twoByteOp8(OP2_SHLD, dst, src);
|
||||
m_formatter.immediate8u(imm);
|
||||
}
|
||||
|
||||
void shrdl_irr(int32_t imm, RegisterID src, RegisterID dst)
|
||||
{
|
||||
MOZ_ASSERT(imm < 32);
|
||||
spew("shrdl $%d, %s, %s", imm, GPReg32Name(src), GPReg32Name(dst));
|
||||
m_formatter.twoByteOp8(OP2_SHRD, dst, src);
|
||||
m_formatter.immediate8u(imm);
|
||||
}
|
||||
|
||||
// SSE operations:
|
||||
|
||||
using BaseAssembler::vcvtsi2sd_mr;
|
||||
@@ -91,6 +115,36 @@ class BaseAssemblerX86 : public BaseAssembler
|
||||
twoByteOpSimd("vmovdqa", VEX_PD, OP2_MOVDQ_VdqWdq, address, invalid_xmm, dst);
|
||||
}
|
||||
|
||||
void vhaddpd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpSimdFlags("vhaddpd", VEX_PD, OP2_HADDPD, src, dst);
|
||||
}
|
||||
|
||||
void vsubpd_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, src1, src0, dst);
|
||||
}
|
||||
void vsubpd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, offset, base, src0, dst);
|
||||
}
|
||||
void vsubpd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, address, src0, dst);
|
||||
}
|
||||
|
||||
void vpunpckldq_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) {
|
||||
twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, src1, src0, dst);
|
||||
}
|
||||
void vpunpckldq_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, offset, base, src0, dst);
|
||||
}
|
||||
void vpunpckldq_mr(const void* addr, XMMRegisterID src0, XMMRegisterID dst)
|
||||
{
|
||||
twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, addr, src0, dst);
|
||||
}
|
||||
|
||||
// Misc instructions:
|
||||
|
||||
void pusha()
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
#include "jsmath.h"
|
||||
#include "jsnum.h"
|
||||
|
||||
#include "jit/IonCaches.h"
|
||||
@@ -1107,18 +1106,3 @@ CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool)
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
void
|
||||
CodeGeneratorX86::visitRandom(LRandom* ins)
|
||||
{
|
||||
Register temp = ToRegister(ins->temp());
|
||||
Register temp2 = ToRegister(ins->temp2());
|
||||
|
||||
masm.loadJSContext(temp);
|
||||
|
||||
masm.setupUnalignedABICall(temp2);
|
||||
masm.passABIArg(temp);
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
|
||||
|
||||
MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
|
||||
}
|
||||
|
||||
@@ -72,8 +72,6 @@ class CodeGeneratorX86 : public CodeGeneratorX86Shared
|
||||
void visitOutOfLineTruncate(OutOfLineTruncate* ool);
|
||||
void visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool);
|
||||
|
||||
void visitRandom(LRandom* ins);
|
||||
|
||||
private:
|
||||
void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
|
||||
int32_t offset, int32_t endOffset);
|
||||
|
||||
@@ -122,23 +122,6 @@ class LAsmJSLoadFuncPtr : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Math.random().
|
||||
class LRandom : public LCallInstructionHelper<1, 0, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(Random)
|
||||
LRandom(const LDefinition& temp, const LDefinition& temp2) {
|
||||
setTemp(0, temp);
|
||||
setTemp(1, temp2);
|
||||
}
|
||||
const LDefinition* temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -441,6 +441,11 @@ LIRGeneratorX86::visitSubstr(MSubstr* ins)
|
||||
void
|
||||
LIRGeneratorX86::visitRandom(MRandom* ins)
|
||||
{
|
||||
LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
|
||||
defineReturn(lir, ins);
|
||||
// eax and edx are necessary for mull.
|
||||
LRandom *lir = new(alloc()) LRandom(tempFixed(eax),
|
||||
tempFixed(edx),
|
||||
temp(),
|
||||
temp(),
|
||||
temp());
|
||||
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ MacroAssembler::andPtr(Imm32 imm, Register dest)
|
||||
andl(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::and64(Imm64 imm, Register64 dest)
|
||||
{
|
||||
andl(Imm32(imm.value & 0xFFFFFFFFL), dest.low);
|
||||
andl(Imm32((imm.value >> 32) & 0xFFFFFFFFL), dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::orPtr(Register src, Register dest)
|
||||
{
|
||||
@@ -41,6 +48,13 @@ MacroAssembler::orPtr(Imm32 imm, Register dest)
|
||||
orl(imm, dest);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::or64(Register64 src, Register64 dest)
|
||||
{
|
||||
orl(src.low, dest.low);
|
||||
orl(src.high, dest.high);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::xorPtr(Register src, Register dest)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "jit/x86/MacroAssembler-x86.h"
|
||||
|
||||
#include "mozilla/Alignment.h"
|
||||
#include "mozilla/Casting.h"
|
||||
|
||||
#include "jit/Bailouts.h"
|
||||
@@ -20,6 +21,62 @@
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
// vpunpckldq requires 16-byte boundary for memory operand.
|
||||
// See convertUInt64ToDouble for the details.
|
||||
MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = {
|
||||
0x4530000043300000LL,
|
||||
0x0LL,
|
||||
0x4330000000000000LL,
|
||||
0x4530000000000000LL
|
||||
};
|
||||
|
||||
void
|
||||
MacroAssemblerX86::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
|
||||
{
|
||||
// Following operation uses entire 128-bit of dest XMM register.
|
||||
// Currently higher 64-bit is free when we have access to lower 64-bit.
|
||||
MOZ_ASSERT(dest.size() == 8);
|
||||
FloatRegister dest128 = FloatRegister(dest.encoding(), FloatRegisters::Int32x4);
|
||||
|
||||
// Assume that src is represented as following:
|
||||
// src = 0x HHHHHHHH LLLLLLLL
|
||||
|
||||
// Move src to dest (=dest128) and ScratchInt32x4Reg (=scratch):
|
||||
// dest = 0x 00000000 00000000 00000000 LLLLLLLL
|
||||
// scratch = 0x 00000000 00000000 00000000 HHHHHHHH
|
||||
vmovd(src.low, dest128);
|
||||
vmovd(src.high, ScratchInt32x4Reg);
|
||||
|
||||
// Unpack and interleave dest and scratch to dest:
|
||||
// dest = 0x 00000000 00000000 HHHHHHHH LLLLLLLL
|
||||
vpunpckldq(ScratchInt32x4Reg, dest128, dest128);
|
||||
|
||||
// Unpack and interleave dest and a constant C1 to dest:
|
||||
// C1 = 0x 00000000 00000000 45300000 43300000
|
||||
// dest = 0x 45300000 HHHHHHHH 43300000 LLLLLLLL
|
||||
// here, each 64-bit part of dest represents following double:
|
||||
// HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000
|
||||
// LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL
|
||||
movePtr(ImmPtr(TO_DOUBLE), temp);
|
||||
vpunpckldq(Operand(temp, 0), dest128, dest128);
|
||||
|
||||
// Subtract a constant C2 from dest, for each 64-bit part:
|
||||
// C2 = 0x 45300000 00000000 43300000 00000000
|
||||
// here, each 64-bit part of C2 represents following double:
|
||||
// HI(C2) = 0x 1.0000000000000 * 2**84 == 2**84
|
||||
// LO(C2) = 0x 1.0000000000000 * 2**52 == 2**52
|
||||
// after the operation each 64-bit part of dest represents following:
|
||||
// HI(dest) = double(0x HHHHHHHH 00000000)
|
||||
// LO(dest) = double(0x 00000000 LLLLLLLL)
|
||||
vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128);
|
||||
|
||||
// Add HI(dest) and LO(dest) in double and store it into LO(dest),
|
||||
// LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL)
|
||||
// = double(0x HHHHHHHH LLLLLLLL)
|
||||
// = double(src)
|
||||
vhaddpd(dest128, dest128);
|
||||
}
|
||||
|
||||
MacroAssemblerX86::Double*
|
||||
MacroAssemblerX86::getDouble(double d)
|
||||
{
|
||||
|
||||
@@ -240,6 +240,14 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
push(tagOf(addr));
|
||||
push(payloadOfAfterStackPush(addr));
|
||||
}
|
||||
void push64(Register64 src) {
|
||||
push(src.high);
|
||||
push(src.low);
|
||||
}
|
||||
void pop64(Register64 dest) {
|
||||
pop(dest.low);
|
||||
pop(dest.high);
|
||||
}
|
||||
void storePayload(const Value& val, Operand dest) {
|
||||
jsval_layout jv = JSVAL_TO_IMPL(val);
|
||||
if (val.isMarkable())
|
||||
@@ -607,6 +615,10 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
void addPtr(const Address& src, Register dest) {
|
||||
addl(Operand(src), dest);
|
||||
}
|
||||
void add64(Imm32 imm, Register64 dest) {
|
||||
addl(imm, dest.low);
|
||||
adcl(Imm32(0), dest.high);
|
||||
}
|
||||
void subPtr(Imm32 imm, Register dest) {
|
||||
sub32(imm, dest);
|
||||
}
|
||||
@@ -622,6 +634,38 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
void mulBy3(const Register& src, const Register& dest) {
|
||||
lea(Operand(src, src, TimesTwo), dest);
|
||||
}
|
||||
// Note: this function clobbers eax and edx.
|
||||
void mul64(Imm64 imm, const Register64& dest) {
|
||||
// LOW32 = LOW(LOW(dest) * LOW(imm));
|
||||
// HIGH32 = LOW(HIGH(dest) * LOW(imm)) [multiply imm into upper bits]
|
||||
// + LOW(LOW(dest) * HIGH(imm)) [multiply dest into upper bits]
|
||||
// + HIGH(LOW(dest) * LOW(imm)) [carry]
|
||||
|
||||
MOZ_ASSERT(dest.low != eax && dest.low != edx);
|
||||
MOZ_ASSERT(dest.high != eax && dest.high != edx);
|
||||
|
||||
// HIGH(dest) = LOW(HIGH(dest) * LOW(imm));
|
||||
movl(Imm32(imm.value & 0xFFFFFFFFL), edx);
|
||||
imull(edx, dest.high);
|
||||
|
||||
// edx:eax = LOW(dest) * LOW(imm);
|
||||
movl(Imm32(imm.value & 0xFFFFFFFFL), edx);
|
||||
movl(dest.low, eax);
|
||||
mull(edx);
|
||||
|
||||
// HIGH(dest) += edx;
|
||||
addl(edx, dest.high);
|
||||
|
||||
// HIGH(dest) += LOW(LOW(dest) * HIGH(imm));
|
||||
if (((imm.value >> 32) & 0xFFFFFFFFL) == 5)
|
||||
leal(Operand(dest.low, dest.low, TimesFour), edx);
|
||||
else
|
||||
MOZ_CRASH("Unsupported imm");
|
||||
addl(edx, dest.high);
|
||||
|
||||
// LOW(dest) = eax;
|
||||
movl(eax, dest.low);
|
||||
}
|
||||
|
||||
void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label* label) {
|
||||
cmp32(Operand(lhs), rhs);
|
||||
@@ -712,6 +756,18 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
j(cond, label);
|
||||
}
|
||||
|
||||
void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp, Label* label) {
|
||||
if (cond == Assembler::Zero) {
|
||||
MOZ_ASSERT(lhs.low == rhs.low);
|
||||
MOZ_ASSERT(lhs.high == rhs.high);
|
||||
movl(lhs.low, temp);
|
||||
orl(lhs.high, temp);
|
||||
branchTestPtr(cond, temp, temp, label);
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported condition");
|
||||
}
|
||||
}
|
||||
|
||||
void movePtr(ImmWord imm, Register dest) {
|
||||
movl(Imm32(imm.value), dest);
|
||||
}
|
||||
@@ -724,6 +780,10 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
void movePtr(ImmGCPtr imm, Register dest) {
|
||||
movl(imm, dest);
|
||||
}
|
||||
void move64(Register64 src, Register64 dest) {
|
||||
movl(src.low, dest.low);
|
||||
movl(src.high, dest.high);
|
||||
}
|
||||
void loadPtr(const Address& address, Register dest) {
|
||||
movl(Operand(address), dest);
|
||||
}
|
||||
@@ -742,6 +802,10 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
void load32(AbsoluteAddress address, Register dest) {
|
||||
movl(Operand(address), dest);
|
||||
}
|
||||
void load64(const Address& address, Register64 dest) {
|
||||
movl(Operand(address), dest.low);
|
||||
movl(Operand(Address(address.base, address.offset + 4)), dest.high);
|
||||
}
|
||||
template <typename T>
|
||||
void storePtr(ImmWord imm, T address) {
|
||||
movl(Imm32(imm.value), Operand(address));
|
||||
@@ -769,6 +833,10 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
void store32(Register src, AbsoluteAddress address) {
|
||||
movl(src, Operand(address));
|
||||
}
|
||||
void store64(Register64 src, Address address) {
|
||||
movl(src.low, Operand(address));
|
||||
movl(src.high, Operand(Address(address.base, address.offset + 4)));
|
||||
}
|
||||
|
||||
void setStackArg(Register reg, uint32_t arg) {
|
||||
movl(reg, Operand(esp, arg * sizeof(intptr_t)));
|
||||
@@ -1060,9 +1128,17 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
void rshiftPtrArithmetic(Imm32 imm, Register dest) {
|
||||
sarl(imm, dest);
|
||||
}
|
||||
void rshift64(Imm32 imm, Register64 dest) {
|
||||
shrdl(imm, dest.high, dest.low);
|
||||
shrl(imm, dest.high);
|
||||
}
|
||||
void lshiftPtr(Imm32 imm, Register dest) {
|
||||
shll(imm, dest);
|
||||
}
|
||||
void lshift64(Imm32 imm, Register64 dest) {
|
||||
shldl(imm, dest.low, dest.high);
|
||||
shll(imm, dest.low);
|
||||
}
|
||||
|
||||
void loadInstructionPointerAfterCall(Register dest) {
|
||||
movl(Operand(StackPointer, 0x0), dest);
|
||||
@@ -1087,6 +1163,13 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
||||
convertDoubleToFloat32(dest, dest);
|
||||
}
|
||||
|
||||
void convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest);
|
||||
|
||||
void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) {
|
||||
movl(imm, temp);
|
||||
vmulsd(Operand(temp, 0), dest, dest);
|
||||
}
|
||||
|
||||
void inc64(AbsoluteAddress dest) {
|
||||
addl(Imm32(1), Operand(dest));
|
||||
Label noOverflow;
|
||||
|
||||
@@ -70,7 +70,6 @@ BEGIN_TEST(testWeakMap_keyDelegates)
|
||||
{
|
||||
JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
|
||||
JS_GC(rt);
|
||||
|
||||
JS::RootedObject map(cx, JS::NewWeakMapObject(cx));
|
||||
CHECK(map);
|
||||
|
||||
@@ -81,16 +80,26 @@ BEGIN_TEST(testWeakMap_keyDelegates)
|
||||
CHECK(delegate);
|
||||
keyDelegate = delegate;
|
||||
|
||||
JS::RootedObject delegateRoot(cx);
|
||||
{
|
||||
JSAutoCompartment ac(cx, delegate);
|
||||
delegateRoot = JS_NewPlainObject(cx);
|
||||
CHECK(delegateRoot);
|
||||
JS::RootedValue delegateValue(cx, ObjectValue(*delegate));
|
||||
CHECK(JS_DefineProperty(cx, delegateRoot, "delegate", delegateValue, 0));
|
||||
}
|
||||
delegate = nullptr;
|
||||
|
||||
/*
|
||||
* Perform an incremental GC, introducing an unmarked CCW to force the map
|
||||
* zone to finish marking before the delegate zone.
|
||||
*/
|
||||
CHECK(newCCW(map, delegate));
|
||||
CHECK(newCCW(map, delegateRoot));
|
||||
js::SliceBudget budget(js::WorkBudget(1000000));
|
||||
rt->gc.startDebugGC(GC_NORMAL, budget);
|
||||
CHECK(!JS::IsIncrementalGCInProgress(rt));
|
||||
#ifdef DEBUG
|
||||
CHECK(map->zone()->lastZoneGroupIndex() < delegate->zone()->lastZoneGroupIndex());
|
||||
CHECK(map->zone()->lastZoneGroupIndex() < delegateRoot->zone()->lastZoneGroupIndex());
|
||||
#endif
|
||||
|
||||
/* Add our entry to the weakmap. */
|
||||
@@ -100,7 +109,7 @@ BEGIN_TEST(testWeakMap_keyDelegates)
|
||||
|
||||
/* Check the delegate keeps the entry alive even if the key is not reachable. */
|
||||
key = nullptr;
|
||||
CHECK(newCCW(map, delegate));
|
||||
CHECK(newCCW(map, delegateRoot));
|
||||
budget = js::SliceBudget(js::WorkBudget(100000));
|
||||
rt->gc.startDebugGC(GC_NORMAL, budget);
|
||||
CHECK(!JS::IsIncrementalGCInProgress(rt));
|
||||
@@ -108,14 +117,14 @@ BEGIN_TEST(testWeakMap_keyDelegates)
|
||||
|
||||
/*
|
||||
* Check that the zones finished marking at the same time, which is
|
||||
* neccessary because of the presence of the delegate and the CCW.
|
||||
* necessary because of the presence of the delegate and the CCW.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
CHECK(map->zone()->lastZoneGroupIndex() == delegate->zone()->lastZoneGroupIndex());
|
||||
CHECK(map->zone()->lastZoneGroupIndex() == delegateRoot->zone()->lastZoneGroupIndex());
|
||||
#endif
|
||||
|
||||
/* Check that when the delegate becomes unreachable the entry is removed. */
|
||||
delegate = nullptr;
|
||||
delegateRoot = nullptr;
|
||||
keyDelegate = nullptr;
|
||||
JS_GC(rt);
|
||||
CHECK(checkSize(map, 0));
|
||||
|
||||
@@ -1018,6 +1018,15 @@ GetPCCountScriptSummary(JSContext* cx, size_t script);
|
||||
JS_FRIEND_API(JSString*)
|
||||
GetPCCountScriptContents(JSContext* cx, size_t script);
|
||||
|
||||
// Generate lcov trace file content for the current compartment, and allocate a
|
||||
// new buffer and return the content in it, the size of the newly allocated
|
||||
// content within the buffer would be set to the length out-param.
|
||||
//
|
||||
// In case of out-of-memory, this function returns nullptr and does not set any
|
||||
// value to the length out-param.
|
||||
JS_FRIEND_API(char*)
|
||||
GetCodeCoverageSummary(JSContext* cx, size_t* length);
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
ContextHasOutstandingRequests(const JSContext* cx);
|
||||
|
||||
|
||||
+76
-34
@@ -1144,7 +1144,7 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
||||
markingValidator(nullptr),
|
||||
#endif
|
||||
interFrameGC(false),
|
||||
sliceBudget(SliceBudget::Unlimited),
|
||||
defaultTimeBudget_(SliceBudget::UnlimitedTimeBudget),
|
||||
incrementalAllowed(true),
|
||||
generationalDisabled(0),
|
||||
compactingEnabled(true),
|
||||
@@ -1396,7 +1396,7 @@ GCRuntime::setParameter(JSGCParamKey key, uint32_t value)
|
||||
zone->setGCMaxMallocBytes(maxMallocBytesAllocated() * 0.9);
|
||||
break;
|
||||
case JSGC_SLICE_TIME_BUDGET:
|
||||
sliceBudget = value ? value : SliceBudget::Unlimited;
|
||||
defaultTimeBudget_ = value ? value : SliceBudget::UnlimitedTimeBudget;
|
||||
break;
|
||||
case JSGC_MARK_STACK_LIMIT:
|
||||
setMarkStackLimit(value);
|
||||
@@ -1502,7 +1502,13 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock)
|
||||
availableChunks(lock).count() +
|
||||
emptyChunks(lock).count());
|
||||
case JSGC_SLICE_TIME_BUDGET:
|
||||
return uint32_t(sliceBudget > 0 ? sliceBudget : 0);
|
||||
if (defaultTimeBudget_ == SliceBudget::UnlimitedTimeBudget) {
|
||||
return 0;
|
||||
} else {
|
||||
MOZ_RELEASE_ASSERT(defaultTimeBudget_ >= 0);
|
||||
MOZ_RELEASE_ASSERT(defaultTimeBudget_ < UINT32_MAX);
|
||||
return uint32_t(defaultTimeBudget_);
|
||||
}
|
||||
case JSGC_MARK_STACK_LIMIT:
|
||||
return marker.maxCapacity();
|
||||
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
|
||||
@@ -2763,8 +2769,8 @@ ArenaLists::forceFinalizeNow(FreeOp* fop, AllocKind thingKind, KeepArenasEnum ke
|
||||
size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
|
||||
SortedArenaList finalizedSorted(thingsPerArena);
|
||||
|
||||
SliceBudget budget;
|
||||
FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, budget, keepArenas);
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, unlimited, keepArenas);
|
||||
MOZ_ASSERT(!arenas);
|
||||
|
||||
if (empty) {
|
||||
@@ -2832,8 +2838,8 @@ ArenaLists::backgroundFinalize(FreeOp* fop, ArenaHeader* listHead, ArenaHeader**
|
||||
size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
|
||||
SortedArenaList finalizedSorted(thingsPerArena);
|
||||
|
||||
SliceBudget budget;
|
||||
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, KEEP_ARENAS);
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, unlimited, KEEP_ARENAS);
|
||||
MOZ_ASSERT(!listHead);
|
||||
|
||||
finalizedSorted.extractEmpty(empty);
|
||||
@@ -2951,13 +2957,13 @@ GCRuntime::refillFreeListInGC(Zone* zone, AllocKind thingKind)
|
||||
}
|
||||
|
||||
SliceBudget::SliceBudget()
|
||||
: timeBudget(Unlimited), workBudget(Unlimited)
|
||||
: timeBudget(UnlimitedTimeBudget), workBudget(UnlimitedWorkBudget)
|
||||
{
|
||||
makeUnlimited();
|
||||
}
|
||||
|
||||
SliceBudget::SliceBudget(TimeBudget time)
|
||||
: timeBudget(time), workBudget(Unlimited)
|
||||
: timeBudget(time), workBudget(UnlimitedWorkBudget)
|
||||
{
|
||||
if (time.budget < 0) {
|
||||
makeUnlimited();
|
||||
@@ -2969,7 +2975,7 @@ SliceBudget::SliceBudget(TimeBudget time)
|
||||
}
|
||||
|
||||
SliceBudget::SliceBudget(WorkBudget work)
|
||||
: timeBudget(Unlimited), workBudget(work)
|
||||
: timeBudget(UnlimitedTimeBudget), workBudget(work)
|
||||
{
|
||||
if (work.budget < 0) {
|
||||
makeUnlimited();
|
||||
@@ -3978,11 +3984,19 @@ GCRuntime::markWeakReferences(gcstats::Phase phase)
|
||||
|
||||
gcstats::AutoPhase ap1(stats, phase);
|
||||
|
||||
marker.enterWeakMarkingMode();
|
||||
|
||||
// TODO bug 1167452: Make weak marking incremental
|
||||
SliceBudget budget = SliceBudget::unlimited();
|
||||
marker.drainMarkStack(budget);
|
||||
|
||||
for (;;) {
|
||||
bool markedAny = false;
|
||||
for (CompartmentIterT c(rt); !c.done(); c.next()) {
|
||||
markedAny |= WatchpointMap::markCompartmentIteratively(c, &marker);
|
||||
markedAny |= WeakMapBase::markCompartmentIteratively(c, &marker);
|
||||
if (c->watchpointMap)
|
||||
markedAny |= c->watchpointMap->markIteratively(&marker);
|
||||
if (marker.weakMapAction() != ExpandWeakMaps)
|
||||
markedAny |= WeakMapBase::markCompartmentIteratively(c, &marker);
|
||||
}
|
||||
markedAny |= Debugger::markAllIteratively(&marker);
|
||||
markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
|
||||
@@ -3990,10 +4004,12 @@ GCRuntime::markWeakReferences(gcstats::Phase phase)
|
||||
if (!markedAny)
|
||||
break;
|
||||
|
||||
SliceBudget budget;
|
||||
marker.drainMarkStack(budget);
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
marker.drainMarkStack(unlimited);
|
||||
}
|
||||
MOZ_ASSERT(marker.isDrained());
|
||||
|
||||
marker.leaveWeakMarkingMode();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4015,8 +4031,8 @@ GCRuntime::markGrayReferences(gcstats::Phase phase)
|
||||
if (JSTraceDataOp op = grayRootTracer.op)
|
||||
(*op)(&marker, grayRootTracer.data);
|
||||
}
|
||||
SliceBudget budget;
|
||||
marker.drainMarkStack(budget);
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
marker.drainMarkStack(unlimited);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -4136,12 +4152,23 @@ js::gc::MarkingValidator::nonIncrementalMark()
|
||||
return;
|
||||
}
|
||||
|
||||
gc::WeakKeyTable savedWeakKeys;
|
||||
if (!savedWeakKeys.init())
|
||||
return;
|
||||
|
||||
for (gc::WeakKeyTable::Range r = gc->marker.weakKeys.all(); !r.empty(); r.popFront()) {
|
||||
if (!savedWeakKeys.put(Move(r.front().key), Move(r.front().value)))
|
||||
CrashAtUnhandlableOOM("saving weak keys table for validator");
|
||||
}
|
||||
|
||||
/*
|
||||
* After this point, the function should run to completion, so we shouldn't
|
||||
* do anything fallible.
|
||||
*/
|
||||
initialized = true;
|
||||
|
||||
gc->marker.weakKeys.clear();
|
||||
|
||||
/* Re-do all the marking, but non-incrementally. */
|
||||
js::gc::State state = gc->incrementalState;
|
||||
gc->incrementalState = MARK_ROOTS;
|
||||
@@ -4164,9 +4191,9 @@ js::gc::MarkingValidator::nonIncrementalMark()
|
||||
|
||||
gc->markRuntime(gcmarker, GCRuntime::MarkRuntime);
|
||||
|
||||
SliceBudget budget;
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
gc->incrementalState = MARK;
|
||||
gc->marker.drainMarkStack(budget);
|
||||
gc->marker.drainMarkStack(unlimited);
|
||||
}
|
||||
|
||||
gc->incrementalState = SWEEP;
|
||||
@@ -4204,7 +4231,13 @@ js::gc::MarkingValidator::nonIncrementalMark()
|
||||
|
||||
for (GCCompartmentsIter c(runtime); !c.done(); c.next())
|
||||
WeakMapBase::unmarkCompartment(c);
|
||||
WeakMapBase::restoreCompartmentMarkedWeakMaps(markedWeakMaps);
|
||||
WeakMapBase::restoreMarkedWeakMaps(markedWeakMaps);
|
||||
|
||||
gc->marker.weakKeys.clear();
|
||||
for (gc::WeakKeyTable::Range r = savedWeakKeys.all(); !r.empty(); r.popFront()) {
|
||||
if (!gc->marker.weakKeys.put(Move(r.front().key), Move(r.front().value)))
|
||||
CrashAtUnhandlableOOM("restoring weak keys table for validator");
|
||||
}
|
||||
|
||||
gc->incrementalState = state;
|
||||
}
|
||||
@@ -4614,8 +4647,8 @@ MarkIncomingCrossCompartmentPointers(JSRuntime* rt, const uint32_t color)
|
||||
c->gcIncomingGrayPointers = nullptr;
|
||||
}
|
||||
|
||||
SliceBudget budget;
|
||||
rt->gc.marker.drainMarkStack(budget);
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
rt->gc.marker.drainMarkStack(unlimited);
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -4711,7 +4744,6 @@ GCRuntime::endMarkingZoneGroup()
|
||||
* black by the action of UnmarkGray.
|
||||
*/
|
||||
MarkIncomingCrossCompartmentPointers(rt, BLACK);
|
||||
|
||||
markWeakReferencesInCurrentGroup(gcstats::PHASE_SWEEP_MARK_WEAK);
|
||||
|
||||
/*
|
||||
@@ -4872,6 +4904,17 @@ GCRuntime::beginSweepingZoneGroup()
|
||||
|
||||
validateIncrementalMarking();
|
||||
|
||||
/* Clear out this zone group's keys from the weakKeys table, to prevent later accesses. */
|
||||
for (WeakKeyTable::Range r = marker.weakKeys.all(); !r.empty(); ) {
|
||||
auto key(r.front().key);
|
||||
r.popFront();
|
||||
if (gc::TenuredCell::fromPointer(key.asCell())->zone()->isGCSweeping()) {
|
||||
bool found;
|
||||
marker.weakKeys.remove(key, &found);
|
||||
MOZ_ASSERT(found);
|
||||
}
|
||||
}
|
||||
|
||||
FreeOp fop(rt);
|
||||
SweepAtomsTask sweepAtomsTask(rt);
|
||||
SweepInnerViewsTask sweepInnerViewsTask(rt);
|
||||
@@ -5615,8 +5658,8 @@ GCRuntime::resetIncrementalGC(const char* reason)
|
||||
bool wasCompacting = isCompacting;
|
||||
isCompacting = false;
|
||||
|
||||
SliceBudget budget;
|
||||
incrementalCollectSlice(budget, JS::gcreason::RESET);
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
incrementalCollectSlice(unlimited, JS::gcreason::RESET);
|
||||
|
||||
isCompacting = wasCompacting;
|
||||
|
||||
@@ -5639,8 +5682,8 @@ GCRuntime::resetIncrementalGC(const char* reason)
|
||||
startedCompacting = true;
|
||||
zonesToMaybeCompact.clear();
|
||||
|
||||
SliceBudget budget;
|
||||
incrementalCollectSlice(budget, JS::gcreason::RESET);
|
||||
auto unlimited = SliceBudget::unlimited();
|
||||
incrementalCollectSlice(unlimited, JS::gcreason::RESET);
|
||||
|
||||
isCompacting = wasCompacting;
|
||||
break;
|
||||
@@ -6193,11 +6236,11 @@ GCRuntime::defaultBudget(JS::gcreason::Reason reason, int64_t millis)
|
||||
{
|
||||
if (millis == 0) {
|
||||
if (reason == JS::gcreason::ALLOC_TRIGGER)
|
||||
millis = sliceBudget;
|
||||
millis = defaultSliceBudget();
|
||||
else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled())
|
||||
millis = sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
|
||||
millis = defaultSliceBudget() * IGC_MARK_SLICE_MULTIPLIER;
|
||||
else
|
||||
millis = sliceBudget;
|
||||
millis = defaultSliceBudget();
|
||||
}
|
||||
|
||||
return SliceBudget(TimeBudget(millis));
|
||||
@@ -6207,7 +6250,7 @@ void
|
||||
GCRuntime::gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
|
||||
{
|
||||
invocationKind = gckind;
|
||||
collect(false, SliceBudget(), reason);
|
||||
collect(false, SliceBudget::unlimited(), reason);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -6242,7 +6285,7 @@ GCRuntime::finishGC(JS::gcreason::Reason reason)
|
||||
isCompacting = false;
|
||||
}
|
||||
|
||||
collect(true, SliceBudget(), reason);
|
||||
collect(true, SliceBudget::unlimited(), reason);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -6256,9 +6299,8 @@ GCRuntime::abortGC()
|
||||
|
||||
AutoStopVerifyingBarriers av(rt, false);
|
||||
|
||||
SliceBudget unlimited;
|
||||
gcstats::AutoGCSlice agc(stats, scanZonesBeforeGC(), invocationKind,
|
||||
unlimited, JS::gcreason::ABORT_GC);
|
||||
SliceBudget::unlimited(), JS::gcreason::ABORT_GC);
|
||||
|
||||
evictNursery(JS::gcreason::ABORT_GC);
|
||||
AutoDisableStoreBuffer adsb(this);
|
||||
@@ -6609,7 +6651,7 @@ GCRuntime::runDebugGC()
|
||||
|
||||
PrepareForDebugGC(rt);
|
||||
|
||||
SliceBudget budget;
|
||||
auto budget = SliceBudget::unlimited();
|
||||
if (type == ZealIncrementalRootsThenFinish ||
|
||||
type == ZealIncrementalMarkAllThenFinish ||
|
||||
type == ZealIncrementalMultipleSlices)
|
||||
|
||||
+360
-42
@@ -10,9 +10,13 @@
|
||||
|
||||
#include "jsopcodeinlines.h"
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
#include "mozilla/SizePrintfMacros.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -170,7 +174,8 @@ js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp)
|
||||
return;
|
||||
|
||||
Sprint(sp, " {");
|
||||
double val = script->getPCCounts(pc).numExec();
|
||||
PCCounts* counts = script->maybeGetPCCounts(pc);
|
||||
double val = counts ? counts->numExec() : 0.0;
|
||||
if (val)
|
||||
Sprint(sp, "\"%s\": %.0f", PCCounts::numExecName, val);
|
||||
Sprint(sp, "}\n");
|
||||
@@ -1611,11 +1616,6 @@ ReleaseScriptCounts(FreeOp* fop)
|
||||
JSRuntime* rt = fop->runtime();
|
||||
MOZ_ASSERT(rt->scriptAndCountsVector);
|
||||
|
||||
ScriptAndCountsVector& vec = *rt->scriptAndCountsVector;
|
||||
|
||||
for (size_t i = 0; i < vec.length(); i++)
|
||||
vec[i].scriptCounts.destroy(fop);
|
||||
|
||||
fop->delete_(rt->scriptAndCountsVector);
|
||||
rt->scriptAndCountsVector = nullptr;
|
||||
}
|
||||
@@ -1647,19 +1647,17 @@ js::StopPCCountProfiling(JSContext* cx)
|
||||
|
||||
ReleaseAllJITCode(rt->defaultFreeOp());
|
||||
|
||||
ScriptAndCountsVector* vec = cx->new_<ScriptAndCountsVector>(SystemAllocPolicy());
|
||||
auto* vec = cx->new_<PersistentRooted<ScriptAndCountsVector>>(cx,
|
||||
ScriptAndCountsVector(SystemAllocPolicy()));
|
||||
if (!vec)
|
||||
return;
|
||||
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
JSScript* script = i.get<JSScript>();
|
||||
if (script->hasScriptCounts() && script->types()) {
|
||||
ScriptAndCounts sac;
|
||||
sac.script = script;
|
||||
sac.scriptCounts.set(script->releaseScriptCounts());
|
||||
if (!vec->append(sac))
|
||||
sac.scriptCounts.destroy(rt->defaultFreeOp());
|
||||
if (!vec->append(script))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1704,20 +1702,6 @@ AppendJSONProperty(StringBuffer& buf, const char* name, MaybeComma comma = COMMA
|
||||
buf.append("\":", 2);
|
||||
}
|
||||
|
||||
static void
|
||||
AppendArrayJSONProperties(JSContext* cx, StringBuffer& buf,
|
||||
double* values, const char * const* names, unsigned count,
|
||||
MaybeComma& comma)
|
||||
{
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (values[i]) {
|
||||
AppendJSONProperty(buf, names[i], comma);
|
||||
comma = COMMA;
|
||||
NumberValueToStringBuffer(cx, DoubleValue(values[i]), buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSString*)
|
||||
js::GetPCCountScriptSummary(JSContext* cx, size_t index)
|
||||
{
|
||||
@@ -1759,21 +1743,21 @@ js::GetPCCountScriptSummary(JSContext* cx, size_t index)
|
||||
}
|
||||
}
|
||||
|
||||
double total = 0.0;
|
||||
uint64_t total = 0;
|
||||
|
||||
jsbytecode* codeEnd = script->codeEnd();
|
||||
for (jsbytecode* pc = script->code(); pc < codeEnd; pc = GetNextPc(pc)) {
|
||||
PCCounts& counts = sac.getPCCounts(pc);
|
||||
double value = counts.numExec();
|
||||
total += value;
|
||||
const PCCounts* counts = sac.maybeGetPCCounts(pc);
|
||||
if (!counts)
|
||||
continue;
|
||||
total += counts->numExec();
|
||||
}
|
||||
|
||||
AppendJSONProperty(buf, "totals");
|
||||
buf.append('{');
|
||||
|
||||
MaybeComma comma = NO_COMMA;
|
||||
|
||||
AppendArrayJSONProperties(cx, buf, &total, &PCCounts::numExecName, 1, comma);
|
||||
AppendJSONProperty(buf, PCCounts::numExecName, NO_COMMA);
|
||||
NumberValueToStringBuffer(cx, DoubleValue(total), buf);
|
||||
|
||||
uint64_t ionActivity = 0;
|
||||
jit::IonScriptCounts* ionCounts = sac.getIonCounts();
|
||||
@@ -1783,7 +1767,7 @@ js::GetPCCountScriptSummary(JSContext* cx, size_t index)
|
||||
ionCounts = ionCounts->previous();
|
||||
}
|
||||
if (ionActivity) {
|
||||
AppendJSONProperty(buf, "ion", comma);
|
||||
AppendJSONProperty(buf, "ion", COMMA);
|
||||
NumberValueToStringBuffer(cx, DoubleValue(ionActivity), buf);
|
||||
}
|
||||
|
||||
@@ -1818,11 +1802,18 @@ GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf)
|
||||
bool comma = false;
|
||||
|
||||
SrcNoteLineScanner scanner(script->notes(), script->lineno());
|
||||
uint64_t hits = 0;
|
||||
|
||||
for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
|
||||
jsbytecode* end = script->codeEnd();
|
||||
for (jsbytecode* pc = script->code(); pc < end; pc = GetNextPc(pc)) {
|
||||
size_t offset = script->pcToOffset(pc);
|
||||
JSOp op = JSOp(*pc);
|
||||
|
||||
JSOp op = (JSOp) *pc;
|
||||
// If the current instruction is a jump target,
|
||||
// then update the number of hits.
|
||||
const PCCounts* counts = sac.maybeGetPCCounts(pc);
|
||||
if (counts)
|
||||
hits = counts->numExec();
|
||||
|
||||
if (comma)
|
||||
buf.append(',');
|
||||
@@ -1863,19 +1854,22 @@ GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf)
|
||||
buf.append(str);
|
||||
}
|
||||
|
||||
PCCounts& counts = sac.getPCCounts(pc);
|
||||
|
||||
AppendJSONProperty(buf, "counts");
|
||||
buf.append('{');
|
||||
|
||||
double value = counts.numExec();
|
||||
if (value > 0) {
|
||||
if (hits > 0) {
|
||||
AppendJSONProperty(buf, PCCounts::numExecName, NO_COMMA);
|
||||
NumberValueToStringBuffer(cx, DoubleValue(value), buf);
|
||||
NumberValueToStringBuffer(cx, DoubleValue(hits), buf);
|
||||
}
|
||||
|
||||
buf.append('}');
|
||||
buf.append('}');
|
||||
|
||||
// If the current instruction has thrown,
|
||||
// then decrement the hit counts with the number of throws.
|
||||
counts = sac.maybeGetThrowCounts(pc);
|
||||
if (counts)
|
||||
hits -= counts->numExec();
|
||||
}
|
||||
|
||||
buf.append(']');
|
||||
@@ -1954,3 +1948,327 @@ js::GetPCCountScriptContents(JSContext* cx, size_t index)
|
||||
|
||||
return buf.finishString();
|
||||
}
|
||||
|
||||
static bool
|
||||
LcovWriteScriptName(GenericPrinter& out, JSScript* script)
|
||||
{
|
||||
JSFunction* fun = script->functionNonDelazifying();
|
||||
if (fun && fun->displayAtom())
|
||||
return EscapedStringPrinter(out, fun->displayAtom(), 0);
|
||||
out.printf("top-level");
|
||||
return true;
|
||||
}
|
||||
|
||||
struct LcovSourceFile
|
||||
{
|
||||
const char* filename;
|
||||
|
||||
LSprinter outFN;
|
||||
LSprinter outFNDA;
|
||||
size_t numFunctionsFound;
|
||||
size_t numFunctionsHit;
|
||||
|
||||
LSprinter outBRDA;
|
||||
size_t numBranchesFound;
|
||||
size_t numBranchesHit;
|
||||
|
||||
LSprinter outDA;
|
||||
size_t numLinesInstrumented;
|
||||
size_t numLinesHit;
|
||||
|
||||
LcovSourceFile(LifoAlloc* alloc, JSScript *script)
|
||||
: filename(script->filename()),
|
||||
outFN(alloc),
|
||||
outFNDA(alloc),
|
||||
numFunctionsFound(0),
|
||||
numFunctionsHit(0),
|
||||
outBRDA(alloc),
|
||||
numBranchesFound(0),
|
||||
numBranchesHit(0),
|
||||
outDA(alloc),
|
||||
numLinesInstrumented(0),
|
||||
numLinesHit(0)
|
||||
{ }
|
||||
};
|
||||
|
||||
static bool
|
||||
LcovWriteScript(JSContext* cx, LcovSourceFile& lsf, JSScript* script)
|
||||
{
|
||||
lsf.numFunctionsFound++;
|
||||
lsf.outFN.printf("FN:%d,", script->lineno());
|
||||
if (!LcovWriteScriptName(lsf.outFN, script))
|
||||
return false;
|
||||
lsf.outFN.put("\n", 1);
|
||||
|
||||
uint64_t hits = 0;
|
||||
ScriptCounts* sc = nullptr;
|
||||
if (script->hasScriptCounts()) {
|
||||
sc = &script->getScriptCounts();
|
||||
lsf.numFunctionsHit++;
|
||||
const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(script->main()));
|
||||
lsf.outFNDA.printf("FNDA:%" PRIu64 ",", counts->numExec());
|
||||
if (!LcovWriteScriptName(lsf.outFNDA, script))
|
||||
return false;
|
||||
lsf.outFNDA.put("\n", 1);
|
||||
|
||||
// Set the hit count of the pre-main code to 1, if the function ever got
|
||||
// visited.
|
||||
hits = 1;
|
||||
}
|
||||
|
||||
jsbytecode* snpc = script->code();
|
||||
jssrcnote* sn = script->notes();
|
||||
if (!SN_IS_TERMINATOR(sn))
|
||||
snpc += SN_DELTA(sn);
|
||||
|
||||
size_t lineno = script->lineno();
|
||||
jsbytecode* end = script->codeEnd();
|
||||
size_t blockId = 0;
|
||||
for (jsbytecode* pc = script->code(); pc != end; pc = GetNextPc(pc)) {
|
||||
JSOp op = JSOp(*pc);
|
||||
bool jump = IsJumpOpcode(op);
|
||||
bool fallsthrough = BytecodeFallsThrough(op);
|
||||
|
||||
// If the current script & pc has a hit-count report, then update the
|
||||
// current number of hits.
|
||||
if (sc) {
|
||||
const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(pc));
|
||||
if (counts)
|
||||
hits = counts->numExec();
|
||||
}
|
||||
|
||||
// If we have additional source notes, walk all the source notes of the
|
||||
// current pc.
|
||||
if (snpc <= pc) {
|
||||
size_t oldLine = lineno;
|
||||
while (!SN_IS_TERMINATOR(sn) && snpc <= pc) {
|
||||
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
||||
if (type == SRC_SETLINE)
|
||||
lineno = size_t(GetSrcNoteOffset(sn, 0));
|
||||
else if (type == SRC_NEWLINE)
|
||||
lineno++;
|
||||
|
||||
sn = SN_NEXT(sn);
|
||||
snpc += SN_DELTA(sn);
|
||||
}
|
||||
|
||||
if (oldLine != lineno && fallsthrough) {
|
||||
lsf.outDA.printf("DA:%d,%" PRIu64 "\n", lineno, hits);
|
||||
|
||||
// Count the number of lines instrumented & hit.
|
||||
lsf.numLinesInstrumented++;
|
||||
if (hits)
|
||||
lsf.numLinesHit++;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current instruction has thrown, then decrement the hit counts
|
||||
// with the number of throws.
|
||||
if (sc) {
|
||||
const PCCounts* counts = sc->maybeGetThrowCounts(script->pcToOffset(pc));
|
||||
if (counts)
|
||||
hits -= counts->numExec();
|
||||
}
|
||||
|
||||
// If the current pc corresponds to a conditional jump instruction, then reports
|
||||
// branch hits.
|
||||
if (jump && fallsthrough) {
|
||||
jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
|
||||
jsbytecode* fallthroughTarget = GetNextPc(pc);
|
||||
uint64_t fallthroughHits = 0;
|
||||
if (sc) {
|
||||
const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(fallthroughTarget));
|
||||
if (counts)
|
||||
fallthroughHits = counts->numExec();
|
||||
}
|
||||
|
||||
size_t targetId = script->pcToOffset(target);
|
||||
uint64_t taken = hits - fallthroughHits;
|
||||
lsf.outBRDA.printf("BRDA:%d,%d,%d,", lineno, blockId, targetId);
|
||||
if (hits)
|
||||
lsf.outBRDA.printf("%d\n", taken);
|
||||
else
|
||||
lsf.outBRDA.put("-\n", 2);
|
||||
|
||||
// Count the number of branches, and the number of branches hit.
|
||||
lsf.numBranchesFound++;
|
||||
if (hits)
|
||||
lsf.numBranchesHit++;
|
||||
|
||||
// Update the blockId when there is a discontinuity.
|
||||
blockId = script->pcToOffset(fallthroughTarget);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GenerateLcovInfo(JSContext* cx, JSCompartment* comp, GenericPrinter& out)
|
||||
{
|
||||
JSRuntime* rt = cx->runtime();
|
||||
|
||||
// Collect the list of scripts which are part of the current compartment.
|
||||
AutoScriptVector topScripts(cx);
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (ZoneCellIter i(zone, AllocKind::SCRIPT); !i.done(); i.next()) {
|
||||
JSScript* script = i.get<JSScript>();
|
||||
if (script->compartment() != comp)
|
||||
continue;
|
||||
|
||||
if (script->functionNonDelazifying())
|
||||
continue;
|
||||
|
||||
if (!topScripts.append(script))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (topScripts.length() == 0)
|
||||
return true;
|
||||
|
||||
// Sort the information to avoid generating multiple file entries, and to
|
||||
// generate functions in the right order.
|
||||
auto lessFun = [](const JSScript* lhs, const JSScript* rhs) -> bool {
|
||||
int d = strcmp(lhs->filename(), rhs->filename());
|
||||
/*
|
||||
This should not be necessary as we are supposed to have only the
|
||||
top-level script.
|
||||
|
||||
d = (d != 0) ? d : lhs->lineno() - rhs->lineno();
|
||||
d = (d != 0) ? d : lhs->column() - rhs->column();
|
||||
*/
|
||||
return d < 0;
|
||||
};
|
||||
std::sort(topScripts.begin(), topScripts.end(), lessFun);
|
||||
|
||||
// lcov trace files are starting with an optional test case name, that we
|
||||
// recycle to be a compartment name.
|
||||
out.put("TN:");
|
||||
if (rt->compartmentNameCallback) {
|
||||
char name[1024];
|
||||
(*rt->compartmentNameCallback)(rt, comp, name, sizeof(name));
|
||||
for (char *s = name; s < name + sizeof(name) && *s; s++) {
|
||||
if (('a' <= *s && *s <= 'z') ||
|
||||
('A' <= *s && *s <= 'Z') ||
|
||||
('0' <= *s && *s <= '9'))
|
||||
{
|
||||
out.put(s, 1);
|
||||
continue;
|
||||
}
|
||||
out.printf("_%p", (void*) size_t(*s));
|
||||
}
|
||||
out.put("\n", 1);
|
||||
} else {
|
||||
out.printf("Compartment_%p%p\n", (void*) size_t('_'), comp);
|
||||
}
|
||||
|
||||
// For each source file
|
||||
LifoAlloc printerAlloc(4096);
|
||||
for (JSScript* topLevel: topScripts) {
|
||||
LifoAllocScope printerScope(&printerAlloc);
|
||||
LcovSourceFile lsf(&printerAlloc, topLevel);
|
||||
|
||||
// We found the top-level script, visit all the functions reachable
|
||||
// from the top-level function.
|
||||
AutoScriptVector queue(cx);
|
||||
if (!queue.append(topLevel))
|
||||
return false;
|
||||
|
||||
RootedScript script(cx);
|
||||
do {
|
||||
script = queue.popCopy();
|
||||
|
||||
// Code the current script before pushing.
|
||||
if (!LcovWriteScript(cx, lsf, script))
|
||||
return false;
|
||||
|
||||
// Iterate from the last to the first object in order to have
|
||||
// the functions them visited in the opposite order when popping
|
||||
// elements from the stack of remaining scripts, such that the
|
||||
// functions are more-less listed with increasing line numbers.
|
||||
if (!script->hasObjects())
|
||||
continue;
|
||||
size_t idx = script->objects()->length;
|
||||
while (idx--) {
|
||||
JSObject* obj = script->getObject(idx);
|
||||
|
||||
// Only continue on JSFunction objects.
|
||||
if (!obj->is<JSFunction>())
|
||||
continue;
|
||||
JSFunction& fun = obj->as<JSFunction>();
|
||||
|
||||
// Let's skip asm.js for now.
|
||||
if (!fun.isInterpreted())
|
||||
continue;
|
||||
|
||||
// Queue the script in the list of script associated to the
|
||||
// current source.
|
||||
if (!queue.append(fun.getOrCreateScript(cx)))
|
||||
return false;
|
||||
}
|
||||
} while (!queue.empty());
|
||||
|
||||
if (lsf.outFN.hadOutOfMemory() ||
|
||||
lsf.outFNDA.hadOutOfMemory() ||
|
||||
lsf.outBRDA.hadOutOfMemory() ||
|
||||
lsf.outDA.hadOutOfMemory())
|
||||
{
|
||||
out.reportOutOfMemory();
|
||||
return false;
|
||||
}
|
||||
|
||||
out.printf("SF:%s\n", lsf.filename);
|
||||
|
||||
lsf.outFN.exportInto(out);
|
||||
lsf.outFNDA.exportInto(out);
|
||||
out.printf("FNF:%d\n", lsf.numFunctionsFound);
|
||||
out.printf("FNH:%d\n", lsf.numFunctionsHit);
|
||||
|
||||
lsf.outBRDA.exportInto(out);
|
||||
out.printf("BRF:%d\n", lsf.numBranchesFound);
|
||||
out.printf("BRH:%d\n", lsf.numBranchesHit);
|
||||
|
||||
lsf.outDA.exportInto(out);
|
||||
out.printf("LF:%d\n", lsf.numLinesInstrumented);
|
||||
out.printf("LH:%d\n", lsf.numLinesHit);
|
||||
|
||||
out.put("end_of_record\n");
|
||||
}
|
||||
|
||||
if (out.hadOutOfMemory())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(char*)
|
||||
js::GetCodeCoverageSummary(JSContext* cx, size_t* length)
|
||||
{
|
||||
Sprinter out(cx);
|
||||
|
||||
if (!out.init())
|
||||
return nullptr;
|
||||
|
||||
if (!GenerateLcovInfo(cx, cx->compartment(), out)) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (out.hadOutOfMemory()) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ptrdiff_t len = out.stringEnd() - out.string();
|
||||
char* res = cx->pod_malloc<char>(len + 1);
|
||||
if (!res) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
js_memcpy(res, out.string(), len);
|
||||
res[len] = 0;
|
||||
if (length)
|
||||
*length = len;
|
||||
return res;
|
||||
}
|
||||
|
||||
+26
-6
@@ -772,23 +772,43 @@ GetBytecodeInteger(jsbytecode* pc)
|
||||
*/
|
||||
class PCCounts
|
||||
{
|
||||
double numExec_;
|
||||
/*
|
||||
* Offset of the pc inside the script. This fields is used to lookup opcode
|
||||
* which have annotations.
|
||||
*/
|
||||
size_t pcOffset_;
|
||||
|
||||
/*
|
||||
* Record the number of execution of one instruction, or the number of
|
||||
* throws executed.
|
||||
*/
|
||||
uint64_t numExec_;
|
||||
|
||||
public:
|
||||
explicit PCCounts(size_t off)
|
||||
: pcOffset_(off),
|
||||
numExec_(0)
|
||||
{}
|
||||
|
||||
double& numExec() {
|
||||
size_t pcOffset() const {
|
||||
return pcOffset_;
|
||||
}
|
||||
|
||||
// Used for sorting and searching.
|
||||
bool operator<(const PCCounts& rhs) const {
|
||||
return pcOffset_ < rhs.pcOffset_;
|
||||
}
|
||||
|
||||
uint64_t& numExec() {
|
||||
return numExec_;
|
||||
}
|
||||
double numExec() const {
|
||||
uint64_t numExec() const {
|
||||
return numExec_;
|
||||
}
|
||||
|
||||
static const char* numExecName;
|
||||
};
|
||||
|
||||
/* Necessary for alignment with the script. */
|
||||
JS_STATIC_ASSERT(sizeof(PCCounts) % sizeof(Value) == 0);
|
||||
|
||||
static inline jsbytecode*
|
||||
GetNextPc(jsbytecode* pc)
|
||||
{
|
||||
|
||||
+125
-30
@@ -14,7 +14,9 @@
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
|
||||
#include "jsapi.h"
|
||||
@@ -1279,38 +1281,79 @@ JSScript::initScriptCounts(JSContext* cx)
|
||||
{
|
||||
MOZ_ASSERT(!hasScriptCounts());
|
||||
|
||||
// We allocate one PCCounts for each offset, instead of for each bytecode,
|
||||
// because the pcCountsVector maps an offset to a PCCounts structure.
|
||||
size_t nbytes = length() * sizeof(PCCounts);
|
||||
// Record all pc which are the first instruction of a basic block.
|
||||
mozilla::Vector<jsbytecode*, 16, SystemAllocPolicy> jumpTargets;
|
||||
jsbytecode* end = codeEnd();
|
||||
jsbytecode* mainEntry = main();
|
||||
for (jsbytecode* pc = code(); pc != end; pc = GetNextPc(pc)) {
|
||||
if (pc == mainEntry) {
|
||||
if (!jumpTargets.append(pc))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool jump = IsJumpOpcode(JSOp(*pc));
|
||||
if (jump) {
|
||||
jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
|
||||
if (!jumpTargets.append(target))
|
||||
return false;
|
||||
|
||||
if (BytecodeFallsThrough(JSOp(*pc))) {
|
||||
jsbytecode* fallthrough = GetNextPc(pc);
|
||||
if (!jumpTargets.append(fallthrough))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark catch/finally blocks as being jump targets.
|
||||
if (hasTrynotes()) {
|
||||
JSTryNote* tn = trynotes()->vector;
|
||||
JSTryNote* tnlimit = tn + trynotes()->length;
|
||||
for (; tn < tnlimit; tn++) {
|
||||
jsbytecode* tryStart = mainEntry + tn->start;
|
||||
jsbytecode* tryPc = tryStart - 1;
|
||||
if (JSOp(*tryPc) != JSOP_TRY)
|
||||
continue;
|
||||
|
||||
jsbytecode* tryTarget = tryStart + tn->length;
|
||||
if (!jumpTargets.append(tryTarget))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all pc, and remove duplicates.
|
||||
std::sort(jumpTargets.begin(), jumpTargets.end());
|
||||
auto last = std::unique(jumpTargets.begin(), jumpTargets.end());
|
||||
jumpTargets.erase(last, jumpTargets.end());
|
||||
|
||||
// Initialize all PCCounts counters to 0.
|
||||
uint8_t* base = zone()->pod_calloc<uint8_t>(nbytes);
|
||||
if (!base)
|
||||
ScriptCounts::PCCountsVector base;
|
||||
if (!base.reserve(jumpTargets.length()))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < jumpTargets.length(); i++)
|
||||
MOZ_ALWAYS_TRUE(base.emplaceBack(pcToOffset(jumpTargets[i])));
|
||||
|
||||
// Create compartment's scriptCountsMap if necessary.
|
||||
ScriptCountsMap* map = compartment()->scriptCountsMap;
|
||||
if (!map) {
|
||||
map = cx->new_<ScriptCountsMap>();
|
||||
if (!map || !map->init()) {
|
||||
js_free(base);
|
||||
js_delete(map);
|
||||
return false;
|
||||
}
|
||||
compartment()->scriptCountsMap = map;
|
||||
}
|
||||
|
||||
ScriptCounts scriptCounts;
|
||||
scriptCounts.pcCountsVector = (PCCounts*) base;
|
||||
|
||||
// Register the current ScriptCount in the compartment's map.
|
||||
if (!map->putNew(this, scriptCounts)) {
|
||||
js_free(base);
|
||||
if (!map->putNew(this, Move(base)))
|
||||
return false;
|
||||
}
|
||||
hasScriptCounts_ = true; // safe to set this; we can't fail after this point
|
||||
|
||||
/* Enable interrupts in any interpreter frames running on this script. */
|
||||
// safe to set this; we can't fail after this point.
|
||||
hasScriptCounts_ = true;
|
||||
|
||||
// Enable interrupts in any interpreter frames running on this script. This
|
||||
// is used to let the interpreter increment the PCCounts, if present.
|
||||
for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
|
||||
if (iter->isInterpreter())
|
||||
iter->asInterpreter()->enableInterruptsIfRunning(this);
|
||||
@@ -1328,11 +1371,47 @@ static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript* script)
|
||||
return p;
|
||||
}
|
||||
|
||||
js::PCCounts&
|
||||
JSScript::getPCCounts(jsbytecode* pc) {
|
||||
MOZ_ASSERT(containsPC(pc));
|
||||
ScriptCounts&
|
||||
JSScript::getScriptCounts()
|
||||
{
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
return p->value().pcCountsVector[pcToOffset(pc)];
|
||||
return p->value();
|
||||
}
|
||||
|
||||
js::PCCounts*
|
||||
ScriptCounts::maybeGetPCCounts(size_t offset) {
|
||||
PCCounts searched = PCCounts(offset);
|
||||
PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
|
||||
if (elem == pcCounts_.end() || elem->pcOffset() != offset)
|
||||
return nullptr;
|
||||
return elem;
|
||||
}
|
||||
|
||||
const js::PCCounts*
|
||||
ScriptCounts::maybeGetPCCounts(size_t offset) const {
|
||||
PCCounts searched = PCCounts(offset);
|
||||
const PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
|
||||
if (elem == pcCounts_.end() || elem->pcOffset() != offset)
|
||||
return nullptr;
|
||||
return elem;
|
||||
}
|
||||
|
||||
const js::PCCounts*
|
||||
ScriptCounts::maybeGetThrowCounts(size_t offset) const {
|
||||
PCCounts searched = PCCounts(offset);
|
||||
const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
|
||||
if (elem == throwCounts_.end() || elem->pcOffset() != offset)
|
||||
return nullptr;
|
||||
return elem;
|
||||
}
|
||||
|
||||
js::PCCounts*
|
||||
ScriptCounts::getThrowCounts(size_t offset) {
|
||||
PCCounts searched = PCCounts(offset);
|
||||
PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
|
||||
if (elem == throwCounts_.end() || elem->pcOffset() != offset)
|
||||
elem = throwCounts_.insert(elem, searched);
|
||||
return elem;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1346,38 +1425,54 @@ JSScript::setIonScript(JSContext* maybecx, js::jit::IonScript* ionScript)
|
||||
updateBaselineOrIonRaw(maybecx);
|
||||
}
|
||||
|
||||
js::PCCounts*
|
||||
JSScript::maybeGetPCCounts(jsbytecode* pc) {
|
||||
MOZ_ASSERT(containsPC(pc));
|
||||
return getScriptCounts().maybeGetPCCounts(pcToOffset(pc));
|
||||
}
|
||||
|
||||
const js::PCCounts*
|
||||
JSScript::maybeGetThrowCounts(jsbytecode* pc) {
|
||||
MOZ_ASSERT(containsPC(pc));
|
||||
return getScriptCounts().maybeGetThrowCounts(pcToOffset(pc));
|
||||
}
|
||||
|
||||
js::PCCounts*
|
||||
JSScript::getThrowCounts(jsbytecode* pc) {
|
||||
MOZ_ASSERT(containsPC(pc));
|
||||
return getScriptCounts().getThrowCounts(pcToOffset(pc));
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
|
||||
{
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
if (p->value().ionCounts)
|
||||
ionCounts->setPrevious(p->value().ionCounts);
|
||||
p->value().ionCounts = ionCounts;
|
||||
ScriptCounts& sc = getScriptCounts();
|
||||
if (sc.ionCounts_)
|
||||
ionCounts->setPrevious(sc.ionCounts_);
|
||||
sc.ionCounts_ = ionCounts;
|
||||
}
|
||||
|
||||
jit::IonScriptCounts*
|
||||
JSScript::getIonCounts()
|
||||
{
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
return p->value().ionCounts;
|
||||
return getScriptCounts().ionCounts_;
|
||||
}
|
||||
|
||||
ScriptCounts
|
||||
JSScript::releaseScriptCounts()
|
||||
void
|
||||
JSScript::releaseScriptCounts(ScriptCounts* counts)
|
||||
{
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
ScriptCounts counts = p->value();
|
||||
*counts = Move(p->value());
|
||||
compartment()->scriptCountsMap->remove(p);
|
||||
hasScriptCounts_ = false;
|
||||
return counts;
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::destroyScriptCounts(FreeOp* fop)
|
||||
{
|
||||
if (hasScriptCounts()) {
|
||||
ScriptCounts scriptCounts = releaseScriptCounts();
|
||||
scriptCounts.destroy(fop);
|
||||
ScriptCounts scriptCounts;
|
||||
releaseScriptCounts(&scriptCounts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-22
@@ -348,28 +348,43 @@ struct GCMethods<Bindings> {
|
||||
|
||||
class ScriptCounts
|
||||
{
|
||||
public:
|
||||
typedef mozilla::Vector<PCCounts, 0, SystemAllocPolicy> PCCountsVector;
|
||||
|
||||
inline ScriptCounts();
|
||||
inline explicit ScriptCounts(PCCountsVector&& jumpTargets);
|
||||
inline explicit ScriptCounts(ScriptCounts&& src);
|
||||
inline ~ScriptCounts();
|
||||
|
||||
inline ScriptCounts& operator=(ScriptCounts&& src);
|
||||
|
||||
// Return the counter used to count the number of visits. Returns null if
|
||||
// the element is not found.
|
||||
PCCounts* maybeGetPCCounts(size_t offset);
|
||||
const PCCounts* maybeGetPCCounts(size_t offset) const;
|
||||
|
||||
// Return the counter used to count the number of throws. Returns null if
|
||||
// the element is not found.
|
||||
const PCCounts* maybeGetThrowCounts(size_t offset) const;
|
||||
|
||||
// Return the counter used to count the number of throws. Allocate it if
|
||||
// none exists yet. Returns null if the allocation failed.
|
||||
PCCounts* getThrowCounts(size_t offset);
|
||||
|
||||
private:
|
||||
friend class ::JSScript;
|
||||
friend struct ScriptAndCounts;
|
||||
|
||||
/*
|
||||
* This points to a single block that holds an array of PCCounts followed
|
||||
* by an array of doubles. Each element in the PCCounts array has a
|
||||
* pointer into the array of doubles.
|
||||
*/
|
||||
PCCounts* pcCountsVector;
|
||||
// This sorted array is used to map an offset to the number of times a
|
||||
// branch got visited.
|
||||
PCCountsVector pcCounts_;
|
||||
|
||||
/* Information about any Ion compilations for the script. */
|
||||
jit::IonScriptCounts* ionCounts;
|
||||
// This sorted vector is used to map an offset to the number of times an
|
||||
// instruction throw.
|
||||
PCCountsVector throwCounts_;
|
||||
|
||||
public:
|
||||
ScriptCounts() : pcCountsVector(nullptr), ionCounts(nullptr) { }
|
||||
|
||||
inline void destroy(FreeOp* fop);
|
||||
|
||||
void set(js::ScriptCounts counts) {
|
||||
pcCountsVector = counts.pcCountsVector;
|
||||
ionCounts = counts.ionCounts;
|
||||
}
|
||||
// Information about any Ion compilations for the script.
|
||||
jit::IonScriptCounts* ionCounts_;
|
||||
};
|
||||
|
||||
typedef HashMap<JSScript*,
|
||||
@@ -1515,10 +1530,13 @@ class JSScript : public js::gc::TenuredCell
|
||||
|
||||
public:
|
||||
bool initScriptCounts(JSContext* cx);
|
||||
js::PCCounts& getPCCounts(jsbytecode* pc);
|
||||
js::ScriptCounts& getScriptCounts();
|
||||
js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
|
||||
const js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
|
||||
js::PCCounts* getThrowCounts(jsbytecode* pc);
|
||||
void addIonCounts(js::jit::IonScriptCounts* ionCounts);
|
||||
js::jit::IonScriptCounts* getIonCounts();
|
||||
js::ScriptCounts releaseScriptCounts();
|
||||
void releaseScriptCounts(js::ScriptCounts* counts);
|
||||
void destroyScriptCounts(js::FreeOp* fop);
|
||||
|
||||
jsbytecode* main() {
|
||||
@@ -2239,12 +2257,22 @@ struct ScriptAndCounts
|
||||
JSScript* script;
|
||||
ScriptCounts scriptCounts;
|
||||
|
||||
PCCounts& getPCCounts(jsbytecode* pc) const {
|
||||
return scriptCounts.pcCountsVector[script->pcToOffset(pc)];
|
||||
inline explicit ScriptAndCounts(JSScript* script);
|
||||
inline explicit ScriptAndCounts(ScriptAndCounts&& sac);
|
||||
|
||||
const PCCounts* maybeGetPCCounts(jsbytecode* pc) const {
|
||||
return scriptCounts.maybeGetPCCounts(script->pcToOffset(pc));
|
||||
}
|
||||
const PCCounts* maybeGetThrowCounts(jsbytecode* pc) const {
|
||||
return scriptCounts.maybeGetThrowCounts(script->pcToOffset(pc));
|
||||
}
|
||||
|
||||
jit::IonScriptCounts* getIonCounts() const {
|
||||
return scriptCounts.ionCounts;
|
||||
return scriptCounts.ionCounts_;
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
TraceRoot(trc, &script, "ScriptAndCounts::script");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -38,11 +38,54 @@ AliasedFormalIter::AliasedFormalIter(JSScript* script)
|
||||
settle();
|
||||
}
|
||||
|
||||
inline void
|
||||
ScriptCounts::destroy(FreeOp* fop)
|
||||
ScriptCounts::ScriptCounts()
|
||||
: pcCounts_(),
|
||||
throwCounts_(),
|
||||
ionCounts_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ScriptCounts::ScriptCounts(PCCountsVector&& jumpTargets)
|
||||
: pcCounts_(Move(jumpTargets)),
|
||||
throwCounts_(),
|
||||
ionCounts_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ScriptCounts::ScriptCounts(ScriptCounts&& src)
|
||||
: pcCounts_(Move(src.pcCounts_)),
|
||||
throwCounts_(Move(src.throwCounts_)),
|
||||
ionCounts_(Move(src.ionCounts_))
|
||||
{
|
||||
src.ionCounts_ = nullptr;
|
||||
}
|
||||
|
||||
ScriptCounts&
|
||||
ScriptCounts::operator=(ScriptCounts&& src)
|
||||
{
|
||||
pcCounts_ = Move(src.pcCounts_);
|
||||
throwCounts_ = Move(src.throwCounts_);
|
||||
ionCounts_ = Move(src.ionCounts_);
|
||||
src.ionCounts_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ScriptCounts::~ScriptCounts()
|
||||
{
|
||||
js_delete(ionCounts_);
|
||||
}
|
||||
|
||||
ScriptAndCounts::ScriptAndCounts(JSScript* script)
|
||||
: script(script),
|
||||
scriptCounts()
|
||||
{
|
||||
script->releaseScriptCounts(&scriptCounts);
|
||||
}
|
||||
|
||||
ScriptAndCounts::ScriptAndCounts(ScriptAndCounts&& sac)
|
||||
: script(Move(sac.script)),
|
||||
scriptCounts(Move(sac.scriptCounts))
|
||||
{
|
||||
fop->free_(pcCountsVector);
|
||||
fop->delete_(ionCounts);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -144,14 +144,6 @@ WatchpointMap::triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, M
|
||||
return handler(cx, obj, id, old, vp.address(), closure);
|
||||
}
|
||||
|
||||
bool
|
||||
WatchpointMap::markCompartmentIteratively(JSCompartment* c, JSTracer* trc)
|
||||
{
|
||||
if (!c->watchpointMap)
|
||||
return false;
|
||||
return c->watchpointMap->markIteratively(trc);
|
||||
}
|
||||
|
||||
bool
|
||||
WatchpointMap::markIteratively(JSTracer* trc)
|
||||
{
|
||||
|
||||
@@ -70,7 +70,6 @@ class WatchpointMap {
|
||||
|
||||
bool triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
|
||||
|
||||
static bool markCompartmentIteratively(JSCompartment* c, JSTracer* trc);
|
||||
bool markIteratively(JSTracer* trc);
|
||||
void markAll(JSTracer* trc);
|
||||
static void sweepAll(JSRuntime* rt);
|
||||
|
||||
+14
-10
@@ -50,22 +50,26 @@ WeakMapBase::trace(JSTracer *tracer)
|
||||
{
|
||||
MOZ_ASSERT(isInList());
|
||||
if (tracer->isMarkingTracer()) {
|
||||
// We don't trace any of the WeakMap entries at this time, just record
|
||||
// record the fact that the WeakMap has been marked. Entries are marked
|
||||
// in the iterative marking phase by markAllIteratively(), which happens
|
||||
// when as many keys as possible have been marked already.
|
||||
MOZ_ASSERT(tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps);
|
||||
marked = true;
|
||||
if (tracer->weakMapAction() == DoNotTraceWeakMaps) {
|
||||
// Do not trace any WeakMap entries at this time. Just record the
|
||||
// fact that the WeakMap has been marked. Entries are marked in the
|
||||
// iterative marking phase by markAllIteratively(), after as many
|
||||
// keys as possible have been marked already.
|
||||
} else {
|
||||
MOZ_ASSERT(tracer->weakMapAction() == ExpandWeakMaps);
|
||||
markEphemeronEntries(tracer);
|
||||
}
|
||||
} else {
|
||||
// If we're not actually doing garbage collection, the keys won't be marked
|
||||
// nicely as needed by the true ephemeral marking algorithm --- custom tracers
|
||||
// such as the cycle collector must use their own means for cycle detection.
|
||||
// So here we do a conservative approximation: pretend all keys are live.
|
||||
if (tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps)
|
||||
if (tracer->weakMapAction() == DoNotTraceWeakMaps)
|
||||
return;
|
||||
|
||||
nonMarkingTraceValues(tracer);
|
||||
if (tracer->eagerlyTraceWeakMaps() == TraceWeakMapKeysValues)
|
||||
if (tracer->weakMapAction() == TraceWeakMapKeysValues)
|
||||
nonMarkingTraceKeys(tracer);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +84,7 @@ WeakMapBase::unmarkCompartment(JSCompartment* c)
|
||||
void
|
||||
WeakMapBase::markAll(JSCompartment* c, JSTracer* tracer)
|
||||
{
|
||||
MOZ_ASSERT(tracer->eagerlyTraceWeakMaps() != DoNotTraceWeakMaps);
|
||||
MOZ_ASSERT(tracer->weakMapAction() != DoNotTraceWeakMaps);
|
||||
for (WeakMapBase* m = c->gcWeakMapList; m; m = m->next) {
|
||||
m->trace(tracer);
|
||||
if (m->memberOf)
|
||||
@@ -158,7 +162,7 @@ WeakMapBase::saveCompartmentMarkedWeakMaps(JSCompartment* c, WeakMapSet& markedW
|
||||
}
|
||||
|
||||
void
|
||||
WeakMapBase::restoreCompartmentMarkedWeakMaps(WeakMapSet& markedWeakMaps)
|
||||
WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps)
|
||||
{
|
||||
for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
|
||||
WeakMapBase* map = r.front();
|
||||
@@ -186,7 +190,7 @@ ObjectValueMap::findZoneEdges()
|
||||
{
|
||||
/*
|
||||
* For unmarked weakmap keys with delegates in a different zone, add a zone
|
||||
* edge to ensure that the delegate zone does finish marking after the key
|
||||
* edge to ensure that the delegate zone finishes marking before the key
|
||||
* zone.
|
||||
*/
|
||||
JS::AutoSuppressGCAnalysis nogc;
|
||||
|
||||
+121
-24
@@ -7,6 +7,8 @@
|
||||
#ifndef jsweakmap_h
|
||||
#define jsweakmap_h
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsobj.h"
|
||||
@@ -39,6 +41,8 @@ typedef HashSet<WeakMapBase*, DefaultHasher<WeakMapBase*>, SystemAllocPolicy> We
|
||||
// Common base class for all WeakMap specializations. The collector uses this to call
|
||||
// their markIteratively and sweep methods.
|
||||
class WeakMapBase {
|
||||
friend void js::GCMarker::enterWeakMarkingMode();
|
||||
|
||||
public:
|
||||
WeakMapBase(JSObject* memOf, JSCompartment* c);
|
||||
virtual ~WeakMapBase();
|
||||
@@ -75,11 +79,17 @@ class WeakMapBase {
|
||||
static bool saveCompartmentMarkedWeakMaps(JSCompartment* c, WeakMapSet& markedWeakMaps);
|
||||
|
||||
// Restore information about which weak maps are marked for many compartments.
|
||||
static void restoreCompartmentMarkedWeakMaps(WeakMapSet& markedWeakMaps);
|
||||
static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
|
||||
|
||||
// Remove a weakmap from its compartment's weakmaps list.
|
||||
static void removeWeakMapFromList(WeakMapBase* weakmap);
|
||||
|
||||
// Any weakmap key types that want to participate in the non-iterative
|
||||
// ephemeron marking must override this method.
|
||||
virtual void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
|
||||
|
||||
virtual void markEphemeronEntries(JSTracer* trc) = 0;
|
||||
|
||||
protected:
|
||||
// Instance member functions called by the above. Instantiations of WeakMap override
|
||||
// these with definitions appropriate for their Key and Value types.
|
||||
@@ -106,6 +116,17 @@ class WeakMapBase {
|
||||
bool marked;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static T extractUnbarriered(BarrieredBase<T> v)
|
||||
{
|
||||
return v.get();
|
||||
}
|
||||
template <typename T>
|
||||
static T* extractUnbarriered(T* v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class Key, class Value,
|
||||
class HashPolicy = DefaultHasher<Key> >
|
||||
class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
|
||||
@@ -154,6 +175,77 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
||||
return p;
|
||||
}
|
||||
|
||||
// The WeakMap and some part of the key are marked. If the entry is marked
|
||||
// according to the exact semantics of this WeakMap, then mark the value.
|
||||
// (For a standard WeakMap, the entry is marked if either the key its
|
||||
// delegate is marked.)
|
||||
void maybeMarkEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr origKey) override
|
||||
{
|
||||
MOZ_ASSERT(marked);
|
||||
|
||||
gc::Cell* l = origKey.asCell();
|
||||
Ptr p = Base::lookup(reinterpret_cast<Lookup&>(l));
|
||||
MOZ_ASSERT(p.found());
|
||||
|
||||
Key key(p->key());
|
||||
if (gc::IsMarked(&key)) {
|
||||
TraceEdge(trc, &p->value(), "ephemeron value");
|
||||
} else if (keyNeedsMark(key)) {
|
||||
TraceEdge(trc, &p->value(), "WeakMap ephemeron value");
|
||||
TraceEdge(trc, &key, "proxy-preserved WeakMap ephemeron key");
|
||||
MOZ_ASSERT(key == p->key()); // No moving
|
||||
}
|
||||
key.unsafeSet(nullptr); // Prevent destructor from running barriers.
|
||||
}
|
||||
|
||||
protected:
|
||||
static void addWeakEntry(JSTracer* trc, JS::GCCellPtr key, gc::WeakMarkable markable)
|
||||
{
|
||||
GCMarker& marker = *static_cast<GCMarker*>(trc);
|
||||
|
||||
auto p = marker.weakKeys.get(key);
|
||||
if (p) {
|
||||
gc::WeakEntryVector& weakEntries = p->value;
|
||||
if (!weakEntries.append(Move(markable)))
|
||||
marker.abortLinearWeakMarking();
|
||||
} else {
|
||||
gc::WeakEntryVector weakEntries;
|
||||
MOZ_ALWAYS_TRUE(weakEntries.append(Move(markable)));
|
||||
if (!marker.weakKeys.put(JS::GCCellPtr(key), Move(weakEntries)))
|
||||
marker.abortLinearWeakMarking();
|
||||
}
|
||||
}
|
||||
|
||||
void markEphemeronEntries(JSTracer* trc) override {
|
||||
MOZ_ASSERT(marked);
|
||||
for (Enum e(*this); !e.empty(); e.popFront()) {
|
||||
Key key(e.front().key());
|
||||
|
||||
// If the entry is live, ensure its key and value are marked.
|
||||
if (gc::IsMarked(&key)) {
|
||||
(void) markValue(trc, &e.front().value());
|
||||
MOZ_ASSERT(key == e.front().key()); // No moving
|
||||
} else if (keyNeedsMark(key)) {
|
||||
TraceEdge(trc, &e.front().value(), "WeakMap entry value");
|
||||
TraceEdge(trc, &key, "proxy-preserved WeakMap entry key");
|
||||
MOZ_ASSERT(key == e.front().key()); // No moving
|
||||
} else if (trc->isWeakMarkingTracer()) {
|
||||
// Entry is not yet known to be live. Record it in the list of
|
||||
// weak keys. Or rather, record this weakmap and the lookup key
|
||||
// so we can repeat the lookup when we need to (to allow
|
||||
// incremental weak marking, we can't just store a pointer to
|
||||
// the entry.) Also record the delegate, if any, because
|
||||
// marking the delegate must also mark the entry.
|
||||
JS::GCCellPtr weakKey(extractUnbarriered(key));
|
||||
gc::WeakMarkable markable(this, weakKey);
|
||||
addWeakEntry(trc, weakKey, markable);
|
||||
if (JSObject* delegate = getDelegate(key))
|
||||
addWeakEntry(trc, JS::GCCellPtr(delegate), markable);
|
||||
}
|
||||
key.unsafeSet(nullptr); // Prevent destructor from running barriers.
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); }
|
||||
void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
|
||||
@@ -166,7 +258,7 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
||||
return true;
|
||||
}
|
||||
|
||||
void nonMarkingTraceKeys(JSTracer* trc) {
|
||||
void nonMarkingTraceKeys(JSTracer* trc) override {
|
||||
for (Enum e(*this); !e.empty(); e.popFront()) {
|
||||
Key key(e.front().key());
|
||||
TraceEdge(trc, &key, "WeakMap entry key");
|
||||
@@ -175,29 +267,34 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
||||
}
|
||||
}
|
||||
|
||||
void nonMarkingTraceValues(JSTracer* trc) {
|
||||
void nonMarkingTraceValues(JSTracer* trc) override {
|
||||
for (Range r = Base::all(); !r.empty(); r.popFront())
|
||||
TraceEdge(trc, &r.front().value(), "WeakMap entry value");
|
||||
}
|
||||
|
||||
bool keyNeedsMark(JSObject* key) {
|
||||
if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
|
||||
JSObject* delegate = op(key);
|
||||
/*
|
||||
* Check if the delegate is marked with any color to properly handle
|
||||
* gray marking when the key's delegate is black and the map is
|
||||
* gray.
|
||||
*/
|
||||
return delegate && gc::IsMarkedUnbarriered(&delegate);
|
||||
}
|
||||
JSObject* getDelegate(JSObject* key) const {
|
||||
JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp;
|
||||
return op ? op(key) : nullptr;
|
||||
}
|
||||
|
||||
JSObject* getDelegate(gc::Cell* cell) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool keyNeedsMark(JSObject* key) const {
|
||||
JSObject* delegate = getDelegate(key);
|
||||
/*
|
||||
* Check if the delegate is marked with any color to properly handle
|
||||
* gray marking when the key's delegate is black and the map is gray.
|
||||
*/
|
||||
return delegate && gc::IsMarkedUnbarriered(&delegate);
|
||||
}
|
||||
|
||||
bool keyNeedsMark(gc::Cell* cell) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool keyNeedsMark(gc::Cell* cell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool markIteratively(JSTracer* trc) {
|
||||
bool markIteratively(JSTracer* trc) override {
|
||||
bool markedAny = false;
|
||||
for (Enum e(*this); !e.empty(); e.popFront()) {
|
||||
/* If the entry is live, ensure its key and value are marked. */
|
||||
@@ -214,17 +311,17 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
||||
entryMoved(e, key);
|
||||
markedAny = true;
|
||||
}
|
||||
key.unsafeSet(nullptr);
|
||||
key.unsafeSet(nullptr); // Prevent destructor from running barriers.
|
||||
}
|
||||
return markedAny;
|
||||
}
|
||||
|
||||
bool findZoneEdges() {
|
||||
bool findZoneEdges() override {
|
||||
// This is overridden by ObjectValueMap.
|
||||
return true;
|
||||
}
|
||||
|
||||
void sweep() {
|
||||
void sweep() override {
|
||||
/* Remove all entries whose keys remain unmarked. */
|
||||
for (Enum e(*this); !e.empty(); e.popFront()) {
|
||||
Key k(e.front().key());
|
||||
@@ -240,12 +337,12 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
||||
assertEntriesNotAboutToBeFinalized();
|
||||
}
|
||||
|
||||
void finish() {
|
||||
void finish() override {
|
||||
Base::finish();
|
||||
}
|
||||
|
||||
/* memberOf can be nullptr, which means that the map is not part of a JSObject. */
|
||||
void traceMappings(WeakMapTracer* tracer) {
|
||||
void traceMappings(WeakMapTracer* tracer) override {
|
||||
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
||||
gc::Cell* key = gc::ToMarkable(r.front().key());
|
||||
gc::Cell* value = gc::ToMarkable(r.front().value());
|
||||
@@ -262,7 +359,7 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
|
||||
e.rekeyFront(k);
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void assertEntriesNotAboutToBeFinalized() {
|
||||
#if DEBUG
|
||||
for (Range r = Base::all(); !r.empty(); r.popFront()) {
|
||||
|
||||
+6
-2
@@ -128,6 +128,7 @@ static JS::PersistentRootedValue gInterruptFunc;
|
||||
static bool gLastWarningEnabled = false;
|
||||
static JS::PersistentRootedValue gLastWarning;
|
||||
|
||||
static bool enableCodeCoverage = false;
|
||||
static bool enableDisassemblyDumps = false;
|
||||
static bool offthreadCompilation = false;
|
||||
static bool enableBaseline = false;
|
||||
@@ -5936,7 +5937,9 @@ SetRuntimeOptions(JSRuntime* rt, const OptionParser& op)
|
||||
reportWarnings = op.getBoolOption('w');
|
||||
compileOnly = op.getBoolOption('c');
|
||||
printTiming = op.getBoolOption('b');
|
||||
rt->profilingScripts = enableDisassemblyDumps = op.getBoolOption('D');
|
||||
enableCodeCoverage = op.getBoolOption("code-coverage");
|
||||
enableDisassemblyDumps = op.getBoolOption('D');
|
||||
rt->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
|
||||
|
||||
jsCacheDir = op.getStringOption("js-cache");
|
||||
if (jsCacheDir) {
|
||||
@@ -5975,7 +5978,7 @@ SetWorkerRuntimeOptions(JSRuntime* rt)
|
||||
.setNativeRegExp(enableNativeRegExp)
|
||||
.setUnboxedArrays(enableUnboxedArrays);
|
||||
rt->setOffthreadIonCompilationEnabled(offthreadCompilation);
|
||||
rt->profilingScripts = enableDisassemblyDumps;
|
||||
rt->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
if (*gZealStr)
|
||||
@@ -6136,6 +6139,7 @@ main(int argc, char** argv, char** envp)
|
||||
"specified by --js-cache. This cache directory will be removed"
|
||||
"when the js shell exits. This is useful for running tests in"
|
||||
"parallel.")
|
||||
|| !op.addBoolOption('\0', "code-coverage", "Enable code coverage instrumentation.")
|
||||
#ifdef DEBUG
|
||||
|| !op.addBoolOption('O', "print-alloc", "Print the number of allocations at exit")
|
||||
#endif
|
||||
|
||||
@@ -50,7 +50,7 @@ typedef HashSet<ReadBarrieredGlobalObject,
|
||||
* compartment.
|
||||
*
|
||||
* The purpose of this is to allow the garbage collector to easily find edges
|
||||
* from debugee object compartments to debugger compartments when calculating
|
||||
* from debuggee object compartments to debugger compartments when calculating
|
||||
* the compartment groups. Note that these edges are the inverse of the edges
|
||||
* stored in the cross compartment map.
|
||||
*
|
||||
@@ -62,6 +62,11 @@ typedef HashSet<ReadBarrieredGlobalObject,
|
||||
* If InvisibleKeysOk is true, then the map can have keys in invisible-to-
|
||||
* debugger compartments. If it is false, we assert that such entries are never
|
||||
* created.
|
||||
*
|
||||
* Also note that keys in these weakmaps can be in any compartment, debuggee or
|
||||
* not, because they cannot be deleted when a compartment is no longer a
|
||||
* debuggee: the values need to maintain object identity across add/remove/add
|
||||
* transitions.
|
||||
*/
|
||||
template <class UnbarrieredKey, bool InvisibleKeysOk=false>
|
||||
class DebuggerWeakMap : private WeakMap<PreBarriered<UnbarrieredKey>, RelocatablePtrObject>
|
||||
|
||||
@@ -1488,6 +1488,14 @@ HandleError(JSContext* cx, InterpreterRegs& regs)
|
||||
{
|
||||
MOZ_ASSERT(regs.fp()->script()->containsPC(regs.pc));
|
||||
|
||||
if (regs.fp()->script()->hasScriptCounts()) {
|
||||
PCCounts* counts = regs.fp()->script()->getThrowCounts(regs.pc);
|
||||
// If we failed to allocate, then skip the increment and continue to
|
||||
// handle the exception.
|
||||
if (counts)
|
||||
counts->numExec()++;
|
||||
}
|
||||
|
||||
ScopeIter si(cx, regs.fp(), regs.pc);
|
||||
bool ok = false;
|
||||
|
||||
@@ -1515,15 +1523,19 @@ HandleError(JSContext* cx, InterpreterRegs& regs)
|
||||
}
|
||||
}
|
||||
|
||||
switch (ProcessTryNotes(cx, si, regs)) {
|
||||
HandleErrorContinuation res = ProcessTryNotes(cx, si, regs);
|
||||
switch (res) {
|
||||
case SuccessfulReturnContinuation:
|
||||
break;
|
||||
case ErrorReturnContinuation:
|
||||
goto again;
|
||||
case CatchContinuation:
|
||||
return CatchContinuation;
|
||||
case FinallyContinuation:
|
||||
return FinallyContinuation;
|
||||
// No need to increment the PCCounts number of execution here, as
|
||||
// the interpreter increments any PCCounts if present.
|
||||
MOZ_ASSERT_IF(regs.fp()->script()->hasScriptCounts(),
|
||||
regs.fp()->script()->maybeGetPCCounts(regs.pc));
|
||||
return res;
|
||||
}
|
||||
|
||||
ok = HandleClosingGeneratorReturn(cx, regs.fp(), ok);
|
||||
@@ -2010,8 +2022,9 @@ CASE(EnableInterruptsPseudoOpcode)
|
||||
}
|
||||
|
||||
if (script->hasScriptCounts()) {
|
||||
PCCounts counts = script->getPCCounts(REGS.pc);
|
||||
counts.numExec()++;
|
||||
PCCounts* counts = script->maybeGetPCCounts(REGS.pc);
|
||||
if (counts)
|
||||
counts->numExec()++;
|
||||
moreInterrupts = true;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -36,6 +36,7 @@
|
||||
#ifdef DEBUG
|
||||
# include "js/Proxy.h" // For AutoEnterPolicy
|
||||
#endif
|
||||
#include "js/TraceableVector.h"
|
||||
#include "js/Vector.h"
|
||||
#include "vm/CommonPropertyNames.h"
|
||||
#include "vm/DateTime.h"
|
||||
@@ -140,7 +141,7 @@ struct ScopeCoordinateNameCache {
|
||||
void purge();
|
||||
};
|
||||
|
||||
typedef Vector<ScriptAndCounts, 0, SystemAllocPolicy> ScriptAndCountsVector;
|
||||
using ScriptAndCountsVector = TraceableVector<ScriptAndCounts, 0, SystemAllocPolicy>;
|
||||
|
||||
struct EvalCacheEntry
|
||||
{
|
||||
@@ -1048,7 +1049,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
#endif
|
||||
|
||||
/* Strong references on scripts held for PCCount profiling API. */
|
||||
js::ScriptAndCountsVector* scriptAndCountsVector;
|
||||
JS::PersistentRooted<js::ScriptAndCountsVector>* scriptAndCountsVector;
|
||||
|
||||
/* Well-known numbers held for use by this runtime's contexts. */
|
||||
const js::Value NaNValue;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "platform.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "IntelPowerGadget.h"
|
||||
#ifdef MOZ_TASK_TRACER
|
||||
#include "GeckoTaskTracer.h"
|
||||
@@ -17,15 +18,16 @@ namespace mozilla {
|
||||
class ProfileGatherer;
|
||||
} // namespace mozilla
|
||||
|
||||
typedef mozilla::Vector<std::string> ThreadNameFilterList;
|
||||
|
||||
static bool
|
||||
threadSelected(ThreadInfo* aInfo, char** aThreadNameFilters, uint32_t aFeatureCount) {
|
||||
if (aFeatureCount == 0) {
|
||||
threadSelected(ThreadInfo* aInfo, const ThreadNameFilterList &aThreadNameFilters) {
|
||||
if (aThreadNameFilters.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aFeatureCount; ++i) {
|
||||
const char* filterPrefix = aThreadNameFilters[i];
|
||||
if (strncmp(aInfo->Name(), filterPrefix, strlen(filterPrefix)) == 0) {
|
||||
for (uint32_t i = 0; i < aThreadNameFilters.length(); ++i) {
|
||||
if (aThreadNameFilters[i] == aInfo->Name()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -49,7 +51,7 @@ class TableTicker: public Sampler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!threadSelected(aInfo, mThreadNameFilters, mFilterCount)) {
|
||||
if (!threadSelected(aInfo, mThreadNameFilters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -144,8 +146,7 @@ protected:
|
||||
|
||||
// Keep the thread filter to check against new thread that
|
||||
// are started while profiling
|
||||
char** mThreadNameFilters;
|
||||
uint32_t mFilterCount;
|
||||
ThreadNameFilterList mThreadNameFilters;
|
||||
bool mPrivacyMode;
|
||||
bool mAddMainThreadIO;
|
||||
bool mProfileMemory;
|
||||
|
||||
@@ -1232,7 +1232,7 @@ enum ccType
|
||||
// Top level structure for the cycle collector.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef js::SliceBudget SliceBudget;
|
||||
using js::SliceBudget;
|
||||
|
||||
class JSPurpleBuffer;
|
||||
|
||||
@@ -3565,7 +3565,7 @@ nsCycleCollector::ShutdownCollect()
|
||||
{
|
||||
FinishAnyIncrementalGCInProgress();
|
||||
|
||||
SliceBudget unlimitedBudget;
|
||||
SliceBudget unlimitedBudget = SliceBudget::unlimited();
|
||||
uint32_t i;
|
||||
for (i = 0; i < DEFAULT_SHUTDOWN_COLLECTIONS; ++i) {
|
||||
if (!Collect(ShutdownCC, unlimitedBudget, nullptr)) {
|
||||
@@ -3710,7 +3710,7 @@ nsCycleCollector::FinishAnyCurrentCollection()
|
||||
return;
|
||||
}
|
||||
|
||||
SliceBudget unlimitedBudget;
|
||||
SliceBudget unlimitedBudget = SliceBudget::unlimited();
|
||||
PrintPhase("FinishAnyCurrentCollection");
|
||||
// Use SliceCC because we only want to finish the CC in progress.
|
||||
Collect(SliceCC, unlimitedBudget, nullptr);
|
||||
@@ -4127,7 +4127,7 @@ nsCycleCollector_collect(nsICycleCollectorListener* aManualListener)
|
||||
PROFILER_LABEL("nsCycleCollector", "collect",
|
||||
js::ProfileEntry::Category::CC);
|
||||
|
||||
SliceBudget unlimitedBudget;
|
||||
SliceBudget unlimitedBudget = SliceBudget::unlimited();
|
||||
data->mCollector->Collect(ManualCC, unlimitedBudget, aManualListener);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user