Exemple #1
0
Type Type::combine(bits_t newBits, Type other) const {
  static_assert(std::is_same<Oper, Union>::value ||
                std::is_same<Oper, Intersect>::value,
                "Type::combine given unsupported template argument");

  // If neither type is specialized, the result is simple.
  if (LIKELY(!isSpecialized() && !other.isSpecialized())) {
    return Type(newBits);
  }

  // If one of the types can't be specialized while the other is specialized,
  // preserve the specialization.
  if (!canSpecializeAny() || !other.canSpecializeAny()) {
    auto const specType = isSpecialized() ? specializedType()
                                          : other.specializedType();

    // If the specialized type doesn't exist in newBits, drop the
    // specialization.
    if (newBits & specType.m_bits) return Type(newBits, specType.m_extra);
    return Type(newBits);
  }

  // If both types are eligible for the same kind of specialization and at
  // least one is specialized, delegate to Oper::combineSame.
  if (canSpecializeClass() && other.canSpecializeClass()) {
    folly::Optional<const Class*> aClass, bClass;
    if (getClass()) aClass = getClass();
    if (other.getClass()) bClass = other.getClass();

    return Oper::template combineSame<ClassOps>(newBits, kAnyObj,
                                                aClass, bClass);
  }

  if (canSpecializeArrayKind() && other.canSpecializeArrayKind()) {
    folly::Optional<ArrayData::ArrayKind> aKind, bKind;
    if (hasArrayKind()) aKind = getArrayKind();
    if (other.hasArrayKind()) bKind = other.getArrayKind();

    return Oper::template combineSame<ArrayOps>(newBits, kAnyArr, aKind, bKind);
  }

  // The types are eligible for different kinds of specialization and at least
  // one is specialized, so delegate to Oper::combineDifferent.
  return Oper::combineDifferent(newBits, *this, other);
}
Exemple #2
0
RuntimeType Type::toRuntimeType() const {
  assert(!isPtr());
  auto const outer = isBoxed() ? KindOfRef : toDataType();
  auto const inner = isBoxed() ? innerType().toDataType() : KindOfNone;
  auto rtt = RuntimeType{outer, inner};

  if (isSpecialized()) {
    if (subtypeOf(Type::Arr)) {
      return rtt.setArrayKind(getArrayKind());
    } else if (subtypeOf(Type::Obj)) {
      return rtt.setKnownClass(getClass());
    }
  }

  return rtt;
}
Exemple #3
0
Type Type::operator-(Type other) const {
  auto const newBits = m_bits & ~other.m_bits;
  auto const spec1 = isSpecialized();
  auto const spec2 = other.isSpecialized();

  // The common easy case is when neither type is specialized.
  if (LIKELY(!spec1 && !spec2)) return Type(newBits);

  if (spec1 && spec2) {
    if (canSpecializeClass() != other.canSpecializeClass()) {
      // Both are specialized but in different ways. Our specialization is
      // preserved.
      return Type(newBits, m_extra);
    }

    // Subtracting different specializations of the same type could get messy
    // so we don't support it for now.
    always_assert(specializedType() == other.specializedType() &&
                  "Incompatible specialized types given to operator-");

    // If we got here, both types have the same specialization, so it's removed
    // from the result.
    return Type(newBits);
  }

  // If masking out other's bits removed all of the bits that correspond to our
  // specialization, take it out. Otherwise, preserve it.
  if (spec1) {
    if (canSpecializeClass()) {
      if (!(newBits & kAnyObj)) return Type(newBits);
      return Type(newBits, m_class);
    }
    if (canSpecializeArrayKind()) {
      if (!(newBits & kAnyArr)) return Type(newBits);
      return Type(newBits, m_arrayKind);
    }
    not_reached();
  }

  // Only other is specialized. This is where things get a little fuzzy. We
  // want to be able to support things like Obj - Obj<C> but we can't represent
  // Obj<~C>. We compromise and return Bottom in cases like this, which means
  // we need to be careful because (a - b) == Bottom doesn't imply a <= b in
  // this world.
  if (other.canSpecializeClass()) return Type(newBits & ~kAnyObj);
  return Type(newBits & ~kAnyArr);
}
Exemple #4
0
Type Type::operator-(Type other) const {
  auto const newBits = m_bits & ~other.m_bits;

  if (m_hasConstVal) {
    // If other is a constant of the same type, the result is Bottom or this
    // depending on whether or not it's the same constant.
    if (other.m_bits == m_bits && other.m_hasConstVal) {
      return other.m_extra == m_extra ? Bottom : *this;
    }

    // Otherwise, just check to see if the constant's type was removed in
    // newBits.
    return (newBits & m_bits) ? *this : Bottom;
  }

  // Rather than try to represent types like "all Ints except 24", treat t -
  // Int<24> as t - Int.
  other = other.dropConstVal();

  auto const spec1 = isSpecialized();
  auto const spec2 = other.isSpecialized();

  // The common easy case is when neither type is specialized.
  if (LIKELY(!spec1 && !spec2)) return Type(newBits);

  if (spec1 && spec2) {
    if (canSpecializeClass() != other.canSpecializeClass()) {
      // Both are specialized but in different ways. Our specialization is
      // preserved.
      return Type(newBits, m_extra);
    }

    // Subtracting different specializations of the same type could get messy
    // so we don't support it for now.
    always_assert(specializedType() == other.specializedType() &&
                  "Incompatible specialized types given to operator-");

    // If we got here, both types have the same specialization, so it's removed
    // from the result.
    return Type(newBits);
  }

  // If masking out other's bits removed all of the bits that correspond to our
  // specialization, take it out. Otherwise, preserve it.
  if (spec1) {
    if (canSpecializeClass()) {
      if (!(newBits & kAnyObj)) return Type(newBits);
      return Type(newBits, m_class);
    }
    if (canSpecializeArrayKind()) {
      if (!(newBits & kAnyArr)) return Type(newBits);
      return Type(newBits, getArrayKind());
    }
    not_reached();
  }

  // Only other is specialized. This is where things get a little fuzzy. We
  // want to be able to support things like Obj - Obj<C> but we can't represent
  // Obj<~C>. We compromise and return Bottom in cases like this, which means
  // we need to be careful because (a - b) == Bottom doesn't imply a <= b in
  // this world.
  if (other.canSpecializeClass()) return Type(newBits & ~kAnyObj);
  return Type(newBits & ~kAnyArr);
}
Exemple #5
0
bool Type::subtypeOf(Type t2) const {
  // First, check for any members in m_bits that aren't in t2.m_bits.
  if ((m_bits & t2.m_bits) != m_bits) return false;

  // If t2 is a constant, we must be the same constant or Bottom.
  if (t2.m_hasConstVal) {
    assert(!t2.isUnion());
    return m_bits == kBottom || (m_hasConstVal && m_extra == t2.m_extra);
  }

  // If t2 is specialized, we must either not be eligible for the same kind of
  // specialization (Int <= {Int|Arr<Packed>}) or have a specialization that is
  // a subtype of t2's specialization.
  if (t2.isSpecialized()) {
    if (t2.canSpecializeClass()) {
      if (!isSpecialized()) return false;

      //  Obj=A <:  Obj=A
      // Obj<=A <: Obj<=A
      if (m_class.isExact() == t2.m_class.isExact() &&
          getClass() == t2.getClass()) {
        return true;
      }

      //      A <: B
      // ----------------
      //  Obj=A <: Obj<=B
      // Obj<=A <: Obj<=B
      if (!t2.m_class.isExact()) return getClass()->classof(t2.getClass());
      return false;
    }

    assert(t2.canSpecializeArray());
    if (!canSpecializeArray()) return true;
    if (!isSpecialized()) return false;

    // Both types are specialized Arr types. "Specialized" in this context
    // means it has at least one of a RepoAuthType::Array* or (const ArrayData*
    // or ArrayData::ArrayKind). We may return false erroneously in cases where
    // a 100% accurate comparison of the specializations would be prohibitively
    // expensive.
    if (m_arrayInfo == t2.m_arrayInfo) return true;
    auto rat1 = getArrayType();
    auto rat2 = t2.getArrayType();

    if (rat1 != rat2 && !(rat1 && !rat2)) {
      // Different rats are only ok if rat1 is present and rat2 isn't. It's
      // possible for one rat to be a subtype of another rat or array kind, but
      // checking that can be very expensive.
      return false;
    }

    auto kind1 = getOptArrayKind();
    auto kind2 = t2.getOptArrayKind();
    assert(kind1 || kind2);
    if (kind1 && !kind2) return true;
    if (kind2 && !kind1) return false;
    if (*kind1 != *kind2) return false;

    // Same kinds but we still have to check for const arrays. a <= b iff they
    // have the same const array or a has a const array and b doesn't. If they
    // have the same non-nullptr const array the m_arrayInfo check up above
    // should've triggered.
    auto const1 = isConst() ? arrVal() : nullptr;
    auto const2 = t2.isConst() ? t2.arrVal() : nullptr;
    assert((!const1 && !const2) || const1 != const2);
    return const1 == const2 || (const1 && !const2);
  }

  return true;
}
Exemple #6
0
void cgCheckType(IRLS& env, const IRInstruction* inst) {
  // Note: If you add new supported type checks, you should update
  // negativeCheckType() to indicate whether it is precise or not.
  auto const src = inst->src(0);
  auto const dst = inst->dst();
  auto const srcData = srcLoc(env, inst, 0).reg(0);
  auto const srcType = srcLoc(env, inst, 0).reg(1);

  auto& v = vmain(env);

  auto const doJcc = [&] (ConditionCode cc, Vreg sf) {
    fwdJcc(v, env, ccNegate(cc), sf, inst->taken());
  };

  auto const doMov = [&] {
    auto const dstData = dstLoc(env, inst, 0).reg(0);
    auto const dstType = dstLoc(env, inst, 0).reg(1);

    if (dst->isA(TBool) && !src->isA(TBool)) {
      v << movtqb{srcData, dstData};
    } else {
      v << copy{srcData, dstData};
    }
    if (dstType == InvalidReg) return;
    if (srcType != InvalidReg) {
      v << copy{srcType, dstType};
    } else {
      v << ldimmq{src->type().toDataType(), dstType};
    }
  };

  auto const typeParam = inst->typeParam();

  if (src->isA(typeParam)) {
    // src is the target type or better.  Just define our dst.
    doMov();
    return;
  }
  if (!src->type().maybe(typeParam)) {
    // src is definitely not the target type.  Always jump.
    v << jmp{label(env, inst->taken())};
    return;
  }

  if (srcType != InvalidReg) {
    emitTypeTest(v, env, typeParam, srcType, srcData, v.makeReg(), doJcc);
    doMov();
    return;
  }

  if (src->type() <= TBoxedCell && typeParam <= TBoxedCell) {
    // We should never have specific known Boxed types; those should only be
    // used for hints and predictions.
    always_assert(!(typeParam < TBoxedInitCell));
    doMov();
    return;
  }

  /*
   * See if we're just checking the array kind or object class of a value with
   * a mostly-known type.
   *
   * Important: We don't support typeParam being something like
   * StaticArr=kPackedKind unless the src->type() also already knows its
   * staticness.  We do allow things like CheckType<Arr=Packed> t1:StaticArr,
   * though.  This is why we have to check that the unspecialized type is at
   * least as big as the src->type().
   */
  if (typeParam.isSpecialized() &&
      typeParam.unspecialize() >= src->type()) {
    detail::emitSpecializedTypeTest(v, env, typeParam, srcData,
                                    v.makeReg(), doJcc);
    doMov();
    return;
  }

  /*
   * Since not all of our unions carry a type register, there are some
   * situations with strings and arrays that are neither constantly-foldable
   * nor in the emitTypeTest() code path.
   *
   * We currently actually check their persistent bit here, which will let
   * both static and uncounted strings through.  Also note that
   * CheckType<Uncounted> t1:{Null|Str} doesn't get this treatment currently---
   * the emitTypeTest() path above will only check the type register.
   */
  if (!typeParam.isSpecialized() &&
      typeParam <= TUncounted &&
      src->type().subtypeOfAny(TStr, TArr) &&
      src->type().maybe(typeParam)) {
    assertx(src->type().maybe(TPersistent));

    auto const sf = v.makeReg();
    v << cmplim{0, srcData[FAST_REFCOUNT_OFFSET], sf};
    doJcc(CC_L, sf);
    doMov();
    return;
  }

  always_assert_flog(
    false,
    "Bad src: {} and dst: {} types in '{}'",
    src->type(), typeParam, *inst
  );
}