folly::Optional<AliasClass> AliasClass::precise_union(AliasClass o) const { if (o <= *this) return *this; if (*this <= o) return o; auto const unioned = static_cast<rep>(m_bits | o.m_bits); // For a precise union, we need to make sure the returned class is not any // bigger than it should be. This means we can't deal with situations where // we have disjoint stags, and right now we also don't try to deal with // situations that have the same stag in a combinable way. (E.g. two // adjacent AStack ranges.) auto const stag1 = m_stag; auto const stag2 = o.m_stag; if (stag1 == STag::None && stag2 == STag::None) { return AliasClass{unioned}; } if (stag1 == STag::None && stag2 != STag::None) { return o.precise_union(*this); // flip args } assertx(stag1 != STag::None); if (stag2 != STag::None) return folly::none; if (o.m_bits & stagBit(stag1)) return folly::none; // Keep the data and stag from this, but change its bits. auto ret = *this; ret.m_bits = unioned; assertx(ret.m_stag == stag1); return ret; }
bool AliasClass::operator<=(AliasClass o) const { if (m_bits == BEmpty) return true; auto const isect = static_cast<rep>(m_bits & o.m_bits); if (isect != m_bits) return false; // If they have the same specialized tag, then since isect is equal to // m_bits, the stagBit must be part of the intersection or be BEmpty. This // means they can only be in a subclass relationship if that specialized data // is. if (m_stag == o.m_stag) return subclassData(o); /* * Disjoint stags. The stagBit for m_stag must be part of the intersection * (or be BEmpty), since isect == m_bits above. This breaks down into the * following cases: * * If the osbit is part of the intersection, then this can't be a subclass of * `o', because this has only generic information for that bit but it is set * in isect. If the osbit is BEmpty, osbit & isect is zero, which avoids * this case. * * The remaining situations are that m_stag is STag::None, in which case it * is a subclass since osbit wasn't in the intersection. Or that m_stag has * a bit that is in the isect (since m_bits == isect), and that bit is set in * o.m_bits. In either case this is a subclass, so we can just return true. */ auto const osbit = stagBit(o.m_stag); if (osbit & isect) return false; return true; }
bool AliasClass::checkInvariants() const { switch (m_stag) { case STag::None: break; case STag::Frame: break; case STag::Prop: break; case STag::ElemI: break; case STag::Stack: assertx(m_stack.base->type() <= TStkPtr || m_stack.base->type() <= TFramePtr); assertx(m_stack.size != 0); // use AEmpty if you want that assertx(m_stack.size > 0); break; case STag::ElemS: assertx(m_elemS.key->isStatic()); break; case STag::MIState: break; case STag::Ref: assertx(m_ref.boxed->isA(TBoxedCell)); break; } assertx(m_bits & stagBit(m_stag)); return true; }
AliasClass AliasClass::operator|(AliasClass o) const { if (auto const c = precise_union(o)) return *c; auto const unioned = static_cast<rep>(m_bits | o.m_bits); // If they have the same stag, try to merge them with unionData. auto stag1 = m_stag; auto stag2 = o.m_stag; if (stag1 == stag2) return unionData(unioned, *this, o); // If one of the alias classes have a non-None stag, we can only keep it if // the other doesn't have the corresponding bit set. if (stag1 != STag::None && (o.m_bits & stagBit(stag1))) stag1 = STag::None; if (stag2 != STag::None && (m_bits & stagBit(stag2))) stag2 = STag::None; auto ret = AliasClass{unioned}; if (stag1 == stag2) return ret; // both None. // Note: union operations are guaranteed to be commutative, so if there are // two non-None stags, we have to consistently choose between them. For now // we keep the one with a smaller `rep' value, instead of discarding both. const AliasClass* chosen = &o; auto stag = stag2; if (stag1 != STag::None) { if (stag2 == STag::None || stagBit(stag1) < stagBit(stag2)) { chosen = this; stag = stag1; } } switch (stag) { case STag::None: break; case STag::Frame: new (&ret.m_frame) AFrame(chosen->m_frame); break; case STag::Prop: new (&ret.m_prop) AProp(chosen->m_prop); break; case STag::ElemI: new (&ret.m_elemI) AElemI(chosen->m_elemI); break; case STag::ElemS: new (&ret.m_elemS) AElemS(chosen->m_elemS); break; case STag::Stack: new (&ret.m_stack) AStack(chosen->m_stack); break; case STag::MIState: new (&ret.m_mis) AMIState(chosen->m_mis); break; case STag::Ref: new (&ret.m_ref) ARef(chosen->m_ref); break; } ret.m_stag = stag; return ret; }
/* * TODO(#2884927): we probably need to be aware of possibly-integer-like string * keys here before we can start using ElemS for anything. (Or else ensure * that we never use ElemS with an integer-like string.) */ bool AliasClass::maybe(AliasClass o) const { auto const isect = static_cast<rep>(m_bits & o.m_bits); if (isect == 0) return false; if (*this <= o || o <= *this) return true; /* * If we have the same stag, then the cases are either the stag is in the * intersection or not. If it's not (including if it was BEmpty), we already * know the intersection is non-empty and return true. * * If it is in the intersection, and it is not the only relevant bit, then we * still have a non-empty intersection. * * Finally if it's in the intersection and the only isect bit, then we need * to see if the data is in a maybe relationship. */ if (m_stag == o.m_stag) { auto const bit = stagBit(m_stag); assertx(isect != 0); if ((bit & isect) == isect) return maybeData(o); return true; } /* * The stags are different. However, since isect is non-empty, there are * three cases: * * o One of the stag bits is not in the intersection, and thus not * relevant. Since the other stag bit is in the intersection, but * didn't have specialized information from one of the intersectees, the * intersection is non-empty and we should return true. * * o Both of the stag bits are not in the intersection, and thus not * relevant. Some other bit was set, since we already checked that * isect was non-zero, so we should return true. * * o Both of the stag bits are in the intersection. But each intersectee * therefore had only generic information for the bit of the other, so * the intersection is non-empty, and we should return true. * * All of these cases are handled by the following statement: */ return true; }
bool AliasClass::checkInvariants() const { switch (m_stag) { case STag::None: break; case STag::Frame: break; case STag::Prop: break; case STag::ElemI: break; case STag::Stack: assertx(m_stack.size > 0); break; case STag::ElemS: assertx(m_elemS.key->isStatic()); break; case STag::MIState: break; case STag::Ref: assertx(m_ref.boxed->isA(TBoxedCell)); break; } assertx(m_bits & stagBit(m_stag)); return true; }
AliasClass AliasClass::operator|(AliasClass o) const { if (o <= *this) return *this; if (*this <= o) return o; auto const unioned = static_cast<rep>(m_bits | o.m_bits); // Note: union operations are guaranteed to be commutative, so if there are // two non-None stags, we have to consistently choose between them. For now // we throw both away in any case where they differ, and try to merge them // with unionData if they are the same. If only one had an stag, we can keep // it only if the other didn't have that bit set. auto const stag1 = m_stag; auto const stag2 = o.m_stag; if (stag1 == stag2) return unionData(unioned, *this, o); if (stag1 != STag::None && stag2 != STag::None) { return AliasClass{unioned}; } if (stag2 != STag::None) return o | *this; if (o.m_bits & stagBit(stag1)) { return AliasClass{unioned}; } auto ret = AliasClass{unioned}; switch (stag1) { case STag::None: break; case STag::Frame: new (&ret.m_frame) AFrame(m_frame); break; case STag::Prop: new (&ret.m_prop) AProp(m_prop); break; case STag::ElemI: new (&ret.m_elemI) AElemI(m_elemI); break; case STag::ElemS: new (&ret.m_elemS) AElemS(m_elemS); break; case STag::Stack: new (&ret.m_stack) AStack(m_stack); break; case STag::MIState: new (&ret.m_mis) AMIState(m_mis); break; } ret.m_stag = stag1; return ret; }