hphp/runtime/vm/jit/ir-builder.cpp
/**
 * Trace back to the guard that provided the type of val, if
 * any. Constrain it so its type will not be relaxed beyond the given
 * DataTypeCategory. Returns true iff one or more guard instructions
 * were constrained.
 */
Trace back to the guard that provided the type of val, if
any. Constrain it so its type will not be relaxed beyond the given
DataTypeCategory. Returns true iff one or more guard instructions
were constrained.
Trace back to the guard that provided the type of val if any . Constrain it so its type will not be relaxed beyond the given DataTypeCategory . Returns true iff one or more guard instructions were constrained .
bool IRBuilder::constrainValue(SSATmp* const val, TypeConstraint tc) {
  if (!shouldConstrainGuards() || tc.empty()) return false;
  if (!val) {
    ITRACE(1, "constrainValue(nullptr, {}), bailing\n", tc);
    return false;
  }
  ITRACE(1, "constrainValue({}, {})\n", *val->inst(), tc);
  Indent _i;
  auto inst = val->inst();
  if (inst->is(LdLoc)) {
    always_assert_flog(m_constraints.typeSrcs.count(inst),
                       "No typeSrcs found for {}\n\n{}", *inst, m_unit);
    auto const typeSrcs = get_required(m_constraints.typeSrcs, inst);
    bool changed = false;
    for (auto typeSrc : typeSrcs) {
      if (typeSrc.isGuard()) {
        changed = constrainLocal(inst->extra<LocalId>()->locId, typeSrc, tc,
                                  "constrainValue") || changed;
      } else {
        assert(typeSrc.isValue());
        changed = constrainValue(typeSrc.value, tc) || changed;
      }
    }
    return changed;
  } else if (inst->is(LdStack)) {
    return constrainStack(inst->src(0), inst->extra<StackOffset>()->offset, tc);
  } else if (inst->is(AssertType)) {
    if (!typeFitsConstraint(val->type(), tc)) return false;
    auto const typeParam = inst->typeParam();
    if (typeFitsConstraint(typeParam, tc)) return false;
    auto const newTc = relaxConstraint(tc, typeParam, inst->src(0)->type());
    ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
           *inst, tc, newTc);
    return constrainValue(inst->src(0), newTc);
  } else if (inst->is(CheckType)) {
    if (!typeFitsConstraint(val->type(), tc)) return false;
    bool changed = false;
    auto const typeParam = inst->typeParam();
    auto const srcType = inst->src(0)->type();
    auto const guardTc = relaxConstraint(tc, srcType, typeParam);
    changed = constrainGuard(inst, guardTc) || changed;
    auto constraint = applyConstraint(m_constraints.guards[inst], guardTc);
    auto const knownType = relaxType(typeParam, constraint);
    if (!typeFitsConstraint(knownType, tc)) {
      auto const newTc = relaxConstraint(tc, knownType, srcType);
      ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
             *inst, tc, newTc);
      changed = constrainValue(inst->src(0), newTc) || changed;
    }
    return changed;
  } else if (inst->isPassthrough()) {
    return constrainValue(inst->getPassthroughValue(), tc);
  } else if (inst->is(DefLabel)) {
    auto changed = false;
    auto dst = 0;
    for (; dst < inst->numDsts(); dst++) {
      if (val == inst->dst(dst)) break;
    }
    assert(dst != inst->numDsts());
    for (auto& pred : inst->block()->preds()) {
      assert(pred.inst()->is(Jmp));
      auto src = pred.inst()->src(dst);
      changed = constrainValue(src, tc) || changed;
    }
    return changed;
  } else {
    ITRACE(2, "value is new in this trace, bailing\n");
    return false;
  }
}
bool IRBuilder :: constrainValue ( SSATmp * const val , TypeConstraint tc ) { if ( ! shouldConstrainGuards ( ) || tc . empty ( ) ) return false ; if ( ! val ) { ITRACE ( 1 , " \n " , tc ) ; return false ; } ITRACE ( 1 , " \n " , * val -> inst ( ) , tc ) ; Indent _i ; auto inst = val -> inst ( ) ; if ( inst -> is ( LdLoc ) ) { always_assert_flog ( m_constraints . typeSrcs . count ( inst ) , " \n \n " , * inst , m_unit ) ; auto const typeSrcs = get_required ( m_constraints . typeSrcs , inst ) ; bool changed = false ; for ( auto typeSrc : typeSrcs ) { if ( typeSrc . isGuard ( ) ) { changed = constrainLocal ( inst -> extra < LocalId > ( ) -> locId , typeSrc , tc , " " ) || changed ; } else { assert ( typeSrc . isValue ( ) ) ; changed = constrainValue ( typeSrc . value , tc ) || changed ; } } return changed ; } else if ( inst -> is ( LdStack ) ) { return constrainStack ( inst -> src ( 0 ) , inst -> extra < StackOffset > ( ) -> offset , tc ) ; } else if ( inst -> is ( AssertType ) ) { if ( ! typeFitsConstraint ( val -> type ( ) , tc ) ) return false ; auto const typeParam = inst -> typeParam ( ) ; if ( typeFitsConstraint ( typeParam , tc ) ) return false ; auto const newTc = relaxConstraint ( tc , typeParam , inst -> src ( 0 ) -> type ( ) ) ; ITRACE ( 1 , " \n " , * inst , tc , newTc ) ; return constrainValue ( inst -> src ( 0 ) , newTc ) ; } else if ( inst -> is ( CheckType ) ) { if ( ! typeFitsConstraint ( val -> type ( ) , tc ) ) return false ; bool changed = false ; auto const typeParam = inst -> typeParam ( ) ; auto const srcType = inst -> src ( 0 ) -> type ( ) ; auto const guardTc = relaxConstraint ( tc , srcType , typeParam ) ; changed = constrainGuard ( inst , guardTc ) || changed ; auto constraint = applyConstraint ( m_constraints . guards [ inst ] , guardTc ) ; auto const knownType = relaxType ( typeParam , constraint ) ; if ( ! typeFitsConstraint ( knownType , tc ) ) { auto const newTc = relaxConstraint ( tc , knownType , srcType ) ; ITRACE ( 1 , " \n " , * inst , tc , newTc ) ; changed = constrainValue ( inst -> src ( 0 ) , newTc ) || changed ; } return changed ; } else if ( inst -> isPassthrough ( ) ) { return constrainValue ( inst -> getPassthroughValue ( ) , tc ) ; } else if ( inst -> is ( DefLabel ) ) { auto changed = false ; auto dst = 0 ; for ( ; dst < inst -> numDsts ( ) ; dst ++ ) { if ( val == inst -> dst ( dst ) ) break ; } assert ( dst != inst -> numDsts ( ) ) ; for ( auto & pred : inst -> block ( ) -> preds ( ) ) { assert ( pred . inst ( ) -> is ( Jmp ) ) ; auto src = pred . inst ( ) -> src ( dst ) ; changed = constrainValue ( src , tc ) || changed ; } return changed ; } else { ITRACE ( 2 , " \n " ) ; return false ; } }
Trace back to the guard that provided the type of val, if
 any.
Trace back to the guard that provided the type of val if any .
// We've hit a LdLoc. If the value's type source is non-null and not a // FramePtr, it's a real value that was killed by a Call. The value won't // be live but it's ok to use it to track down the guard. // Keep chasing down the source of val. // Sometimes code in HhbcTranslator asks for a value with DataTypeSpecific // but can tolerate a less specific value. If that happens, there's nothing // to constrain. // If the immutable typeParam fits the constraint, we're done. // Sometimes code in HhbcTranslator asks for a value with DataTypeSpecific // but can tolerate a less specific value. If that happens, there's nothing // to constrain. // Constrain the guard on the CheckType, but first relax the constraint // based on what's known about srcType. // Relax typeParam with its current constraint. This is used below to // recursively relax the constraint on the source, if needed. // Any instructions not special cased above produce a new value, so // there's no guard for us to constrain.