2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/tc-region.h"
19 #include "hphp/runtime/vm/jit/tc.h"
20 #include "hphp/runtime/vm/jit/tc-internal.h"
21 #include "hphp/runtime/vm/jit/tc-prologue.h"
22 #include "hphp/runtime/vm/jit/tc-record.h"
24 #include "hphp/runtime/base/perf-warning.h"
25 #include "hphp/runtime/vm/jit/align.h"
26 #include "hphp/runtime/vm/jit/cfg.h"
27 #include "hphp/runtime/vm/jit/cg-meta.h"
28 #include "hphp/runtime/vm/jit/code-cache.h"
29 #include "hphp/runtime/vm/jit/func-order.h"
30 #include "hphp/runtime/vm/jit/ir-unit.h"
31 #include "hphp/runtime/vm/jit/irlower.h"
32 #include "hphp/runtime/vm/jit/mcgen.h"
33 #include "hphp/runtime/vm/jit/perf-counters.h"
34 #include "hphp/runtime/vm/jit/prof-data.h"
35 #include "hphp/runtime/vm/jit/relocation.h"
36 #include "hphp/runtime/vm/jit/service-requests.h"
37 #include "hphp/runtime/vm/jit/smashable-instr.h"
38 #include "hphp/runtime/vm/jit/srcdb.h"
39 #include "hphp/runtime/vm/jit/timer.h"
40 #include "hphp/runtime/vm/jit/trans-db.h"
41 #include "hphp/runtime/vm/jit/trans-rec.h"
42 #include "hphp/runtime/vm/jit/translate-region.h"
43 #include "hphp/runtime/vm/jit/vasm-emit.h"
44 #include "hphp/runtime/vm/jit/vasm-gen.h"
45 #include "hphp/runtime/vm/jit/vm-protect.h"
46 #include "hphp/runtime/vm/jit/vtune-jit.h"
47 #include "hphp/runtime/vm/resumable.h"
48 #include "hphp/runtime/vm/treadmill.h"
50 #include "hphp/util/boot-stats.h"
51 #include "hphp/util/service-data.h"
52 #include "hphp/util/struct-log.h"
53 #include "hphp/util/timer.h"
54 #include "hphp/util/trace.h"
58 namespace HPHP::jit::tc
{
62 using PrologueTCAMap
= jit::hash_map
<PrologueID
,TCA
,PrologueID::Hasher
>;
63 using SrcKeyTransMap
= jit::hash_map
<SrcKey
,jit::vector
<TCA
>,
66 bool checkLimit(TransKind kind
, const size_t numTrans
) {
67 auto const limit
= kind
== TransKind::Profile
68 ? RuntimeOption::EvalJitMaxProfileTranslations
69 : RuntimeOption::EvalJitMaxTranslations
;
71 // Once numTrans has reached limit + 1 we know that an interp translation
72 // has already been emitted. Prior to that if numTrans == limit only allow
73 // interp translations to avoid a race where multiple threads believe there
74 // are still available translations.
75 if (numTrans
== limit
+ 1) return false;
76 if (numTrans
== limit
&& kind
!= TransKind::Interp
) return false;
80 void invalidateSrcKey(SrcKey sk
, SBInvOffset spOff
) {
81 assertx(!RuntimeOption::RepoAuthoritative
|| RuntimeOption::EvalJitPGO
);
83 * Reroute existing translations for SrcKey to an as-yet indeterminate
86 auto const sr
= srcDB().find(sk
);
88 always_assert(profData()->wasDeserialized());
92 // Retranslate stub must exist, as createSrcRec() created it.
93 auto const transStub
= svcreq::getOrEmitStub(
94 svcreq::StubType::Translate
, sk
, spOff
);
95 always_assert(transStub
);
98 * Since previous translations aren't reachable from here, we know we
99 * just created some garbage in the TC. We currently have no mechanism
102 FTRACE_MOD(Trace::reusetc
, 1,
103 "Replacing translations from sk: {} " "to SrcRec addr={}\n",
104 showShort(sk
), (void*)sr
);
106 sr
->replaceOldTranslations(transStub
);
109 void invalidateFuncProfSrcKeys(const Func
* func
) {
111 auto const funcId
= func
->getFuncId();
112 for (auto tid
: profData()->funcProfTransIDs(funcId
)) {
113 auto const transRec
= profData()->transRec(tid
);
114 invalidateSrcKey(transRec
->srcKey(), transRec
->startSpOff());
118 size_t infoSize(const FuncMetaInfo
& info
) {
120 for (auto& trans
: info
.translators
) {
121 auto const& range
= trans
->range();
122 sz
+= range
.main
.size() + range
.cold
.size() + range
.frozen
.size();
127 bool checkTCLimits() {
128 auto const main_under
= code().main().used() < CodeCache::AMaxUsage
;
129 auto const cold_under
= code().cold().used() < CodeCache::AColdMaxUsage
;
130 auto const froz_under
= code().frozen().used() < CodeCache::AFrozenMaxUsage
;
132 return main_under
&& cold_under
&& froz_under
;
135 void relocateOptFunc(FuncMetaInfo
& info
,
136 PrologueTCAMap
& prologueTCAs
,
137 SrcKeyTransMap
& srcKeyTrans
,
138 size_t* failedBytes
= nullptr) {
139 auto const func
= info
.func
;
140 unsigned nRegions
= 0;
141 // Relocate/emit all prologues and translations for func in order.
142 for (auto& translator
: info
.translators
) {
143 assertx(func
== translator
->sk
.func());
144 auto const prologueTranslator
=
145 dynamic_cast<PrologueTranslator
*>(translator
.get());
146 auto const regionTranslator
=
147 dynamic_cast<RegionTranslator
*>(translator
.get());
149 if (regionTranslator
) {
150 auto const it
= srcKeyTrans
.find(translator
->sk
);
151 // We don't publish translations one at a time during optimized
152 // retranslation. Use the srcKeyTransMap to establish if we hit the
153 // translation limit.
154 if (it
!= srcKeyTrans
.end() &&
155 !checkLimit(regionTranslator
->kind
, it
->second
.size())) {
160 if (!translator
->translateSuccess()) continue;
161 auto const& range
= translator
->range();
162 auto const bytes
= range
.main
.size() + range
.cold
.size() +
164 if (regionTranslator
) nRegions
++;
165 // We don't want to align the first region, to enable fall-through from the
166 // last emitted prologue.
167 const bool alignMain
= !regionTranslator
|| nRegions
!= 1;
168 translator
->relocate(alignMain
);
169 if (!translator
->translateSuccess()) {
170 if (failedBytes
) *failedBytes
+= bytes
;
174 translator
->bindOutgoingEdges();
175 if (!translator
->translateSuccess()) continue;
177 always_assert(code().inMain(translator
->entry()));
178 if (prologueTranslator
) {
179 const auto pid
= PrologueID(func
, prologueTranslator
->paramIndex());
180 prologueTCAs
[pid
] = translator
->entry();
181 } else if (regionTranslator
) {
182 srcKeyTrans
[regionTranslator
->sk
].emplace_back(translator
->entry());
187 void publishOptFuncMeta(FuncMetaInfo
& info
) {
188 for (auto const& translator
: info
.translators
) {
189 if (translator
->translateSuccess()) translator
->publishMetaInternal();
193 void publishOptFuncCode(FuncMetaInfo
& info
,
194 jit::hash_set
<TCA
>* publishedSet
= nullptr) {
195 auto const func
= info
.func
;
197 // Publish all prologues and translations for func in order.
198 for (auto const& translator
: info
.translators
) {
199 if (translator
->translateSuccess()) {
200 translator
->publishCodeInternal();
201 auto const tca
= translator
->entry();
202 if (publishedSet
) publishedSet
->insert(tca
);
203 auto const numParams
= func
->numNonVariadicParams();
204 if (translator
->sk
== SrcKey
{func
, numParams
, SrcKey::FuncEntryTag
{}} &&
205 func
->numRequiredParams() == numParams
) {
206 func
->setFuncEntry(tca
);
209 // If we failed to emit the prologue (e.g. the TC filled up), redirect
210 // all the callers to the fcallHelperThunk so that they stop calling
212 translator
->smashBackup();
217 void relocateSortedOptFuncs(std::vector
<FuncMetaInfo
>& infos
,
218 PrologueTCAMap
& prologueTCAs
,
219 SrcKeyTransMap
& srcKeyTrans
) {
220 size_t failedBytes
= 0;
222 bool shouldLog
= RuntimeOption::ServerExecutionMode();
223 for (auto& finfo
: infos
) {
224 // We clear the translations that are not relocated to ensure
225 // no one tries publishing such translations.
226 if (!Func::isFuncIdValid(finfo
.fid
)) {
231 // make sure we don't get ahead of the translation threads
232 mcgen::waitForTranslate(finfo
);
234 if (!checkTCLimits()) {
235 FTRACE(1, "relocateSortedOptFuncs: ran out of space in the TC. "
236 "Skipping function {} {}\n", finfo
.func
->getFuncId(),
237 finfo
.func
->fullName());
238 failedBytes
+= infoSize(finfo
);
239 // Reset the translators. This will cause the callers of the OptPrologues
240 // that are being skipped to be smashed to stop calling the corresponding
241 // ProfPrologues (see publishOptFuncCode).
242 for (auto& translator
: finfo
.translators
) {
249 Logger::Info("retranslateAll: starting to relocate functions");
251 relocateOptFunc(finfo
, prologueTCAs
, srcKeyTrans
, &failedBytes
);
255 FTRACE(1, "relocateSortedOptFuncs: failedBytes = {}\n", failedBytes
);
256 logPerfWarning("opt_translation_overflow", 1, [&] (StructuredLogEntry
& e
) {
257 e
.setInt("bytes_dropped", failedBytes
);
263 * Smash and optimize the calls in `meta' to prologues in `srcKeyTrans'.
265 void smashOptCalls(CGMeta
& meta
,
266 const PrologueTCAMap
& prologueTCAs
) {
267 auto const oldSmashableCallData
= std::move(meta
.smashableCallData
);
268 for (auto& pair
: oldSmashableCallData
) {
269 TCA call
= pair
.first
;
270 auto const& pid
= pair
.second
;
271 auto it
= prologueTCAs
.find(pid
);
272 if (it
== prologueTCAs
.end()) {
273 // insert non-smashed call back into transInfo.meta.smashableCallData
274 meta
.smashableCallData
.emplace(pair
);
278 const TCA target
= it
->second
;
279 FTRACE(1, "smashedOptCalls: found candidate call @ {}, "
280 "target prologue @ {} (funcId={}, nArgs={})\n",
281 call
, target
, pid
.funcId(), pid
.nargs());
282 assertx(code().inMainOrColdOrFrozen(call
));
283 assertx(code().inMainOrColdOrFrozen(target
));
285 smashCall(call
, target
);
286 optimizeSmashedCall(call
);
288 meta
.smashableLocations
.erase(call
);
293 * Find the jump target for jumps within the translation corresponding to
294 * `curEntry' going to the SrcRec corresponding to `vec'. `curEntry' is the TCA
295 * of the start of the current tracelet. For prologues this is nullptr. It is
296 * used while smashing retranslations of the same srckey. `isRetrans` indicate
297 * whether this is a fallback jump to the next translation.
299 TCA
findJumpTarget(TCA curEntry
,
300 const jit::vector
<TCA
>& vec
,
302 always_assert(vec
.size() > 0);
304 // It may seem like !isRetrans should imply that none of the elements in the
305 // retranslation chain vector is the curEntry. In practice self loops may
307 always_assert(IMPLIES(curEntry
== nullptr, !isRetrans
));
309 // Case 1: when the jump is in a prologue (curEntry == nullptr) or it jumps to a
310 // different SrcKey, we jump to the first translation in the list.
315 // Case 2: when jumping to the same SrcKey, we jump to the translation
316 // following this one (`curEntry').
317 for (size_t i
= 0; i
< vec
.size() - 1; i
++) {
318 if (vec
[i
] == curEntry
) {
323 // We should only get here if `curEntry' is the last translation.
324 always_assert(curEntry
== vec
.back());
329 * Smash and optimize the jumps in the translation starting at TCA entry (with
330 * tracked metadata in meta) going to the optimized translations in
333 void smashOptBinds(CGMeta
& meta
,
334 TCA entry
, // nullptr for prologues
335 const SrcKeyTransMap
& srcKeyTrans
) {
336 jit::hash_set
<TCA
> smashed
;
337 decltype(meta
.smashableBinds
) newBinds
;
339 for (auto& bind
: meta
.smashableBinds
) {
340 auto it
= srcKeyTrans
.find(bind
.sk
);
341 if (it
== srcKeyTrans
.end()) {
342 newBinds
.emplace_back(bind
);
345 const auto& transVec
= it
->second
;
346 TCA succTCA
= findJumpTarget(entry
, transVec
, bind
.fallback
);
347 if (succTCA
== nullptr) {
348 newBinds
.emplace_back(bind
);
352 FTRACE(3, "smashOptBinds: found candidate bind @ {}, target SrcKey {}, "
354 bind
.smashable
.show(), showShort(bind
.sk
), bind
.fallback
);
355 // The bound ADDR pointer may not belong to the data segment, as is the case
356 // with SSwitchMap (see #10347945)
357 assertx(code().inMainOrColdOrFrozen(bind
.smashable
.toSmash()) ||
358 bind
.smashable
.type() == IncomingBranch::Tag::ADDR
);
359 assertx(code().inMainOrColdOrFrozen(succTCA
));
360 bind
.smashable
.patch(succTCA
);
361 bind
.smashable
.optimize();
362 smashed
.insert(bind
.smashable
.toSmash());
363 meta
.smashableLocations
.erase(bind
.smashable
.toSmash());
366 // Remove jumps that were smashed from meta.smashableJumpData.
367 meta
.smashableBinds
.swap(newBinds
);
369 // If any of the jumps we smashed was a tail jump (i.e. going to a
370 // retranslation of the entry SrcKey), then remove it from the set of
371 // inProgressTailJumps as it has already been smashed.
372 GrowableVector
<IncomingBranch
> newTailJumps
;
373 for (auto& jump
: meta
.inProgressTailJumps
) {
374 if (smashed
.count(jump
.toSmash()) == 0) {
375 newTailJumps
.push_back(jump
);
377 FTRACE(3, "smashOptBinds: removing {} from inProgressTailJumps\n",
381 meta
.inProgressTailJumps
.swap(newTailJumps
);
385 * Smash and optimize the calls and jumps between the translations/prologues in
388 void smashOptSortedOptFuncs(std::vector
<FuncMetaInfo
>& infos
,
389 const PrologueTCAMap
& prologueTCAs
,
390 const SrcKeyTransMap
& srcKeyTrans
) {
391 BootStats::Block
timer("RTA_smash_opt_funcs",
392 RuntimeOption::ServerExecutionMode());
393 for (auto& finfo
: infos
) {
394 if (!Func::isFuncIdValid(finfo
.fid
)) continue;
396 for (auto& translator
: finfo
.translators
) {
397 // Skip if the translation wasn't relocated (e.g. ran out of TC space).
398 if (!translator
->entry()) continue;
399 assertx(code().inMainOrColdOrFrozen(translator
->entry()));
401 if (isPrologue(translator
->kind
)) {
402 smashOptBinds(translator
->meta(), nullptr, srcKeyTrans
);
404 smashOptCalls(translator
->meta(), prologueTCAs
);
405 smashOptBinds(translator
->meta(), translator
->entry(), srcKeyTrans
);
411 void invalidateFuncsProfSrcKeys() {
412 BootStats::Block
timer("RTA_invalidate_prof_srckeys",
413 RuntimeOption::ServerExecutionMode());
414 auto const pd
= profData();
416 pd
->forEachProfilingFunc([&](auto const& func
) {
419 invalidateFuncProfSrcKeys(func
);
421 // clear the func body and prologues
422 const_cast<Func
*>(func
)->resetFuncEntry();
423 auto const numPrologues
= func
->numPrologues();
424 for (int p
= 0; p
< numPrologues
; p
++) {
425 const_cast<Func
*>(func
)->resetPrologue(p
);
430 void publishSortedOptFuncsMeta(std::vector
<FuncMetaInfo
>& infos
) {
431 BootStats::Block
timer("RTA_publish_meta",
432 RuntimeOption::ServerExecutionMode());
433 for (auto& finfo
: infos
) {
434 if (Func::isFuncIdValid(finfo
.fid
)) {
435 publishOptFuncMeta(finfo
);
440 void publishSortedOptFuncsCode(std::vector
<FuncMetaInfo
>& infos
,
441 jit::hash_set
<TCA
>* publishedSet
) {
442 BootStats::Block
timer("RTA_publish_code",
443 RuntimeOption::ServerExecutionMode());
444 for (auto& finfo
: infos
) {
445 if (Func::isFuncIdValid(finfo
.fid
)) {
446 publishOptFuncCode(finfo
, publishedSet
);
451 std::string
show(const SrcKeyTransMap
& map
) {
453 for (auto& skt
: map
) {
454 folly::format(&ret
, " - [{}]:", showShort(skt
.first
));
455 for (auto tca
: skt
.second
) {
456 folly::format(&ret
, " {},", tca
);
463 void checkPublishedAddr(TCA tca
, const jit::hash_set
<TCA
>& publishedSet
) {
464 always_assert_flog(code().inMainOrColdOrFrozen(tca
),
465 "srcKeyTrans has address not in hot/main: {}", tca
);
466 always_assert_flog(publishedSet
.count(tca
),
467 "srcKeyTrans has unpublished translation @ {}", tca
);
471 * Make sure that every address that we may have smashed to was published
472 * somewhere in the translation cache.
474 void checkPublishedAddresses(const PrologueTCAMap
& prologueTCAs
,
475 const SrcKeyTransMap
& srcKeyTrans
,
476 const jit::hash_set
<TCA
>& publishedSet
) {
477 for (auto& prologueTCA
: prologueTCAs
) {
478 checkPublishedAddr(prologueTCA
.second
, publishedSet
);
481 for (auto& skt
: srcKeyTrans
) {
482 auto& vec
= skt
.second
;
483 for (auto tca
: vec
) {
485 checkPublishedAddr(tca
, publishedSet
);
491 ////////////////////////////////////////////////////////////////////////////////
494 SrcRec
* findSrcRec(SrcKey sk
) {
495 return srcDB().find(sk
);
498 SrcRec
* createSrcRec(SrcKey sk
, SBInvOffset spOff
) {
499 if (auto const sr
= srcDB().find(sk
)) return sr
;
501 // invalidateSrcKey() depends on existence of this stub.
502 auto const transStub
= svcreq::getOrEmitStub(
503 svcreq::StubType::Translate
, sk
, spOff
);
504 if (!transStub
) return nullptr;
506 auto metaLock
= lockMetadata();
507 if (auto const sr
= srcDB().find(sk
)) return sr
;
509 SKTRACE(1, sk
, "inserting SrcRec\n");
511 auto const sr
= srcDB().insert(sk
);
512 if (RuntimeOption::EvalEnableReusableTC
) {
513 recordFuncSrcRec(sk
.func(), sr
);
520 ////////////////////////////////////////////////////////////////////////////////
522 void publishOptFunc(FuncMetaInfo info
) {
523 PrologueTCAMap prologueTCAs
;
524 SrcKeyTransMap srcKeyTrans
;
525 relocateOptFunc(info
, prologueTCAs
, srcKeyTrans
);
527 auto codeLock
= lockCode();
528 auto metaLock
= lockMetadata();
529 invalidateFuncProfSrcKeys(Func::fromFuncId(info
.fid
));
530 publishOptFuncMeta(info
);
531 publishOptFuncCode(info
);
533 updateCodeSizeCounters();
536 void relocatePublishSortedOptFuncs(std::vector
<FuncMetaInfo
> infos
) {
537 // Grab the session now (which includes a Treadmill::Session) so
538 // that no Func's get destroyed during this step
539 ProfData::Session
pds(Treadmill::SessionKind::RetranslateAll
);
540 const bool serverMode
= RuntimeOption::ServerExecutionMode();
542 PrologueTCAMap prologueTCAs
;
543 SrcKeyTransMap srcKeyTrans
;
545 relocateSortedOptFuncs(infos
, prologueTCAs
, srcKeyTrans
);
549 "retranslateAll: finished optimizing and relocating functions"
553 auto codeLock
= lockCode();
554 auto metaLock
= lockMetadata();
557 "relocatePublishSortedOptFuncs: after relocateSortedOptFuncs:\n{}\n",
560 smashOptSortedOptFuncs(infos
, prologueTCAs
, srcKeyTrans
);
563 "relocatePublishSortedOptFuncs: after smashOptSortedOptFuncs:\n{}\n",
567 Logger::Info("retranslateAll: starting to publish functions");
570 invalidateFuncsProfSrcKeys();
572 // Publish the metadata for all the translations/prologues before actually
573 // publishing any of their code. This is necessary because we've smashed
574 // calls and jumps across translations so, once any of them is made reachable,
575 // we have to assume that all of them are already reachable, even before
576 // they're all published.
577 publishSortedOptFuncsMeta(infos
);
579 jit::hash_set
<TCA
> publishedSet
;
580 publishSortedOptFuncsCode(infos
, debug
? &publishedSet
: nullptr);
583 "relocatePublishSortedOptFuncs: after publishSortedOptFuncsCode:\n"
584 "{}\n", show(srcKeyTrans
));
587 checkPublishedAddresses(prologueTCAs
, srcKeyTrans
, publishedSet
);
590 updateCodeSizeCounters();
593 void RegionTranslator::computeKind() {
594 // Update the translation kind if it is invalid, or if it may
595 // have changed (original kind was a profiling kind)
596 if (kind
== TransKind::Invalid
|| kind
== TransKind::Profile
) {
597 kind
= profileFunc(sk
.func()) ? TransKind::Profile
602 Optional
<TranslationResult
> RegionTranslator::getCached() {
603 auto const srcRec
= srcDB().find(sk
);
604 auto const numTrans
= srcRec
->numTrans();
605 if (prevNumTranslations
!= -1 && prevNumTranslations
!= numTrans
) {
606 // A new translation was generated before we grabbed the lock. Force
607 // execution to rerun through the retranslation chain.
608 return TranslationResult
{srcRec
->getTopTranslation()};
610 prevNumTranslations
= numTrans
;
612 // An optimize retranslation request may be in flight that will
613 // remove profiling translations from the srcRec. Wait till
614 // getCached is called while the lease is held to check the
615 // number of translations in the srcRec is not over max
617 if (!m_lease
|| !(*m_lease
)) return std::nullopt
;
618 // Check for potential interp anchor translation
619 if (kind
== TransKind::Profile
) {
620 if (numTrans
> RuntimeOption::EvalJitMaxProfileTranslations
) {
621 always_assert(numTrans
==
622 RuntimeOption::EvalJitMaxProfileTranslations
+ 1);
623 return TranslationResult
{srcRec
->getTopTranslation()};
625 } else if (numTrans
> RuntimeOption::EvalJitMaxTranslations
) {
626 always_assert(numTrans
== RuntimeOption::EvalJitMaxTranslations
+ 1);
627 return TranslationResult
{srcRec
->getTopTranslation()};
632 void RegionTranslator::resetCached() {
633 invalidateSrcKey(sk
, spOff
);
636 void RegionTranslator::gen() {
637 auto const srcRec
= srcDB().find(sk
);
638 auto const emitInterpStub
= [&] {
639 FTRACE(1, "emitting dispatchBB interp request for failed "
640 "translation (spOff = {})\n", spOff
.offset
);
641 vunit
= std::make_unique
<Vunit
>();
642 Vout vmain
{*vunit
, vunit
->makeBlock(AreaIndex::Main
)};
643 vunit
->entry
= Vlabel(vmain
);
645 emitInterpReq(vmain
, sk
, spOff
);
647 irlower::optimize(*vunit
, CodeKind::Helper
);
649 auto const fail
= [&] {
650 if (isProfiling(kind
)) return;
652 kind
= TransKind::Interp
;
653 if (!checkLimit(kind
, srcRec
->numTrans())) return;
657 // Only start profiling new functions at their entry point. This reduces the
658 // chances of profiling the body of a function but not its entry (where we
659 // trigger retranslation) and helps remove bias towards larger functions that
660 // can cause variations in the size of code.prof.
661 if (kind
== TransKind::Profile
&&
662 !profData()->profiling(sk
.funcID()) &&
666 if (!checkLimit(kind
, srcRec
->numTrans())) return fail();
669 region
= selectRegion(regionContext(), kind
);
671 if (!region
) return fail();
674 Timer
timer(Timer::mcg_translate
);
676 tracing::Block _
{"translate", [&] {
677 return traceProps(sk
.func())
679 .add("trans_kind", show(kind
));
681 tracing::annotateBlock(
683 return tracing::Props
{}
684 .add("region_size", region
->instrSize());
688 rqtrace::ScopeGuard trace
{"JIT_TRANSLATE"};
689 trace
.annotate("func_name", sk
.func()->fullName()->data());
690 trace
.annotate("trans_kind", show(kind
));
691 trace
.setEventPrefix("JIT_");
693 "region_size", folly::to
<std::string
>(region
->instrSize()));
696 transId
== kInvalidTransID
? TransIDSet
{} : TransIDSet
{transId
},
703 unit
= irGenRegion(*region
, ctx
, pconds
);
705 auto const& uas
= unit
->annotationData
->getAllAnnotations();
706 annotations
.insert(annotations
.end(), uas
.begin(), uas
.end());
707 hasLoop
= cfgHasLoop(*unit
);
709 vunit
= irlower::lowerUnit(*unit
);
710 // vasm gen can punt, and when it does it's important we don't retry
711 // translating the same thing again and again as it will potentially consume
712 // more and more RDS space.
713 if (!vunit
) emitInterpStub();
717 * Record various metadata about the translation into the global data
720 void RegionTranslator::publishMetaImpl() {
721 auto& fixups
= transMeta
->fixups
;
722 const auto& view
= transMeta
->view
;
723 const auto& loc
= transMeta
->range
.loc();
724 assertx(!loc
.empty());
726 auto const srcRec
= srcDB().find(sk
);
727 always_assert(srcRec
);
729 if (RuntimeOption::EvalProfileBC
) {
730 TransBCMapping prev
{};
731 for (auto& cur
: fixups
.bcMap
) {
732 if (!cur
.aStart
) continue;
734 recordBCInstr(uint32_t(prev
.sk
.op()), prev
.aStart
, cur
.aStart
, false);
736 recordBCInstr(OpTraceletGuard
, loc
.mainStart(), cur
.aStart
, false);
742 recordGdbTranslation(sk
, view
.main(), loc
.mainStart(), loc
.mainEnd());
743 recordGdbTranslation(sk
, view
.cold(), loc
.coldCodeStart(), loc
.coldEnd());
745 TransRec tr
{sk
, transId
, kind
, loc
.mainStart(), loc
.mainSize(),
746 loc
.coldCodeStart(), loc
.coldCodeSize(),
747 loc
.frozenCodeStart(), loc
.frozenCodeSize(),
748 std::move(annotations
), region
, fixups
.bcMap
, hasLoop
};
749 transdb::addTranslation(tr
);
750 FuncOrder::recordTranslation(tr
);
751 if (RuntimeOption::EvalJitUseVtuneAPI
) {
752 reportTraceletToVtune(sk
.unit(), sk
.func(), tr
);
754 recordTranslationSizes(tr
);
756 fixups
.process(&tailBranches
);
760 * Add a translation to the corresponding SrcRec, effectively making it
761 * reachable. This should be done after the metadata for the translation has
764 void RegionTranslator::publishCodeImpl() {
765 const auto& loc
= transMeta
->range
.loc();
766 const auto srcRec
= srcDB().find(sk
);
767 always_assert(srcRec
);
768 assertx(checkLimit(kind
, srcRec
->numTrans()));
770 if (kind
== TransKind::Profile
) {
771 always_assert(region
);
772 profData()->addTransProfile(transId
, region
, pconds
,
773 transMeta
->range
.main
.size());
776 TRACE(1, "newTranslation: %p sk: %s\n",
777 entry(), showShort(sk
).c_str());
779 srcRec
->newTranslation(loc
, tailBranches
);
781 TRACE(1, "mcg: %u-byte translation (%u main, %u cold, %u frozen)\n",
782 loc
.mainSize() + loc
.coldCodeSize() + loc
.frozenCodeSize(),
783 loc
.mainSize(), loc
.coldCodeSize(), loc
.frozenCodeSize());
784 if (Trace::moduleEnabledRelease(Trace::tcspace
, 1)) {
785 Trace::traceRelease("%s", getTCSpace().c_str());
790 void RegionTranslator::setCachedForProcessFail() {
791 auto const srcRec
= srcDB().find(sk
);
792 always_assert(srcRec
);
794 auto const stub
= svcreq::emit_interp_no_translate_stub(spOff
, sk
);
795 TRACE(1, "setCachedForProcessFail: stub: %p sk: %s\n",
796 stub
, showShort(sk
).c_str());
799 srcRec
->smashFallbacksToStub(stub
);