Refine to bottom
[hiphop-php.git] / hphp / runtime / vm / jit / ir-builder.cpp
blobfda261377e3fa04664d0beab045dbf7987e0a4e6
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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/ir-builder.h"
18 #include <algorithm>
19 #include <utility>
21 #include <folly/ScopeGuard.h>
23 #include "hphp/util/trace.h"
24 #include "hphp/runtime/vm/jit/ir-unit.h"
25 #include "hphp/runtime/vm/jit/guard-relaxation.h"
26 #include "hphp/runtime/vm/jit/mutation.h"
27 #include "hphp/runtime/vm/jit/timer.h"
28 #include "hphp/runtime/vm/jit/translator.h"
29 #include "hphp/runtime/vm/jit/print.h"
30 #include "hphp/runtime/vm/jit/punt.h"
31 #include "hphp/runtime/base/rds.h"
32 #include "hphp/runtime/vm/jit/analysis.h"
33 #include "hphp/util/assertions.h"
35 namespace HPHP { namespace jit {
37 namespace {
39 TRACE_SET_MOD(hhir);
40 using Trace::Indent;
42 //////////////////////////////////////////////////////////////////////
44 template<typename M>
45 const typename M::mapped_type& get_required(const M& m,
46 typename M::key_type key) {
47 auto it = m.find(key);
48 always_assert(it != m.end());
49 return it->second;
52 SSATmp* fwdGuardSource(IRInstruction* inst) {
53 if (inst->is(AssertType, CheckType)) return inst->src(0);
55 assert(inst->is(AssertLoc, CheckLoc, AssertStk, CheckStk));
56 inst->convertToNop();
57 return nullptr;
60 //////////////////////////////////////////////////////////////////////
64 IRBuilder::IRBuilder(IRUnit& unit, BCMarker initMarker)
65 : m_unit(unit)
66 , m_initialMarker(initMarker)
67 , m_curMarker(initMarker)
68 , m_state(initMarker)
69 , m_curBlock(m_unit.entry())
70 , m_constrainGuards(shouldHHIRRelaxGuards())
72 m_state.setBuilding();
73 if (RuntimeOption::EvalHHIRGenOpts) {
74 m_enableCse = false; // CSE is disabled at gen time.
75 m_enableSimplification = RuntimeOption::EvalHHIRSimplification;
80 * Returns whether or not the given value might have its type relaxed by guard
81 * relaxation. If tmp is null, only conditions that apply to all values are
82 * checked.
84 bool IRBuilder::typeMightRelax(SSATmp* tmp /* = nullptr */) const {
85 return shouldConstrainGuards() && jit::typeMightRelax(tmp);
88 void IRBuilder::appendInstruction(IRInstruction* inst) {
89 FTRACE(1, " append {}\n", inst->toString());
91 if (shouldConstrainGuards()) {
92 // If we're constraining guards, some instructions need certain information
93 // to be recorded in side tables.
94 if (inst->is(AssertLoc, CheckLoc, LdLoc)) {
95 auto const locId = inst->extra<LocalId>()->locId;
96 m_constraints.typeSrcs[inst] = localTypeSources(locId);
97 if (inst->is(AssertLoc, CheckLoc)) {
98 m_constraints.prevTypes[inst] = localType(locId, DataTypeGeneric);
101 if (inst->is(AssertStk, CheckStk, LdStk)) {
102 auto const offset = inst->extra<StackOffset>()->offset;
103 m_constraints.typeSrcs[inst] = stackTypeSources(offset);
104 if (inst->is(AssertStk, CheckStk)) {
105 m_constraints.prevTypes[inst] = stackType(offset, DataTypeGeneric);
109 // And a LdRef or CheckRefInner automatically constrains the value to be a
110 // boxed cell, specifically.
111 if (inst->is(LdRef, CheckRefInner)) {
112 constrainValue(inst->src(0), DataTypeSpecific);
115 // In psuedomains we have to pre-constrain local guards, because we don't
116 // ever actually generate code that will constrain them otherwise.
117 // (Because of the LdLocPseudoMain stuff.)
118 if (inst->marker().func()->isPseudoMain() &&
119 inst->is(GuardLoc, CheckLoc)) {
120 constrainGuard(inst, DataTypeSpecific);
124 auto where = m_curBlock->end();
126 // If the block isn't empty, check if we need to create a new block.
127 if (where != m_curBlock->begin()) {
128 auto prevIt = where;
129 --prevIt;
130 auto& prev = *prevIt;
132 if (prev.isBlockEnd()) {
133 assert(where == m_curBlock->end());
135 auto oldBlock = m_curBlock;
137 // First make the inst's next block, so we can save state to it in
138 // finishBlock.
139 m_curBlock = m_unit.defBlock();
140 if (!prev.isTerminal()) {
141 // New block is reachable from old block so link it.
142 prev.setNext(m_curBlock);
143 m_curBlock->setHint(prev.block()->hint());
146 m_state.finishBlock(oldBlock);
148 m_state.startBlock(m_curBlock, inst->marker());
149 where = m_curBlock->begin();
151 FTRACE(2, "lazily adding B{}\n", m_curBlock->id());
156 assert(IMPLIES(inst->isBlockEnd(), where == m_curBlock->end()) &&
157 "Can't insert a BlockEnd instruction in the middle of a block");
158 if (do_assert && where != m_curBlock->begin()) {
159 UNUSED auto prevIt = where;
160 --prevIt;
161 assert(!prevIt->isBlockEnd() &&
162 "Can't append an instruction after a BlockEnd instruction");
165 assert(inst->marker().valid());
166 if (!inst->is(Nop, DefConst)) {
167 where = m_curBlock->insert(where, inst);
168 ++where;
171 m_state.update(inst);
172 cseUpdate(*inst);
175 void IRBuilder::appendBlock(Block* block) {
176 m_state.finishBlock(m_curBlock);
178 FTRACE(2, "appending B{}\n", block->id());
179 // Load up the state for the new block.
180 m_state.startBlock(block, m_curMarker);
181 m_curBlock = block;
184 void IRBuilder::setGuardFailBlock(Block* block) {
185 m_guardFailBlock = block;
188 void IRBuilder::resetGuardFailBlock() {
189 m_guardFailBlock = nullptr;
192 Block* IRBuilder::guardFailBlock() const {
193 return m_guardFailBlock;
196 //////////////////////////////////////////////////////////////////////
198 SSATmp* IRBuilder::preOptimizeCheckTypeOp(IRInstruction* inst, Type oldType) {
199 auto const typeParam = inst->typeParam();
201 if (!oldType.maybe(typeParam)) {
202 /* This check will always fail. It's probably due to an incorrect
203 * prediction. Generate a Jmp and return the src. The fact that the type
204 * will be slightly off is ok because all the code after the Jmp is
205 * unreachable. */
206 gen(Jmp, inst->taken());
207 return fwdGuardSource(inst);
210 auto const newType = oldType & inst->typeParam();
212 if (oldType <= newType) {
213 /* The type of the src is the same or more refined than type, so the guard
214 * is unnecessary. */
215 return fwdGuardSource(inst);
218 return nullptr;
221 SSATmp* IRBuilder::preOptimizeCheckType(IRInstruction* inst) {
222 return preOptimizeCheckTypeOp(inst, inst->src(0)->type());
225 SSATmp* IRBuilder::preOptimizeCheckStk(IRInstruction* inst) {
226 auto const offset = inst->extra<CheckStk>()->offset;
228 if (auto const prevValue = stackValue(offset, DataTypeGeneric)) {
229 gen(CheckType, inst->typeParam(), inst->taken(), prevValue);
230 inst->convertToNop();
231 return nullptr;
234 return preOptimizeCheckTypeOp(
235 inst,
236 stackType(offset, DataTypeGeneric)
240 SSATmp* IRBuilder::preOptimizeCheckLoc(IRInstruction* inst) {
241 auto const locId = inst->extra<CheckLoc>()->locId;
243 if (auto const prevValue = localValue(locId, DataTypeGeneric)) {
244 gen(CheckType, inst->typeParam(), inst->taken(), prevValue);
245 inst->convertToNop();
246 return nullptr;
249 return preOptimizeCheckTypeOp(
250 inst,
251 localType(locId, DataTypeGeneric)
255 SSATmp* IRBuilder::preOptimizeHintLocInner(IRInstruction* inst) {
256 auto const locId = inst->extra<HintLocInner>()->locId;
257 if (!(localType(locId, DataTypeGeneric) <= Type::BoxedCell) ||
258 predictedInnerType(locId).box() <= inst->typeParam()) {
259 inst->convertToNop();
260 return nullptr;
262 return nullptr;
265 SSATmp* IRBuilder::preOptimizeAssertTypeOp(IRInstruction* inst,
266 const Type oldType,
267 SSATmp* oldVal,
268 const IRInstruction* typeSrc) {
269 ITRACE(3, "preOptimizeAssertTypeOp({}, {}, {}, {})\n",
270 *inst, oldType,
271 oldVal ? oldVal->toString() : "nullptr",
272 typeSrc ? typeSrc->toString() : "nullptr");
273 auto const typeParam = inst->typeParam();
275 if (!oldType.maybe(typeParam)) {
276 // If both types are boxed this is ok and even expected as a means to
277 // update the hint for the inner type.
278 if (oldType.isBoxed() && typeParam.isBoxed()) return nullptr;
280 // We got external information (probably from static analysis) that
281 // conflicts with what we've built up so far. There's no reasonable way to
282 // continue here: we can't properly fatal the request because we can't make
283 // a catch trace or SpillStack without HhbcTranslator, we can't punt on
284 // just this instruction because we might not be in the initial translation
285 // phase, and we can't just plow on forward since we'll probably generate
286 // malformed IR. Since this case is very rare, just punt on the whole trace
287 // so it gets interpreted.
288 TRACE_PUNT("Invalid AssertTypeOp");
291 // Asserting in these situations doesn't add any information.
292 if (typeParam == Type::Cls && oldType <= Type::Cls) return inst->src(0);
293 if (typeParam == Type::Gen && oldType <= Type::Gen) return inst->src(0);
295 auto const newType = oldType & typeParam;
297 if (oldType <= newType) {
298 // oldType is at least as good as the new type. Eliminate this
299 // AssertTypeOp, but only if the src type won't relax, or the source value
300 // is another assert that's good enough. We do this to avoid eliminating
301 // apparently redundant assert opcodes that may become useful after prior
302 // guards are relaxed.
303 if (!typeMightRelax(oldVal) ||
304 (typeSrc && typeSrc->is(AssertType, AssertLoc, AssertStk) &&
305 typeSrc->typeParam() <= inst->typeParam())) {
306 return fwdGuardSource(inst);
309 if (oldType < newType) {
310 // This can happen because of limitations in how Type::operator& (used in
311 // refinedType()) handles specialized types: sometimes it returns a Type
312 // that's wider than it could be. It shouldn't affect correctness but it
313 // can cause us to miss out on some perf.
314 ITRACE(1, "Suboptimal AssertTypeOp: refineType({}, {}) -> {} in {}\n",
315 oldType, typeParam, newType, *inst);
317 // We don't currently support intersecting RepoAuthType::Arrays
318 // (t4473238), so we might be here because oldType and typeParam have
319 // different RATArrays. If that's the case and typeParam provides no
320 // other useful information we can unconditionally eliminate this
321 // instruction: RATArrays never come from guards so we can't miss out on
322 // anything by doing so.
323 if (oldType < Type::Arr &&
324 oldType.arrSpec().type() &&
325 typeParam < Type::Arr &&
326 typeParam.arrSpec().type() &&
327 !typeParam.arrSpec().kind()) {
328 return fwdGuardSource(inst);
333 return nullptr;
336 SSATmp* IRBuilder::preOptimizeAssertType(IRInstruction* inst) {
337 auto const src = inst->src(0);
338 return preOptimizeAssertTypeOp(inst, src->type(), src, src->inst());
341 static const IRInstruction* singleTypeSrcInst(const TypeSourceSet& typeSrcs) {
342 if (typeSrcs.size() == 1) {
343 auto typeSrc = *typeSrcs.begin();
344 return typeSrc.isValue() ? typeSrc.value->inst() :
345 typeSrc.isGuard() ? typeSrc.guard :
346 nullptr;
348 return nullptr;
351 // TODO(#5868904): almost the same as AssertLoc now; combine
352 SSATmp* IRBuilder::preOptimizeAssertStk(IRInstruction* inst) {
353 auto const offset = inst->extra<AssertStk>()->offset;
355 if (auto const prevValue = stackValue(offset, DataTypeGeneric)) {
356 gen(AssertType, inst->typeParam(), prevValue);
357 inst->convertToNop();
358 return nullptr;
361 // If the value has a single type-source instruction, pass it along to
362 // preOptimizeAssertTypeOp, which may be able to use it to optimize away the
363 // AssertStk.
364 auto const typeSrcInst = singleTypeSrcInst(stackTypeSources(offset));
366 return preOptimizeAssertTypeOp(
367 inst,
368 stackType(offset, DataTypeGeneric),
369 stackValue(offset, DataTypeGeneric),
370 typeSrcInst
374 SSATmp* IRBuilder::preOptimizeAssertLoc(IRInstruction* inst) {
375 auto const locId = inst->extra<AssertLoc>()->locId;
377 if (auto const prevValue = localValue(locId, DataTypeGeneric)) {
378 gen(AssertType, inst->typeParam(), prevValue);
379 inst->convertToNop();
380 return nullptr;
383 // If the local has a single type-source instruction, pass it along
384 // to preOptimizeAssertTypeOp, which may be able to use it to
385 // optimize away the AssertLoc.
386 auto const typeSrcInst = singleTypeSrcInst(localTypeSources(locId));
388 return preOptimizeAssertTypeOp(
389 inst,
390 localType(locId, DataTypeGeneric),
391 localValue(locId, DataTypeGeneric),
392 typeSrcInst
396 SSATmp* IRBuilder::preOptimizeCheckCtxThis(IRInstruction* inst) {
397 if (m_state.thisAvailable()) inst->convertToNop();
398 return nullptr;
401 SSATmp* IRBuilder::preOptimizeLdCtx(IRInstruction* inst) {
402 auto const fpInst = inst->src(0)->inst();
404 // Change LdCtx in static functions to LdCctx, or if we're inlining try to
405 // fish out a constant context.
406 auto const func = inst->marker().func();
407 if (func->isStatic()) {
408 if (fpInst->is(DefInlineFP)) {
409 auto const ctx = fpInst->extra<DefInlineFP>()->ctx;
410 if (ctx->isConst(Type::Cls)) {
411 inst->convertToNop();
412 return m_unit.cns(ConstCctx::cctx(ctx->clsVal()));
416 // ActRec->m_cls of a static function is always a valid class pointer with
417 // the bottom bit set
418 auto const src = inst->src(0);
419 inst->convertToNop();
420 return gen(LdCctx, src);
423 if (fpInst->is(DefInlineFP)) {
424 // TODO(#5623596): this optimization required for correctness in refcount
425 // opts right now.
426 // check that we haven't nuked the SSATmp
427 if (!m_state.frameMaySpanCall()) {
428 auto const ctx = fpInst->extra<DefInlineFP>()->ctx;
429 if (ctx->isA(Type::Obj)) return ctx;
432 return nullptr;
435 SSATmp* IRBuilder::preOptimizeDecRefThis(IRInstruction* inst) {
437 * If $this is available, convert to an instruction sequence that doesn't
438 * need to test $this is available. (Hopefully we CSE the load, too.)
440 if (thisAvailable()) {
441 auto const ctx = gen(LdCtx, m_state.fp());
442 auto const thiss = gen(CastCtxThis, ctx);
443 gen(DecRef, thiss);
444 inst->convertToNop();
447 return nullptr;
450 SSATmp* IRBuilder::preOptimizeLdLoc(IRInstruction* inst) {
451 auto const locId = inst->extra<LdLoc>()->locId;
452 if (auto tmp = localValue(locId, DataTypeGeneric)) return tmp;
454 auto const type = localType(locId, DataTypeGeneric);
456 // The types may not be compatible in the presence of unreachable code.
457 // Unreachable code elimination will take care of it later.
458 if (!type.maybe(inst->typeParam())) {
459 inst->setTypeParam(Type::Bottom);
460 return nullptr;
462 // If FrameStateMgr's type isn't as good as the type param, we're missing
463 // information in the IR.
464 assert(inst->typeParam() >= type);
465 inst->setTypeParam(std::min(type, inst->typeParam()));
467 if (typeMightRelax()) return nullptr;
468 if (inst->typeParam().isConst()) {
469 return m_unit.cns(inst->typeParam());
472 return nullptr;
475 SSATmp* IRBuilder::preOptimizeStLoc(IRInstruction* inst) {
476 // Guard relaxation might change the current local type, so don't try to
477 // change to StLocNT until after relaxation happens.
478 if (typeMightRelax()) return nullptr;
480 auto locId = inst->extra<StLoc>()->locId;
481 auto const curType = localType(locId, DataTypeGeneric);
482 auto const newType = inst->src(1)->type();
485 * There's no need to store the type if it's going to be the same
486 * KindOfFoo. We'll still have to store string types because we
487 * aren't specific about storing KindOfStaticString
488 * vs. KindOfString, and a Type::Null might mean KindOfUninit or
489 * KindOfNull.
491 auto const bothBoxed = curType.isBoxed() && newType.isBoxed();
492 auto const sameUnboxed = [&] {
493 auto avoidable = { Type::Uninit,
494 Type::InitNull,
495 Type::Bool,
496 Type::Int,
497 Type::Dbl,
498 // No strings.
499 Type::Arr,
500 Type::Obj,
501 Type::Res };
502 for (auto t : avoidable) {
503 if (curType <= t && newType <= t) return true;
505 return false;
507 if (bothBoxed || sameUnboxed()) {
508 inst->setOpcode(StLocNT);
511 return nullptr;
514 SSATmp* IRBuilder::preOptimizeCastStk(IRInstruction* inst) {
515 auto const curType = stackType(
516 inst->extra<CastStk>()->offset,
517 DataTypeGeneric
519 auto const curVal = stackValue(
520 inst->extra<CastStk>()->offset,
521 DataTypeGeneric
523 if (typeMightRelax(curVal)) return nullptr;
524 if (inst->typeParam() == Type::NullableObj && curType <= Type::Null) {
525 // If we're casting Null to NullableObj, we still need to call
526 // tvCastToNullableObjectInPlace. See comment there and t3879280 for
527 // details.
528 return nullptr;
530 if (curType <= inst->typeParam()) {
531 inst->convertToNop();
532 return nullptr;
534 return nullptr;
537 SSATmp* IRBuilder::preOptimizeCoerceStk(IRInstruction* inst) {
538 auto const curType = stackType(
539 inst->extra<CoerceStk>()->offset,
540 DataTypeGeneric
542 auto const curVal = stackValue(
543 inst->extra<CoerceStk>()->offset,
544 DataTypeGeneric
546 if (typeMightRelax(curVal)) return nullptr;
547 if (curType <= inst->typeParam()) {
548 inst->convertToNop();
549 return nullptr;
551 return nullptr;
554 SSATmp* IRBuilder::preOptimizeLdStk(IRInstruction* inst) {
555 auto const offset = inst->extra<LdStk>()->offset;
556 if (auto tmp = stackValue(offset, DataTypeGeneric)) {
557 gen(TakeStk, tmp);
558 return tmp;
560 // The types may not be compatible in the presence of unreachable code.
561 // Don't try to optimize the code in this case, and just let
562 // unreachable code elimination take care of it later.
563 auto const type = stackType(offset, DataTypeGeneric);
564 if (!type.maybe(inst->typeParam())) return nullptr;
565 inst->setTypeParam(std::min(type, inst->typeParam()));
567 if (typeMightRelax()) return nullptr;
568 if (inst->typeParam().isConst()) {
569 return m_unit.cns(inst->typeParam());
572 return nullptr;
575 SSATmp* IRBuilder::preOptimize(IRInstruction* inst) {
576 #define X(op) case op: return preOptimize##op(inst);
577 switch (inst->op()) {
578 X(CheckType)
579 X(CheckStk)
580 X(CheckLoc)
581 X(HintLocInner)
582 X(AssertLoc)
583 X(AssertStk)
584 X(AssertType)
585 X(CheckCtxThis)
586 X(LdCtx)
587 X(DecRefThis)
588 X(LdLoc)
589 X(StLoc)
590 X(CastStk)
591 X(CoerceStk)
592 X(LdStk)
593 default: break;
595 #undef X
596 return nullptr;
599 //////////////////////////////////////////////////////////////////////
602 * Performs simplification and CSE on the input instruction. If the input
603 * instruction has a dest, this will return an SSATmp that represents the same
604 * value as dst(0) of the input instruction. If the input instruction has no
605 * dest, this will return nullptr.
607 * The caller never needs to clone or append; all this has been done.
609 SSATmp* IRBuilder::optimizeInst(IRInstruction* inst,
610 CloneFlag doClone,
611 Block* srcBlock,
612 const folly::Optional<IdomVector>& idoms) {
613 static DEBUG_ONLY __thread int instNest = 0;
614 if (debug) ++instNest;
615 SCOPE_EXIT { if (debug) --instNest; };
616 DEBUG_ONLY auto indent = [&] { return std::string(instNest * 2, ' '); };
618 FTRACE(1, "optimize: {}\n", inst->toString());
620 auto doCse = [&] (IRInstruction* cseInput) -> SSATmp* {
621 if (m_enableCse && cseInput->canCSE()) {
622 if (auto const cseResult = cseLookup(*cseInput, srcBlock, idoms)) {
623 // Found a dominating instruction that can be used instead of input
624 FTRACE(1, " {}cse found: {}\n",
625 indent(), cseResult->inst()->toString());
627 assert(!cseInput->consumesReferences());
628 if (cseInput->producesReference(0)) {
629 // Replace with an IncRef
630 FTRACE(1, " {}cse of refcount-producing instruction\n", indent());
631 gen(IncRef, cseResult);
633 return cseResult;
636 return nullptr;
639 auto cloneAndAppendOriginal = [&] () -> SSATmp* {
640 if (inst->op() == Nop) return nullptr;
641 if (auto cseResult = doCse(inst)) {
642 return cseResult;
644 if (doClone == CloneFlag::Yes) {
645 inst = m_unit.cloneInstruction(inst);
647 appendInstruction(inst);
648 return inst->dst(0);
651 // Since some of these optimizations inspect tracked state, we don't
652 // perform any of them on non-main traces.
653 if (m_savedBlocks.size() > 0) return cloneAndAppendOriginal();
655 // copy propagation on inst source operands
656 copyProp(inst);
658 // First pass of IRBuilder optimizations try to replace an
659 // instruction based on tracked state before we do anything else.
660 // May mutate the IRInstruction in place (and return nullptr) or
661 // return an SSATmp*.
662 if (auto const preOpt = preOptimize(inst)) {
663 FTRACE(1, " {}preOptimize returned: {}\n",
664 indent(), preOpt->inst()->toString());
665 return preOpt;
667 if (inst->op() == Nop) return cloneAndAppendOriginal();
669 if (!m_enableSimplification) {
670 return cloneAndAppendOriginal();
673 auto const simpResult = simplify(m_unit, inst, shouldConstrainGuards());
675 // These are the possible outputs:
677 // ([], nullptr): no optimization possible. Use original inst.
679 // ([], non-nullptr): passing through a src. Don't CSE.
681 // ([X, ...], Y): throw away input instruction, append 'X, ...' (CSEing
682 // as we go), return Y.
684 if (!simpResult.instrs.empty()) {
685 // New instructions were generated. Append the new ones, filtering out Nops.
686 for (auto* newInst : simpResult.instrs) {
687 assert(!newInst->isTransient());
688 if (newInst->op() == Nop) continue;
690 auto cseResult = doCse(newInst);
691 if (cseResult) {
692 appendInstruction(m_unit.mov(newInst->dst(), cseResult,
693 newInst->marker()));
694 } else {
695 appendInstruction(newInst);
699 return simpResult.dst;
702 // No new instructions were generated. Either simplification didn't do
703 // anything, or we're using some other instruction's dst instead of our own.
705 if (simpResult.dst) {
706 // We're using some other instruction's output. Don't append anything, and
707 // don't do any CSE.
708 assert(simpResult.dst->inst() != inst);
709 return simpResult.dst;
712 // No simplification happened.
713 return cloneAndAppendOriginal();
716 void IRBuilder::prepareForNextHHBC() {
717 assert(
718 syncedSpLevel() + m_state.evalStack().size() - m_state.stackDeficit() ==
719 m_curMarker.spOff()
721 m_exnStack = ExnStackState {
722 m_state.spOffset(),
723 m_state.syncedSpLevel(),
724 m_state.stackDeficit(),
725 m_state.evalStack(),
726 m_state.sp()
730 void IRBuilder::exceptionStackBoundary() {
732 * If this assert fires, we're trying to put things on the stack in a catch
733 * trace that the unwinder won't be able to see.
735 assert(
736 syncedSpLevel() + m_state.evalStack().size() - m_state.stackDeficit() ==
737 m_curMarker.spOff()
739 m_exnStack.spOffset = m_state.spOffset();
740 m_exnStack.syncedSpLevel = m_state.syncedSpLevel();
741 m_exnStack.stackDeficit = m_state.stackDeficit();
742 m_exnStack.evalStack = m_state.evalStack();
743 m_exnStack.sp = m_state.sp();
747 * reoptimize() runs a trace through a second pass of IRBuilder
748 * optimizations, like this:
750 * reset state.
751 * move all blocks to a temporary list.
752 * compute immediate dominators.
753 * for each block in trace order:
754 * if we have a snapshot state for this block:
755 * clear cse entries that don't dominate this block.
756 * use snapshot state.
757 * move all instructions to a temporary list.
758 * for each instruction:
759 * optimizeWork - do CSE and simplify again
760 * if not simplified:
761 * append existing instruction and update state.
762 * else:
763 * if the instruction has a result, insert a mov from the
764 * simplified tmp to the original tmp and discard the instruction.
765 * if the last conditional branch was turned into a jump, remove the
766 * fall-through edge to the next block.
768 void IRBuilder::reoptimize() {
769 Timer _t(Timer::optimize_reoptimize);
770 FTRACE(5, "ReOptimize:vvvvvvvvvvvvvvvvvvvv\n");
771 SCOPE_EXIT { FTRACE(5, "ReOptimize:^^^^^^^^^^^^^^^^^^^^\n"); };
772 always_assert(m_savedBlocks.empty());
774 if (splitCriticalEdges(m_unit)) {
775 printUnit(6, m_unit, "after splitting critical edges for reoptimize");
778 m_state = FrameStateMgr{m_initialMarker};
779 m_cseHash.clear();
780 m_enableCse = RuntimeOption::EvalHHIRCse;
781 m_enableSimplification = RuntimeOption::EvalHHIRSimplification;
782 if (!m_enableCse && !m_enableSimplification) return;
783 setConstrainGuards(false);
785 // We need to use fixed-point for loops, otherwise legacy reoptimize will not
786 // handle the CFG's back-edges correctly.
787 auto const use_fixed_point = RuntimeOption::EvalJitLoops;
789 auto blocksIds = rpoSortCfgWithIds(m_unit);
790 auto const idoms = findDominators(m_unit, blocksIds);
791 boost::dynamic_bitset<> reachable(m_unit.numBlocks());
792 reachable.set(m_unit.entry()->id());
794 if (use_fixed_point) {
795 m_state.computeFixedPoint(blocksIds);
796 } else {
797 m_state.setLegacyReoptimize();
800 for (auto block : blocksIds.blocks) {
801 ITRACE(5, "reoptimize entering block: {}\n", block->id());
802 Indent _i;
804 // Skip block if it's unreachable.
805 if (!reachable.test(block->id())) continue;
807 if (use_fixed_point) {
808 m_state.loadBlock(block);
809 } else {
810 m_state.startBlock(block, block->front().marker());
812 m_curBlock = block;
814 auto nextBlock = block->next();
815 auto backMarker = block->back().marker();
816 auto instructions = block->moveInstrs();
817 assert(block->empty());
818 while (!instructions.empty()) {
819 auto* inst = &instructions.front();
820 instructions.pop_front();
822 // optimizeWork below may create new instructions, and will use
823 // m_nextMarker to decide where they are. Use the marker from this
824 // instruction.
825 assert(inst->marker().valid());
826 setCurMarker(inst->marker());
828 auto const tmp = optimizeInst(inst, CloneFlag::No, block, idoms);
829 SSATmp* dst = inst->dst(0);
831 if (dst != tmp) {
832 // The result of optimization has a different destination than the inst.
833 // Generate a mov(tmp->dst) to get result into dst.
834 assert(inst->op() != DefLabel);
835 assert(block->empty() || !block->back().isBlockEnd() || inst->next());
836 auto src = tmp->inst()->is(Mov) ? tmp->inst()->src(0) : tmp;
837 if (inst->next()) {
838 // If the last instruction is a guard, insert the mov on the
839 // fall-through edge (inst->next()).
840 auto nextBlk = inst->next();
841 nextBlk->insert(nextBlk->begin(),
842 m_unit.mov(dst, src, inst->marker()));
843 } else {
844 appendInstruction(m_unit.mov(dst, src, inst->marker()));
848 if (inst->block() == nullptr && inst->isBlockEnd()) {
849 // We're not re-adding the block-end instruction. Unset its edges.
850 inst->setTaken(nullptr);
851 inst->setNext(nullptr);
855 if (block->empty() || !block->back().isBlockEnd()) {
856 // Our block-end instruction was eliminated (most likely a Jmp* converted
857 // to a nop). Replace it with a jump to the next block.
858 appendInstruction(m_unit.gen(Jmp, backMarker, nextBlock));
860 assert(block->back().isBlockEnd());
861 if (!block->back().isTerminal() && !block->next()) {
862 // We converted the block-end instruction to a different one.
863 // Set its next block appropriately.
864 block->back().setNext(nextBlock);
866 // Mark successor blocks as reachable.
867 if (block->back().next()) reachable.set(block->back().next()->id());
868 if (block->back().taken()) reachable.set(block->back().taken()->id());
869 if (!use_fixed_point) {
870 m_state.finishBlock(block);
876 * Returns true iff a guard to constrain was found, and tc was more specific
877 * than the guard's existing constraint. Note that this doesn't necessarily
878 * mean that the guard was constrained: tc.weak might be true.
880 bool IRBuilder::constrainGuard(const IRInstruction* inst, TypeConstraint tc) {
881 if (!shouldConstrainGuards()) return false;
883 auto& guard = m_constraints.guards[inst];
884 auto newTc = applyConstraint(guard, tc);
885 ITRACE(2, "constrainGuard({}, {}): {} -> {}\n", *inst, tc, guard, newTc);
886 Indent _i;
888 auto const changed = guard != newTc;
889 if (!tc.weak) guard = newTc;
891 return changed;
895 * Trace back to the guard that provided the type of val, if
896 * any. Constrain it so its type will not be relaxed beyond the given
897 * DataTypeCategory. Returns true iff one or more guard instructions
898 * were constrained.
900 bool IRBuilder::constrainValue(SSATmp* const val, TypeConstraint tc) {
901 if (!shouldConstrainGuards() || tc.empty()) return false;
903 if (!val) {
904 ITRACE(1, "constrainValue(nullptr, {}), bailing\n", tc);
905 return false;
908 ITRACE(1, "constrainValue({}, {})\n", *val->inst(), tc);
909 Indent _i;
911 auto inst = val->inst();
912 if (inst->is(LdLoc, LdStk)) {
913 // If the value's type source is non-null and not a FramePtr, it's a real
914 // value that was killed by a Call. The value won't be live but it's ok to
915 // use it to track down the guard.
917 always_assert_flog(m_constraints.typeSrcs.count(inst),
918 "No typeSrcs found for {}", *inst);
920 auto const typeSrcs = get_required(m_constraints.typeSrcs, inst);
922 bool changed = false;
923 for (auto typeSrc : typeSrcs) {
924 if (typeSrc.isGuard()) {
925 if (inst->is(LdLoc)) {
926 changed = constrainSlot(inst->extra<LdLoc>()->locId, typeSrc, tc,
927 "constrainValueLoc") || changed;
928 } else {
929 assert(inst->is(LdStk));
930 changed = constrainSlot(inst->extra<LdStk>()->offset, typeSrc, tc,
931 "constrainValueStk") || changed;
933 } else {
934 // Keep chasing down the source of val.
935 assert(typeSrc.isValue());
936 changed = constrainValue(typeSrc.value, tc) || changed;
939 return changed;
942 if (inst->is(AssertType)) {
943 // Sometimes code in HhbcTranslator asks for a value with DataTypeSpecific
944 // but can tolerate a less specific value. If that happens, there's nothing
945 // to constrain.
946 if (!typeFitsConstraint(val->type(), tc)) return false;
948 // If the immutable typeParam fits the constraint, we're done.
949 auto const typeParam = inst->typeParam();
950 if (typeFitsConstraint(typeParam, tc)) return false;
952 auto const newTc = relaxConstraint(tc, typeParam, inst->src(0)->type());
953 ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
954 *inst, tc, newTc);
955 return constrainValue(inst->src(0), newTc);
958 if (inst->is(CheckType)) {
959 // Sometimes code in HhbcTranslator asks for a value with DataTypeSpecific
960 // but can tolerate a less specific value. If that happens, there's nothing
961 // to constrain.
962 if (!typeFitsConstraint(val->type(), tc)) return false;
964 bool changed = false;
965 auto const typeParam = inst->typeParam();
966 auto const srcType = inst->src(0)->type();
968 // Constrain the guard on the CheckType, but first relax the constraint
969 // based on what's known about srcType.
970 auto const guardTc = relaxConstraint(tc, srcType, typeParam);
971 changed = constrainGuard(inst, guardTc) || changed;
973 // Relax typeParam with its current constraint. This is used below to
974 // recursively relax the constraint on the source, if needed.
975 auto constraint = applyConstraint(m_constraints.guards[inst], guardTc);
976 auto const knownType = relaxType(typeParam, constraint);
978 if (!typeFitsConstraint(knownType, tc)) {
979 auto const newTc = relaxConstraint(tc, knownType, srcType);
980 ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
981 *inst, tc, newTc);
982 changed = constrainValue(inst->src(0), newTc) || changed;
984 return changed;
987 if (inst->isPassthrough()) {
988 return constrainValue(inst->getPassthroughValue(), tc);
991 if (inst->is(DefLabel)) {
992 auto changed = false;
993 auto dst = 0;
994 for (; dst < inst->numDsts(); dst++) {
995 if (val == inst->dst(dst)) break;
997 assert(dst != inst->numDsts());
998 for (auto& pred : inst->block()->preds()) {
999 assert(pred.inst()->is(Jmp));
1000 auto src = pred.inst()->src(dst);
1001 changed = constrainValue(src, tc) || changed;
1003 return changed;
1006 // Any instructions not special cased above produce a new value, so
1007 // there's no guard for us to constrain.
1008 ITRACE(2, "value is new in this trace, bailing\n");
1009 return false;
1012 bool IRBuilder::constrainLocal(uint32_t locId,
1013 TypeConstraint tc,
1014 const std::string& why) {
1015 if (!shouldConstrainGuards() || tc.empty()) return false;
1016 bool changed = false;
1017 for (auto typeSrc : localTypeSources(locId)) {
1018 changed = constrainSlot(locId, typeSrc, tc, why + "Loc") || changed;
1020 return changed;
1023 bool IRBuilder::constrainStack(int32_t offset, TypeConstraint tc) {
1024 if (!shouldConstrainGuards() || tc.empty()) return false;
1025 auto changed = false;
1026 for (auto typeSrc : stackTypeSources(offset)) {
1027 changed = constrainSlot(offset, typeSrc, tc, "Stk") || changed;
1029 return changed;
1032 // Constrains a local or stack slot.
1033 bool IRBuilder::constrainSlot(int32_t idOrOffset,
1034 TypeSource typeSrc,
1035 TypeConstraint tc,
1036 const std::string& why) {
1037 if (!shouldConstrainGuards() || tc.empty()) return false;
1039 ITRACE(1, "constrainSlot({}, {}, {}, {})\n",
1040 idOrOffset, show(typeSrc), tc, why);
1041 Indent _i;
1043 if (typeSrc.isValue()) return constrainValue(typeSrc.value, tc);
1045 assert(typeSrc.isGuard());
1046 auto const guard = typeSrc.guard;
1048 if (guard->is(GuardLoc, GuardStk)) {
1049 ITRACE(2, "found guard to constrain\n");
1050 return constrainGuard(guard, tc);
1053 always_assert(guard->is(AssertLoc, CheckLoc, AssertStk, CheckStk));
1055 // If the dest of the Assert/Check doesn't fit tc there's no point in
1056 // continuing.
1057 auto prevType = get_required(m_constraints.prevTypes, guard);
1058 if (!typeFitsConstraint(prevType & guard->typeParam(), tc)) {
1059 return false;
1062 if (guard->is(AssertLoc, AssertStk)) {
1063 // If the immutable typeParam fits the constraint, we're done.
1064 auto const typeParam = guard->typeParam();
1065 if (typeFitsConstraint(typeParam, tc)) return false;
1067 auto const newTc = relaxConstraint(tc, typeParam, prevType);
1068 ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
1069 *guard, tc, newTc);
1070 bool changed = false;
1071 auto typeSrcs = get_required(m_constraints.typeSrcs, guard);
1072 for (auto typeSrc : typeSrcs) {
1073 changed = constrainSlot(idOrOffset, typeSrc, newTc, why) || changed;
1075 return changed;
1078 // guard is a CheckLoc or CheckStk.
1079 auto changed = false;
1080 auto const typeParam = guard->typeParam();
1082 // Constrain the guard on the Check instruction, but first relax the
1083 // constraint based on what's known about prevType.
1084 auto const guardTc = relaxConstraint(tc, prevType, typeParam);
1085 changed = constrainGuard(guard, guardTc) || changed;
1087 // Relax typeParam with its current constraint. This is used below to
1088 // recursively relax the constraint on the source, if needed.
1089 auto constraint = applyConstraint(m_constraints.guards[guard], guardTc);
1090 auto const knownType = relaxType(typeParam, constraint);
1092 if (!typeFitsConstraint(knownType, tc)) {
1093 auto const newTc = relaxConstraint(tc, knownType, prevType);
1094 ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
1095 *guard, tc, newTc);
1096 auto typeSrcs = get_required(m_constraints.typeSrcs, guard);
1097 for (auto typeSrc : typeSrcs) {
1098 changed = constrainSlot(idOrOffset, typeSrc, newTc, why) || changed;
1101 return changed;
1104 Type IRBuilder::localType(uint32_t id, TypeConstraint tc) {
1105 constrainLocal(id, tc, "localType");
1106 return m_state.localType(id);
1109 Type IRBuilder::predictedInnerType(uint32_t id) {
1110 auto const ty = m_state.predictedLocalType(id);
1111 assert(ty.isBoxed());
1112 return ldRefReturn(ty.unbox());
1115 Type IRBuilder::stackInnerTypePrediction(int32_t offset) const {
1116 auto const ty = m_state.predictedStackType(offset);
1117 assert(ty.isBoxed());
1118 return ldRefReturn(ty.unbox());
1121 Type IRBuilder::predictedLocalType(uint32_t id) {
1122 return m_state.predictedLocalType(id);
1125 SSATmp* IRBuilder::localValue(uint32_t id, TypeConstraint tc) {
1126 constrainLocal(id, tc, "localValue");
1127 return m_state.localValue(id);
1130 SSATmp* IRBuilder::stackValue(int32_t offset, TypeConstraint tc) {
1131 auto const val = m_state.stackValue(offset);
1132 if (val) constrainValue(val, tc);
1133 return val;
1136 Type IRBuilder::stackType(int32_t offset, TypeConstraint tc) {
1137 constrainStack(offset, tc);
1138 return m_state.stackType(offset);
1141 void IRBuilder::setCurMarker(BCMarker newMarker) {
1142 if (newMarker == m_curMarker) return;
1143 FTRACE(2, "IRBuilder changing current marker from {} to {}\n",
1144 m_curMarker.valid() ? m_curMarker.show() : "<invalid>",
1145 newMarker.show());
1146 assert(newMarker.valid());
1147 m_curMarker = newMarker;
1150 bool IRBuilder::startBlock(Block* block, const BCMarker& marker,
1151 bool isLoopHeader) {
1152 assert(block);
1153 assert(m_savedBlocks.empty()); // No bytecode control flow in exits.
1155 if (block == m_curBlock) return true;
1157 // Return false if we don't have a state for block. This can happen
1158 // when trying to start a region block that turned out to be unreachable.
1159 if (!m_state.hasStateFor(block)) return false;
1161 // There's no reason for us to be starting on the entry block when it's not
1162 // our current block.
1163 always_assert(!block->isEntry());
1165 auto& lastInst = m_curBlock->back();
1166 always_assert(lastInst.isBlockEnd());
1167 always_assert(lastInst.isTerminal() || m_curBlock->next() != nullptr);
1169 m_state.finishBlock(m_curBlock);
1170 m_curBlock = block;
1172 m_state.startBlock(m_curBlock, marker, isLoopHeader);
1173 if (sp() == nullptr) gen(DefSP, StackOffset{spOffset()}, fp());
1174 always_assert(fp() != nullptr);
1176 FTRACE(2, "IRBuilder switching to block B{}: {}\n", block->id(),
1177 show(m_state));
1178 return true;
1181 Block* IRBuilder::makeBlock(Offset offset) {
1182 auto it = m_offsetToBlockMap.find(offset);
1183 if (it == m_offsetToBlockMap.end()) {
1184 auto* block = m_unit.defBlock();
1185 m_offsetToBlockMap.insert(std::make_pair(offset, block));
1186 return block;
1188 return it->second;
1191 void IRBuilder::resetOffsetMapping() {
1192 m_offsetToBlockMap.clear();
1195 bool IRBuilder::hasBlock(Offset offset) const {
1196 return m_offsetToBlockMap.count(offset);
1199 void IRBuilder::setBlock(Offset offset, Block* block) {
1200 assert(!hasBlock(offset));
1201 m_offsetToBlockMap[offset] = block;
1204 void IRBuilder::pushBlock(BCMarker marker, Block* b) {
1205 FTRACE(2, "IRBuilder saving {}@{} and using {}@{}\n",
1206 m_curBlock, m_curMarker.show(), b, marker.show());
1207 assert(b);
1209 m_savedBlocks.push_back(
1210 BlockState { m_curBlock, m_curMarker, m_exnStack }
1212 m_state.pauseBlock(m_curBlock);
1213 m_state.startBlock(b, marker);
1214 m_curBlock = b;
1215 m_curMarker = marker;
1217 if (do_assert) {
1218 for (UNUSED auto const& state : m_savedBlocks) {
1219 assert(state.block != b &&
1220 "Can't push a block that's already in the saved stack");
1225 void IRBuilder::popBlock() {
1226 assert(!m_savedBlocks.empty());
1228 auto const& top = m_savedBlocks.back();
1229 FTRACE(2, "IRBuilder popping {}@{} to restore {}@{}\n",
1230 m_curBlock, m_curMarker.show(), top.block, top.marker.show());
1231 m_state.finishBlock(m_curBlock);
1232 m_state.unpauseBlock(top.block);
1233 m_curBlock = top.block;
1234 m_curMarker = top.marker;
1235 m_exnStack = top.exnStack;
1236 m_savedBlocks.pop_back();
1239 //////////////////////////////////////////////////////////////////////
1241 CSEHash& IRBuilder::cseHashTable(const IRInstruction& inst) {
1242 return inst.is(DefConst) ? m_unit.constTable() : m_cseHash;
1245 const CSEHash& IRBuilder::cseHashTable(const IRInstruction& inst) const {
1246 return const_cast<IRBuilder*>(this)->cseHashTable(inst);
1249 SSATmp* IRBuilder::cseLookup(const IRInstruction& inst,
1250 const Block* srcBlock,
1251 const folly::Optional<IdomVector>& idoms) const {
1252 auto tmp = cseHashTable(inst).lookup(const_cast<IRInstruction*>(&inst));
1253 if (tmp && idoms) {
1254 // During a reoptimize pass, we need to make sure that any values we want
1255 // to reuse for CSE are only reused in blocks dominated by the block that
1256 // defines it.
1257 if (tmp->inst()->is(DefConst)) return tmp;
1258 auto const dom = findDefiningBlock(tmp);
1259 if (!dom || !dominates(dom, srcBlock, *idoms)) return nullptr;
1261 return tmp;
1264 void IRBuilder::cseUpdate(const IRInstruction& inst) {
1265 switch (inst.op()) {
1266 case Call:
1267 case CallArray:
1268 case ContEnter:
1269 m_cseHash.clear();
1270 break;
1271 default:
1272 break;
1275 if (m_enableCse && inst.canCSE()) {
1276 cseHashTable(inst).insert(inst.dst());
1278 if (inst.killsSources()) {
1279 for (auto i = uint32_t{0}; i < inst.numSrcs(); ++i) {
1280 if (inst.killsSource(i) && inst.src(i)->inst()->canCSE()) {
1281 cseHashTable(inst).erase(inst.src(i));
1287 //////////////////////////////////////////////////////////////////////