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:
2021-10-08 11:10:44 +08:00
parent da1635889a
commit fae40c10fb
83 changed files with 2801 additions and 688 deletions
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
+57 -2
View File
@@ -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;
+4 -2
View File
@@ -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(); }
+2 -2
View File
@@ -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
View File
@@ -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),
+67
View File
@@ -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;
-6
View File
@@ -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
View File
@@ -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
+215
View File
@@ -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
});
+193
View File
@@ -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();
+128
View File
@@ -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();
+5 -10
View File
@@ -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
+123
View File
@@ -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(&regs);
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
+4
View File
@@ -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
+17 -5
View File
@@ -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);
+4
View File
@@ -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;
+20
View File
@@ -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:
+7 -12
View File
@@ -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);
}
+1 -1
View File
@@ -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>
-17
View File
@@ -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
+6 -2
View File
@@ -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));
}
+14
View File
@@ -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)
{
+29
View File
@@ -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)
{
+62
View File
@@ -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.
+11 -6
View File
@@ -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);
}
+1 -1
View File
@@ -220,7 +220,7 @@ class CodeGeneratorARM64 : public CodeGeneratorShared
void generateInvalidateEpilogue();
void visitRandom(LRandom* ins);
void setReturnDoubleRegs(LiveRegisterSet* regs);
protected:
void postAsmJSCall(LAsmJSCall* lir) {
-17
View File
@@ -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
+4 -1
View File
@@ -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)
{
+44 -1
View File
@@ -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);
-17
View File
@@ -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));
}
+5 -12
View File
@@ -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);
}
+1 -1
View File
@@ -241,7 +241,7 @@ class CodeGeneratorMIPS : public CodeGeneratorShared
void generateInvalidateEpilogue();
void visitRandom(LRandom* ins);
void setReturnDoubleRegs(LiveRegisterSet* regs);
protected:
void visitEffectiveAddress(LEffectiveAddress* ins);
+11
View File
@@ -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)
{
+144 -56
View File
@@ -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);
+105 -51
View File
@@ -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;
}
+1
View File
@@ -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;
-1
View File
@@ -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
-1
View File
@@ -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(); }
};
+9
View File
@@ -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()
+51
View File
@@ -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
-126
View File
@@ -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());
}
-4
View File
@@ -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;
-35
View File
@@ -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
-2
View File
@@ -337,8 +337,6 @@ void
LIRGeneratorX64::visitRandom(MRandom* ins)
{
LRandom* lir = new(alloc()) LRandom(temp(),
temp(),
temp(),
temp(),
temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
+13
View File
@@ -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)
{
+35
View File
@@ -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,
+61
View File
@@ -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);
+54
View File
@@ -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()
-16
View File
@@ -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);
}
-2
View File
@@ -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);
-17
View File
@@ -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
+7 -2
View File
@@ -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));
}
+14
View File
@@ -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)
{
+57
View File
@@ -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)
{
+83
View File
@@ -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;
+16 -7
View File
@@ -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));
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
}
};
+47 -4
View File
@@ -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
-8
View File
@@ -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)
{
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+6 -1
View File
@@ -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>
+18 -5
View File
@@ -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
View File
@@ -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;
+9 -8
View File
@@ -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;
+4 -4
View File
@@ -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);
}