Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / src / jit / CacheIRHealth.cpp
blob5a5ac279b37d31d3b0afb45923aa9d944ccbc223
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifdef JS_CACHEIR_SPEW
8 # include "jit/CacheIRHealth.h"
10 # include "mozilla/Maybe.h"
12 # include "gc/Zone.h"
13 # include "jit/BaselineIC.h"
14 # include "jit/CacheIRCompiler.h"
15 # include "jit/JitScript.h"
16 # include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
17 # include "vm/JSScript.h"
19 # include "vm/JSObject-inl.h"
20 # include "vm/Realm-inl.h"
22 using namespace js;
23 using namespace js::jit;
25 // TODO: Refine how we assign happiness based on total health score.
26 CacheIRHealth::Happiness CacheIRHealth::determineStubHappiness(
27 uint32_t stubHealthScore) {
28 if (stubHealthScore >= 30) {
29 return Sad;
32 if (stubHealthScore >= 20) {
33 return MediumSad;
36 if (stubHealthScore >= 10) {
37 return MediumHappy;
40 return Happy;
43 CacheIRHealth::Happiness CacheIRHealth::spewStubHealth(
44 AutoStructuredSpewer& spew, ICCacheIRStub* stub) {
45 const CacheIRStubInfo* stubInfo = stub->stubInfo();
46 CacheIRReader stubReader(stubInfo);
47 uint32_t totalStubHealth = 0;
48 spew->beginListProperty("cacheIROps");
49 while (stubReader.more()) {
50 CacheOp op = stubReader.readOp();
51 uint32_t opHealth = CacheIROpHealth[size_t(op)];
52 uint32_t argLength = CacheIROpInfos[size_t(op)].argLength;
53 const char* opName = CacheIROpNames[size_t(op)];
55 spew->beginObject();
56 if (opHealth == UINT32_MAX) {
57 spew->property("unscoredOp", opName);
58 } else {
59 spew->property("cacheIROp", opName);
60 spew->property("opHealth", opHealth);
61 totalStubHealth += opHealth;
63 spew->endObject();
65 stubReader.skip(argLength);
67 spew->endList(); // cacheIROps
69 spew->property("stubHealth", totalStubHealth);
71 Happiness stubHappiness = determineStubHappiness(totalStubHealth);
72 spew->property("stubHappiness", stubHappiness);
74 return stubHappiness;
77 BaseScript* CacheIRHealth::maybeExtractBaseScript(JSContext* cx, Shape* shape) {
78 TaggedProto taggedProto = shape->base()->proto();
79 if (!taggedProto.isObject()) {
80 return nullptr;
82 Value cval;
83 JSObject* proto = taggedProto.toObject();
84 AutoRealm ar(cx, proto);
85 if (!GetPropertyPure(cx, proto, NameToId(cx->names().constructor), &cval)) {
86 return nullptr;
88 if (!IsFunctionObject(cval)) {
89 return nullptr;
91 JSFunction& jsfun = cval.toObject().as<JSFunction>();
92 if (!jsfun.hasBaseScript()) {
93 return nullptr;
95 return jsfun.baseScript();
98 void CacheIRHealth::spewShapeInformation(AutoStructuredSpewer& spew,
99 JSContext* cx, ICStub* stub) {
100 bool shapesStarted = false;
101 const CacheIRStubInfo* stubInfo = stub->toCacheIRStub()->stubInfo();
102 size_t offset = 0;
103 uint32_t fieldIndex = 0;
105 while (stubInfo->fieldType(fieldIndex) != StubField::Type::Limit) {
106 if (stubInfo->fieldType(fieldIndex) == StubField::Type::Shape) {
107 Shape* shape = reinterpret_cast<Shape*>(
108 stubInfo->getStubRawWord(stub->toCacheIRStub(), offset));
109 if (!shapesStarted) {
110 shapesStarted = true;
111 spew->beginListProperty("shapes");
114 const PropMap* propMap =
115 shape->isNative() ? shape->asNative().propMap() : nullptr;
116 if (propMap) {
117 spew->beginObject();
119 if (!propMap->isDictionary()) {
120 uint32_t mapLength = shape->asNative().propMapLength();
121 if (mapLength) {
122 PropertyKey lastKey = shape->asNative().lastProperty().key();
123 if (lastKey.isInt()) {
124 spew->property("lastProperty", lastKey.toInt());
125 } else if (lastKey.isString()) {
126 JSString* str = lastKey.toString();
127 if (str && str->isLinear()) {
128 spew->property("lastProperty", &str->asLinear());
130 } else {
131 MOZ_ASSERT(lastKey.isSymbol());
132 JSString* str = lastKey.toSymbol()->description();
133 if (str && str->isLinear()) {
134 spew->property("lastProperty", &str->asLinear());
138 spew->property("totalKeys", propMap->approximateEntryCount());
139 BaseScript* baseScript = maybeExtractBaseScript(cx, shape);
140 if (baseScript) {
141 spew->beginObjectProperty("shapeAllocSite");
143 spew->property("filename", baseScript->filename());
144 spew->property("line", baseScript->lineno());
145 spew->property("column", baseScript->column().oneOriginValue());
147 spew->endObject();
151 spew->endObject();
154 offset += StubField::sizeInBytes(stubInfo->fieldType(fieldIndex));
155 fieldIndex++;
158 if (shapesStarted) {
159 spew->endList();
163 bool CacheIRHealth::spewNonFallbackICInformation(AutoStructuredSpewer& spew,
164 JSContext* cx,
165 ICStub* firstStub,
166 Happiness* entryHappiness) {
167 const CacheIRStubInfo* stubInfo = firstStub->toCacheIRStub()->stubInfo();
168 Vector<bool, 8, SystemAllocPolicy> sawDistinctValueAtFieldIndex;
170 bool sawNonZeroCount = false;
171 bool sawDifferentCacheIRStubs = false;
172 ICStub* stub = firstStub;
174 spew->beginListProperty("stubs");
175 while (stub && !stub->isFallback()) {
176 spew->beginObject();
178 Happiness stubHappiness = spewStubHealth(spew, stub->toCacheIRStub());
179 if (stubHappiness < *entryHappiness) {
180 *entryHappiness = stubHappiness;
183 spewShapeInformation(spew, cx, stub);
185 ICStub* nextStub = stub->toCacheIRStub()->next();
186 if (!nextStub->isFallback()) {
187 if (nextStub->enteredCount() > 0) {
188 // More than one stub has a hit count greater than zero.
189 // This is sad because we do not Warp transpile in this case.
190 *entryHappiness = Sad;
191 sawNonZeroCount = true;
194 if (nextStub->toCacheIRStub()->stubInfo() != stubInfo) {
195 sawDifferentCacheIRStubs = true;
198 // If there are multiple stubs with non zero hit counts and if all
199 // of the stubs have equivalent CacheIR, then keep track of how many
200 // distinct stub field values are seen for each field index.
201 if (sawNonZeroCount && !sawDifferentCacheIRStubs) {
202 uint32_t fieldIndex = 0;
203 size_t offset = 0;
205 while (stubInfo->fieldType(fieldIndex) != StubField::Type::Limit) {
206 if (sawDistinctValueAtFieldIndex.length() <= fieldIndex) {
207 if (!sawDistinctValueAtFieldIndex.append(false)) {
208 return false;
212 if (StubField::sizeIsWord(stubInfo->fieldType(fieldIndex))) {
213 uintptr_t firstRaw =
214 stubInfo->getStubRawWord(firstStub->toCacheIRStub(), offset);
215 uintptr_t nextRaw =
216 stubInfo->getStubRawWord(nextStub->toCacheIRStub(), offset);
217 if (firstRaw != nextRaw) {
218 sawDistinctValueAtFieldIndex[fieldIndex] = true;
220 } else {
221 MOZ_ASSERT(
222 StubField::sizeIsInt64(stubInfo->fieldType(fieldIndex)));
223 int64_t firstRaw =
224 stubInfo->getStubRawInt64(firstStub->toCacheIRStub(), offset);
225 int64_t nextRaw =
226 stubInfo->getStubRawInt64(nextStub->toCacheIRStub(), offset);
228 if (firstRaw != nextRaw) {
229 sawDistinctValueAtFieldIndex[fieldIndex] = true;
233 offset += StubField::sizeInBytes(stubInfo->fieldType(fieldIndex));
234 fieldIndex++;
239 spew->property("hitCount", stub->enteredCount());
240 stub = nextStub;
242 spew->endObject();
244 spew->endList(); // stubs
246 // If more than one CacheIR stub has an entered count greater than
247 // zero and all the stubs have equivalent CacheIR, then spew
248 // the information collected about the stub fields across the IC.
249 if (sawNonZeroCount && !sawDifferentCacheIRStubs) {
250 spew->beginListProperty("stubFields");
251 for (size_t i = 0; i < sawDistinctValueAtFieldIndex.length(); i++) {
252 spew->beginObject();
254 spew->property("fieldType", uint8_t(stubInfo->fieldType(i)));
255 spew->property("sawDistinctFieldValues",
256 sawDistinctValueAtFieldIndex[i]);
258 spew->endObject();
260 spew->endList();
263 return true;
266 bool CacheIRHealth::spewICEntryHealth(AutoStructuredSpewer& spew, JSContext* cx,
267 HandleScript script, ICEntry* entry,
268 ICFallbackStub* fallback, jsbytecode* pc,
269 JSOp op, Happiness* entryHappiness) {
270 spew->property("op", CodeName(op));
272 // TODO: If a perf issue arises, look into improving the SrcNotes
273 // API call below.
274 JS::LimitedColumnNumberOneOrigin column;
275 spew->property("lineno", PCToLineNumber(script, pc, &column));
276 spew->property("column", column.oneOriginValue());
278 ICStub* firstStub = entry->firstStub();
279 if (!firstStub->isFallback()) {
280 if (!spewNonFallbackICInformation(spew, cx, firstStub, entryHappiness)) {
281 return false;
285 if (fallback->state().mode() != ICState::Mode::Specialized) {
286 *entryHappiness = Sad;
289 spew->property("entryHappiness", uint8_t(*entryHappiness));
291 spew->property("mode", uint8_t(fallback->state().mode()));
293 spew->property("fallbackCount", fallback->enteredCount());
295 return true;
298 void CacheIRHealth::spewScriptFinalWarmUpCount(JSContext* cx,
299 const char* filename,
300 JSScript* script,
301 uint32_t warmUpCount) {
302 AutoStructuredSpewer spew(cx, SpewChannel::CacheIRHealthReport, nullptr);
303 if (!spew) {
304 return;
307 spew->property("filename", filename);
308 spew->property("line", script->lineno());
309 spew->property("column", script->column().oneOriginValue());
310 spew->property("finalWarmUpCount", warmUpCount);
313 static bool addScriptToFinalWarmUpCountMap(JSContext* cx, HandleScript script) {
314 // Create Zone::scriptFilenameMap if necessary.
315 JS::Zone* zone = script->zone();
316 if (!zone->scriptFinalWarmUpCountMap) {
317 auto map = MakeUnique<ScriptFinalWarmUpCountMap>();
318 if (!map) {
319 return false;
322 zone->scriptFinalWarmUpCountMap = std::move(map);
325 SharedImmutableString sfilename =
326 SharedImmutableStringsCache::getSingleton().getOrCreate(
327 script->filename(), strlen(script->filename()));
328 if (!sfilename) {
329 ReportOutOfMemory(cx);
330 return false;
333 if (!zone->scriptFinalWarmUpCountMap->put(
334 script, std::make_tuple(uint32_t(0), std::move(sfilename)))) {
335 ReportOutOfMemory(cx);
336 return false;
339 script->setNeedsFinalWarmUpCount();
340 return true;
343 void CacheIRHealth::healthReportForIC(JSContext* cx, ICEntry* entry,
344 ICFallbackStub* fallback,
345 HandleScript script,
346 SpewContext context) {
347 AutoStructuredSpewer spew(cx, SpewChannel::CacheIRHealthReport, script);
348 if (!spew) {
349 return;
352 if (!addScriptToFinalWarmUpCountMap(cx, script)) {
353 cx->recoverFromOutOfMemory();
354 return;
356 spew->property("spewContext", uint8_t(context));
358 jsbytecode* op = script->offsetToPC(fallback->pcOffset());
359 JSOp jsOp = JSOp(*op);
361 Happiness entryHappiness = Happy;
362 if (!spewICEntryHealth(spew, cx, script, entry, fallback, op, jsOp,
363 &entryHappiness)) {
364 cx->recoverFromOutOfMemory();
365 return;
367 MOZ_ASSERT(entryHappiness == Sad);
370 void CacheIRHealth::healthReportForScript(JSContext* cx, HandleScript script,
371 SpewContext context) {
372 jit::JitScript* jitScript = script->maybeJitScript();
373 if (!jitScript) {
374 return;
377 AutoStructuredSpewer spew(cx, SpewChannel::CacheIRHealthReport, script);
378 if (!spew) {
379 return;
382 if (!addScriptToFinalWarmUpCountMap(cx, script)) {
383 cx->recoverFromOutOfMemory();
384 return;
387 spew->property("spewContext", uint8_t(context));
389 spew->beginListProperty("entries");
391 Happiness scriptHappiness = Happy;
393 for (size_t i = 0; i < jitScript->numICEntries(); i++) {
394 ICEntry& entry = jitScript->icEntry(i);
395 ICFallbackStub* fallback = jitScript->fallbackStub(i);
396 jsbytecode* pc = script->offsetToPC(fallback->pcOffset());
397 JSOp op = JSOp(*pc);
399 spew->beginObject();
400 Happiness entryHappiness = Happy;
401 if (!spewICEntryHealth(spew, cx, script, &entry, fallback, pc, op,
402 &entryHappiness)) {
403 cx->recoverFromOutOfMemory();
404 return;
406 if (entryHappiness < scriptHappiness) {
407 scriptHappiness = entryHappiness;
409 spew->endObject();
412 spew->endList(); // entries
414 spew->property("scriptHappiness", uint8_t(scriptHappiness));
417 #endif /* JS_CACHEIR_SPEW */