Beispiel #1
0
/*
 * relaxConstraint returns the least specific TypeConstraint 'tc' that doesn't
 * prevent the intersection of knownType and relaxType(toRelax, tc) from
 * satisfying origTc. It is used in IRBuilder::constrainValue and
 * IRBuilder::constrainStack to determine how to constrain the typeParam and
 * src values of CheckType/CheckStk instructions, and the src values of
 * AssertType/AssertStk instructions.
 *
 * AssertType example:
 * t24:Obj<C> = AssertType<{Obj<C>|InitNull}> t4:Obj
 *
 * If constrainValue is called with (t24, DataTypeSpecialized), relaxConstraint
 * will be called with (DataTypeSpecialized, Obj<C>|InitNull, Obj). After a few
 * iterations it will determine that constraining Obj with DataTypeCountness
 * will still allow the result type of the AssertType instruction to satisfy
 * DataTypeSpecialized, because relaxType(Obj, DataTypeCountness) == Obj.
 */
TypeConstraint relaxConstraint(const TypeConstraint origTc,
                               const Type knownType, const Type toRelax) {
  ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n",
         origTc, knownType, toRelax);
  Trace::Indent _i;

  auto const dstType = refineType(knownType, toRelax);
  always_assert_flog(typeFitsConstraint(dstType, origTc),
                     "refine({}, {}) doesn't fit {}",
                     knownType, toRelax, origTc);

  // Preserve origTc's weak property.
  TypeConstraint newTc{DataTypeGeneric, DataTypeGeneric};
  newTc.weak = origTc.weak;

  while (true) {
    if (newTc.isSpecialized()) {
      // We need to ask for the right kind of specialization, so grab it from
      // origTc.
      if (origTc.wantArrayKind()) newTc.setWantArrayKind();
      if (origTc.wantClass()) newTc.setDesiredClass(origTc.desiredClass());
    }

    auto const relaxed = relaxType(toRelax, newTc);
    auto const newDstType = refineType(relaxed, knownType);
    if (typeFitsConstraint(newDstType, origTc)) break;

    ITRACE(5, "newDstType = {}, newTc = {}; ", newDstType, newTc);
    if (newTc.category == DataTypeGeneric ||
        !typeFitsOuterConstraint(newDstType, origTc)) {
      FTRACE(5, "incrementing outer\n");
      incCategory(newTc.category);
    } else if (!typeFitsInnerConstraint(newDstType, origTc)) {
      FTRACE(5, "incrementing inner\n");
      incCategory(newTc.innerCat);
    } else {
      not_reached();
    }
  }

  ITRACE(4, "Returning {}\n", newTc);
  // newTc shouldn't be any more specific than origTc.
  always_assert(newTc.category <= origTc.category &&
                newTc.innerCat <= origTc.innerCat);
  return newTc;
}
Beispiel #2
0
GuardConstraint relaxConstraint(GuardConstraint origGc,
                                Type knownType, Type toRelax) {
  ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n",
         origGc, knownType, toRelax);
  Trace::Indent _i;

  // AssertType can be given TCtx, which should never relax.
  if (toRelax.maybe(TCctx)) {
    always_assert(toRelax <= TCtx);
    return origGc;
  }

  auto const dstType = knownType & toRelax;
  always_assert_flog(typeFitsConstraint(dstType, origGc),
                     "refine({}, {}) doesn't fit {}",
                     knownType, toRelax, origGc);

  // Preserve origGc's weak property.
  GuardConstraint newGc{DataTypeGeneric};
  newGc.weak = origGc.weak;

  while (true) {
    if (newGc.isSpecialized()) {
      // We need to ask for the right kind of specialization, so grab it from
      // origGc.
      if (origGc.wantArrayKind()) newGc.setWantArrayKind();
      if (origGc.wantClass()) newGc.setDesiredClass(origGc.desiredClass());
    }

    auto const relaxed = relaxType(toRelax, newGc.category);
    auto const newDstType = relaxed & knownType;
    if (typeFitsConstraint(newDstType, origGc)) break;

    ITRACE(5, "newDstType = {}, newGc = {}; incrementing constraint\n",
      newDstType, newGc);
    incCategory(newGc.category);
  }
  // DataTypeCountness can be relaxed to DataTypeGeneric in
  // optimizeProfiledGuards, so we can't rely on this category to give type
  // information through guards.  Since relaxConstraint is used to relax the
  // DataTypeCategory for guards, we cannot return DataTypeCountness unless we
  // already had it to start with.  Instead, we return DataTypeBoxCountness,
  // which won't be further relaxed by optimizeProfiledGuards.
  if (newGc.category == DataTypeCountness && origGc != DataTypeCountness) {
    newGc.category = DataTypeBoxAndCountness;
  }
  ITRACE(4, "Returning {}\n", newGc);
  // newGc shouldn't be any more specific than origGc.
  always_assert(newGc.category <= origGc.category);
  return newGc;
}
Beispiel #3
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;
}