Exemple #1
0
SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) {
  auto const locId = inst->extra<AssertLoc>()->locId;

  auto const prevType = localType(locId, DataTypeGeneric);
  auto const typeParam = inst->typeParam();

  if (prevType.not(typeParam)) {
    TRACE_PUNT("Invalid AssertLoc");
  }

  if (shouldElideAssertType(prevType, typeParam, nullptr)) {
    return inst->src(0);
  }

  if (filterAssertType(inst, prevType)) {
    constrainLocal(locId, categoryForType(prevType), "AssertLoc");
  }

  return nullptr;
}
Exemple #2
0
SSATmp* IRBuilder::preOptimizeAssertTypeOp(IRInstruction* inst,
                                           Type oldType,
                                           ConstraintFunc constrain) {
  auto const newType = inst->typeParam();
  if (oldType.not(newType)) {
    // If both types are boxed this is ok and even expected as a means to
    // update the hint for the inner type.
    if (oldType.isBoxed() && newType.isBoxed()) return nullptr;

    // We got external information (probably from static analysis) that
    // conflicts with what we've built up so far. There's no reasonable way to
    // continue here: we can't properly fatal the request because we can't make
    // a catch trace or SpillStack without HhbcTranslator, we can't punt on
    // just this instruction because we might not be in the initial translation
    // phase, and we can't just plow on forward since we'll probably generate
    // malformed IR. Since this case is very rare, just punt on the whole trace
    // so it gets interpreted.
    TRACE_PUNT("Invalid AssertTypeOp");
  }

  // Asserting in these situations doesn't add any information.
  if (oldType <= Type::Cls || newType == Type::Gen) return inst->src(0);

  // We're asserting a strict subtype of the old type, so keep the assert
  // around.
  if (newType < oldType) return nullptr;

  // oldType is at least as good as the new type. Kill this assert op but
  // preserve the type we were asserting in case the source type gets relaxed
  // past it.
  if (newType >= oldType) {
    constrain({DataTypeGeneric, newType});
    return inst->src(0);
  }

  // AssertLoc is special here because it's the one AssertTypeOp that doesn't
  // do its own filtering of the destination type based on the input type and
  // the asserted type. This will hopefully be fixed soon.
  if (inst->is(AssertLoc)) {
    // Now we're left with cases where neither type is a subtype of the other
    // but they have some nonzero intersection. We want to end up asserting the
    // intersection, but we have to constrain the input to avoid reintroducing
    // types that were removed from the original typeParam.
    auto const intersect = newType & oldType;
    inst->setTypeParam(intersect);

    TypeConstraint tc;
    if (intersect != newType) {
      Type relaxed;
      // Find the most general constraint that doesn't modify the type being
      // asserted.
      while ((relaxed = newType & relaxType(oldType, tc)) != intersect) {
        if (tc.category > DataTypeGeneric &&
            relaxed.maybeBoxed() && intersect.maybeBoxed() &&
            (relaxed & Type::Cell) == (intersect & Type::Cell)) {
          // If the inner type is why we failed, constrain that a level.
          incCategory(tc.innerCat);
        } else {
          incCategory(tc.category);
        }
      }
    }
    constrain(tc);
  }

  return nullptr;
}