2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/type.h"
19 #include "hphp/runtime/base/repo-auth-type-array.h"
20 #include "hphp/runtime/base/shape.h"
21 #include "hphp/runtime/base/struct-array.h"
22 #include "hphp/runtime/base/struct-array-defs.h"
23 #include "hphp/runtime/vm/jit/ir-opcode.h"
24 #include "hphp/runtime/vm/jit/ir-instruction.h"
25 #include "hphp/runtime/vm/jit/print.h"
26 #include "hphp/runtime/vm/jit/ssa-tmp.h"
27 #include "hphp/runtime/vm/jit/translator.h"
28 #include "hphp/runtime/vm/jit/minstr-effects.h"
30 #include "hphp/util/abi-cxx.h"
31 #include "hphp/util/text-util.h"
32 #include "hphp/util/trace.h"
34 #include <boost/algorithm/string/trim.hpp>
36 #include <folly/Conv.h>
37 #include <folly/Format.h>
38 #include <folly/MapUtil.h>
39 #include <folly/gen/Base.h>
44 namespace HPHP
{ namespace jit
{
48 std::string
Type::constValString() const {
49 if (*this <= TBottom
) return "Bottom";
50 if (*this <= TUninit
) return "Uninit";
51 if (*this <= TInitNull
) return "InitNull";
52 if (*this <= TNullptr
) return "Nullptr";
54 assertx(hasConstVal());
57 return m_boolVal
? "true" : "false";
60 return folly::format("{}", m_intVal
).str();
63 // don't format doubles as integers.
64 auto s
= folly::format("{}", m_dblVal
).str();
65 if (!strchr(s
.c_str(), '.') && !strchr(s
.c_str(), 'e')) {
66 return folly::format("{:.1f}", m_dblVal
).str();
70 if (*this <= TStaticStr
) {
72 return folly::format("\"{}\"", escapeStringForCPP(str
->data(),
75 if (*this <= TStaticArr
) {
76 if (m_arrVal
->empty()) {
79 return folly::format("Array({})", m_arrVal
).str();
82 return folly::format("Func({})", m_funcVal
? m_funcVal
->fullName()->data()
86 return folly::format("Cls({})", m_clsVal
? m_clsVal
->name()->data()
91 return "Cctx(Cls(nullptr))";
93 auto const cls
= m_cctxVal
.cls();
94 return folly::format("Cctx(Cls({}))", cls
->name()).str();
97 auto name
= getNativeFunctionName(m_tcaVal
);
98 const char* hphp
= "HPHP::";
100 if (!name
.compare(0, strlen(hphp
), hphp
)) {
101 name
= name
.substr(strlen(hphp
));
103 auto pos
= name
.find_first_of('(');
104 if (pos
!= std::string::npos
) {
105 name
= name
.substr(0, pos
);
107 return folly::format("TCA: {}({})", m_tcaVal
, boost::trim_copy(name
)).str();
109 if (*this <= TRDSHandle
) {
110 return folly::format("rds::Handle({:#x})", m_rdsHandleVal
).str();
112 if (*this <= TPtrToGen
) {
113 return folly::sformat("TV: {}", m_ptrVal
);
116 always_assert_flog(false, "Bad type in constValString(): {:#16x}:{:#16x}",
120 static std::string
show(Ptr ptr
) {
121 always_assert(ptrSubsetOf(ptr
, Ptr::Ptr
));
126 case Ptr::NotPtr
: not_reached();
127 case Ptr::Ptr
: return "";
129 #define PTRT(name, ...) case Ptr::name: return #name;
130 PTR_TYPES(PTRT
, PTR_R
)
134 std::vector
<const char*> parts
;
135 #define PTRT(name, ...) \
136 if (ptrSubsetOf(Ptr::name, ptr)) parts.emplace_back(#name);
137 PTR_PRIMITIVE(PTRT
, PTR_NO_R
)
139 return folly::sformat("{{{}}}", folly::join('|', parts
));
142 static const std::unordered_map
<Type
, const char*> s_typeNames
{
143 #define IRT(x, ...) {T##x, #x},
150 std::string
Type::toString() const {
151 // First, see if this is a predefined type.
152 auto it
= s_typeNames
.find(*this);
153 if (it
!= s_typeNames
.end()) return it
->second
;
155 if (maybe(TNullptr
)) {
156 return folly::to
<std::string
>(
158 (*this - TNullptr
).toString()
162 if (*this <= TBoxedCell
) {
163 return folly::to
<std::string
>("Boxed", inner().toString());
168 return folly::sformat("Cls={}", m_clsVal
->name()->data());
170 return folly::sformat("{}<{}>",
171 dropConstVal().toString(), constValString());
176 if (t
.maybe(TPtrToGen
)) {
177 assertx(!t
.m_hasConstVal
);
179 show(t
.ptrKind() & Ptr::Ptr
) +
180 (t
& TPtrToGen
).deref().toString();
183 if (t
!= TBottom
) ret
+= "|" + t
.toString();
187 assertx(ptrSubsetOf(t
.ptrKind(), Ptr::NotPtr
));
189 std::vector
<std::string
> parts
;
190 if (isSpecialized()) {
191 if (auto clsSpec
= t
.clsSpec()) {
192 auto const base
= Type(m_bits
& kClsSpecBits
, t
.ptrKind());
193 auto const exact
= clsSpec
.exact() ? "=" : "<=";
194 auto const name
= clsSpec
.cls()->name()->data();
195 auto const partStr
= folly::to
<std::string
>(base
.toString(), exact
, name
);
197 parts
.push_back(partStr
);
199 } else if (auto arrSpec
= t
.arrSpec()) {
200 auto str
= Type(m_bits
& kArrSpecBits
, t
.ptrKind()).toString();
201 if (auto const kind
= arrSpec
.kind()) {
203 str
+= ArrayData::kindToString(*kind
);
205 if (auto const ty
= arrSpec
.type()) {
206 str
+= folly::to
<std::string
>(':', show(*ty
));
208 if (auto const shape
= arrSpec
.shape()) {
209 str
+= folly::to
<std::string
>(":", show(*shape
));
211 parts
.push_back(str
);
218 // Concat all of the primitive types in the custom union type
219 # define IRT(name, ...) if (T##name <= t) parts.push_back(#name);
220 # define IRTP(name, ...)
225 assertx(!parts
.empty());
226 if (parts
.size() == 1) return parts
.front();
227 return folly::sformat("{{{}}}", folly::join('|', parts
));
230 std::string
Type::debugString(Type t
) {
234 ///////////////////////////////////////////////////////////////////////////////
236 bool Type::checkValid() const {
237 // Note: be careful, the TFoo objects aren't all constructed yet in this
240 assertx(((m_bits
& kClsSpecBits
) == 0 || (m_bits
& kArrSpecBits
) == 0) &&
241 "Conflicting specialization");
244 // We should have one canonical representation of Bottom.
245 if (m_bits
== kBottom
) {
246 assert_flog(*this == TBottom
,
247 "Bottom m_bits but nonzero others in {:#16x}:{:#16x}",
254 Type::bits_t
Type::bitsFromDataType(DataType outer
, DataType inner
) {
255 assertx(inner
!= KindOfRef
);
256 assertx(inner
== KindOfUninit
|| outer
== KindOfRef
);
259 case KindOfUninit
: return kUninit
;
260 case KindOfNull
: return kInitNull
;
261 case KindOfBoolean
: return kBool
;
262 case KindOfInt64
: return kInt
;
263 case KindOfDouble
: return kDbl
;
264 case KindOfPersistentString
: return kPersistentStr
;
265 case KindOfString
: return kStr
;
266 case KindOfPersistentArray
: return kPersistentArr
;
267 case KindOfArray
: return kArr
;
268 case KindOfResource
: return kRes
;
269 case KindOfObject
: return kObj
;
270 case KindOfClass
: return kCls
;
272 assertx(inner
!= KindOfUninit
);
273 return bitsFromDataType(inner
, KindOfUninit
) << kBoxShift
;
278 DataType
Type::toDataType() const {
279 assertx(!maybe(TPtrToGen
) || m_bits
== kBottom
);
280 assertx(isKnownDataType());
282 // Order is important here: types must progress from more specific
283 // to less specific to return the most specific DataType.
284 if (*this <= TUninit
) return KindOfUninit
;
285 if (*this <= TInitNull
) return KindOfNull
;
286 if (*this <= TBool
) return KindOfBoolean
;
287 if (*this <= TInt
) return KindOfInt64
;
288 if (*this <= TDbl
) return KindOfDouble
;
289 if (*this <= TPersistentStr
) return KindOfPersistentString
;
290 if (*this <= TStr
) return KindOfString
;
291 if (*this <= TPersistentArr
) return KindOfPersistentArray
;
292 if (*this <= TArr
) return KindOfArray
;
293 if (*this <= TObj
) return KindOfObject
;
294 if (*this <= TRes
) return KindOfResource
;
295 if (*this <= TBoxedCell
) return KindOfRef
;
296 if (*this <= TCls
) return KindOfClass
;
297 always_assert_flog(false,
298 "Bad Type {} in Type::toDataType()", *this);
301 ///////////////////////////////////////////////////////////////////////////////
304 Type
Type::specialize(TypeSpec spec
) const {
305 assertx(!spec
.arrSpec() || supports(SpecKind::Array
));
306 assertx(!spec
.clsSpec() || supports(SpecKind::Class
));
308 // If we don't have exactly one kind of specialization, or if our bits
309 // support both kinds, don't specialize.
310 if ((bool)spec
.arrSpec() == (bool)spec
.clsSpec() ||
311 (supports(SpecKind::Array
) && supports(SpecKind::Class
))) {
315 if (spec
.arrSpec() != ArraySpec::Bottom
) {
316 return Type
{*this, spec
.arrSpec()};
319 assertx(spec
.clsSpec() != ClassSpec::Bottom
);
320 return Type
{*this, spec
.clsSpec()};
323 // Return true if the array satisfies requirement on the ArraySpec.
324 static bool arrayFitsSpec(const ArrayData
* arr
, const ArraySpec spec
) {
325 if (spec
== ArraySpec::Top
) return true;
327 if (auto const spec_kind
= spec
.kind()) {
328 if (arr
->kind() == spec_kind
) return true;
331 if (auto const rat_type
= spec
.type()) {
332 using A
= RepoAuthType::Array
;
333 if (arr
->empty() && rat_type
->emptiness() != A::Empty::No
) return true;
334 if (arr
->isVectorData()) {
335 switch (rat_type
->tag()) {
337 if (arr
->size() != rat_type
->size()) break;
339 case A::Tag::PackedN
: {
341 for ( ; k
< arr
->size(); ++k
) {
342 auto const specElemType
=
343 rat_type
->tag() == A::Tag::Packed
? rat_type
->packedElem(k
)
344 : rat_type
->elemType();
345 if (!tvMatchesRepoAuthType(*(arr
->get(k
).asTypedValue()),
350 if (k
== arr
->size()) return true;
357 if (arr
->isStruct()) {
358 if (StructArray::asStructArray(arr
)->shape() == spec
.shape()) return true;
364 bool Type::operator<=(Type rhs
) const {
365 auto const lhs
= *this;
367 // Check for any members in lhs.m_bits that aren't in rhs.m_bits.
368 if ((lhs
.m_bits
& rhs
.m_bits
) != lhs
.m_bits
) {
372 // Check for Bottom; all the remaining cases assume `lhs' is not Bottom.
373 if (lhs
.m_bits
== kBottom
) return true;
375 // If `rhs' is a constant, we must be the same constant.
376 if (rhs
.m_hasConstVal
) {
377 assertx(!rhs
.isUnion());
378 return lhs
.m_hasConstVal
&& lhs
.m_extra
== rhs
.m_extra
;
381 // Make sure lhs's ptr kind is a subtype of rhs's.
382 if (!ptrSubsetOf(lhs
.ptrKind(), rhs
.ptrKind())) {
386 // If rhs isn't specialized no further checking is needed.
387 if (!rhs
.isSpecialized()) {
391 if (lhs
.hasConstVal(TArr
)) {
392 // Arrays can be specialized in different ways, here we check if the
393 // constant array fits the kind()/type() of the specialization of rhs, if
395 auto const lhs_arr
= lhs
.arrVal();
396 auto const rhs_as
= rhs
.arrSpec();
397 return arrayFitsSpec(lhs_arr
, rhs_as
);
400 // Compare specializations only if `rhs' is specialized.
401 return lhs
.spec() <= rhs
.spec();
404 Type
Type::operator|(Type rhs
) const {
407 if (lhs
== rhs
|| rhs
== TBottom
) return lhs
;
408 if (lhs
== TBottom
) return rhs
;
410 // Representing types like {Int<12>|Arr} could get messy and isn't useful in
411 // practice, so unless we hit one of the trivial cases above, drop the
412 // constant value(s).
413 lhs
= lhs
.dropConstVal();
414 rhs
= rhs
.dropConstVal();
416 auto const bits
= lhs
.m_bits
| rhs
.m_bits
;
417 auto const ptr
= lhs
.ptrKind() | rhs
.ptrKind();
418 auto const spec
= lhs
.spec() | rhs
.spec();
420 return Type
{bits
, ptr
}.specialize(spec
);
423 Type
Type::operator&(Type rhs
) const {
426 // When intersecting a constant value with another type, the result will be
427 // the constant value if the other value is a supertype of the constant, and
429 if (lhs
.m_hasConstVal
) return lhs
<= rhs
? lhs
: TBottom
;
430 if (rhs
.m_hasConstVal
) return rhs
<= lhs
? rhs
: TBottom
;
432 auto bits
= lhs
.m_bits
& rhs
.m_bits
;
433 auto ptr
= lhs
.ptrKind() & rhs
.ptrKind();
434 auto arrSpec
= lhs
.arrSpec() & rhs
.arrSpec();
435 auto clsSpec
= lhs
.clsSpec() & rhs
.clsSpec();
437 // Filter out bits and pieces that no longer exist due to other components
438 // going to Bottom, starting with bits.
439 if (ptr
== Ptr::Bottom
) bits
&= ~kGen
;
440 if (arrSpec
== ArraySpec::Bottom
) bits
&= ~kArrSpecBits
;
441 if (clsSpec
== ClassSpec::Bottom
) bits
&= ~kClsSpecBits
;
444 if ((bits
& kGen
) == 0) ptr
= Ptr::Bottom
;
447 if (!supports(bits
, SpecKind::Array
)) arrSpec
= ArraySpec::Bottom
;
448 if (!supports(bits
, SpecKind::Class
)) clsSpec
= ClassSpec::Bottom
;
450 return Type
{bits
, ptr
}.specialize({arrSpec
, clsSpec
});
453 Type
Type::operator-(Type rhs
) const {
455 if (rhs
== TBottom
) return lhs
;
456 if (lhs
<= rhs
) return TBottom
;
457 if (lhs
.hasConstVal()) return lhs
; // not covered by rhs.
459 // If `rhs' has a constant value, but `lhs' doesn't, conservatively return
460 // `lhs', rather than trying to represent things like "everything except
461 // Int<24>". Boolean is a special case.
462 if (rhs
.m_hasConstVal
) {
463 if (rhs
<= TBool
&& lhs
<= TBool
) {
464 auto const res
= !rhs
.boolVal();
465 if (lhs
.hasConstVal() && lhs
.boolVal() != res
) return TBottom
;
471 // For each component C, we should subtract C_rhs from C_lhs iff every other
472 // component of lhs that can intersect with C is subsumed by the
473 // corresponding component of rhs. This prevents us from removing members of
474 // lhs that weren't present in rhs, but would be casualties of removing
475 // certain bits in lhs.
477 // As an example, consider PtrToRMembInt - PtrToRefStr. Simple subtraction of
478 // each component would yield PtrToMembInt, but that would mean we removed
479 // PtrToRefInt from the lhs despite it not being in rhs. Checking if Int is a
480 // subset of Str shows us that removing Ref from lhs would erase types not
483 // In practice, it's more concise to eagerly do each subtraction, then check
484 // for components that went to Bottom as a way of seeing which components of
485 // lhs were subsets of the corresponding components in rhs. When we find a
486 // component that we weren't supposed to subtract, just restore lhs's
488 auto bits
= lhs
.m_bits
& ~rhs
.m_bits
;
489 auto ptr
= lhs
.ptrKind() - rhs
.ptrKind();
490 auto arrSpec
= lhs
.arrSpec() - rhs
.arrSpec();
491 auto clsSpec
= lhs
.clsSpec() - rhs
.clsSpec();
493 auto const have_gen_bits
= (bits
& kGen
) != 0;
494 auto const have_arr_bits
= supports(bits
, SpecKind::Array
);
495 auto const have_cls_bits
= supports(bits
, SpecKind::Class
);
496 auto const have_ptr
= ptr
!= Ptr::Bottom
;
497 auto const have_arr_spec
= arrSpec
!= ArraySpec::Bottom
;
498 auto const have_cls_spec
= clsSpec
!= ClassSpec::Bottom
;
500 // ptr can only interact with clsSpec if lhs.m_bits has at least one kGen
501 // member of kClsSpecBits.
502 auto const have_ptr_cls
= supports(lhs
.m_bits
& kGen
, SpecKind::Class
);
505 if (have_ptr
) bits
|= lhs
.m_bits
& kGen
;
506 if (have_arr_spec
) bits
|= lhs
.m_bits
& kArrSpecBits
;
507 if (have_cls_spec
) bits
|= lhs
.m_bits
& kClsSpecBits
;
510 if (have_gen_bits
|| have_arr_spec
|| (have_cls_spec
&& have_ptr_cls
)) {
515 if (have_ptr
|| have_arr_bits
) arrSpec
= lhs
.arrSpec();
516 if ((have_ptr
&& have_ptr_cls
) || have_cls_bits
) clsSpec
= lhs
.clsSpec();
518 return Type
{bits
, ptr
}.specialize({arrSpec
, clsSpec
});
521 ///////////////////////////////////////////////////////////////////////////////
524 Type
typeFromTV(const TypedValue
* tv
) {
525 assertx(tv
->m_type
== KindOfClass
|| tvIsPlausible(*tv
));
527 if (tv
->m_type
== KindOfObject
) {
528 auto const cls
= tv
->m_data
.pobj
->getVMClass();
530 // We only allow specialization on classes that can't be overridden for
531 // now. If this changes, then this will need to specialize on sub object
533 if (!cls
|| !(cls
->attrs() & AttrNoOverride
)) return TObj
;
534 return Type::ExactObj(cls
);
537 if (isArrayType(tv
->m_type
)) {
538 auto const ar
= tv
->m_data
.parr
;
539 if (ar
->kind() == ArrayData::kStructKind
) {
540 return Type::Array(StructArray::asStructArray(ar
)->shape());
542 return Type::Array(tv
->m_data
.parr
->kind());
545 auto outer
= tv
->m_type
;
546 auto inner
= KindOfUninit
;
548 if (outer
== KindOfPersistentString
) outer
= KindOfString
;
549 if (outer
== KindOfRef
) {
550 inner
= tv
->m_data
.pref
->tv()->m_type
;
551 if (inner
== KindOfPersistentString
) inner
= KindOfString
;
552 else if (inner
== KindOfPersistentArray
) inner
= KindOfArray
;
554 return Type(outer
, inner
);
557 Type
typeFromRAT(RepoAuthType ty
) {
558 using T
= RepoAuthType::Tag
;
560 case T::OptBool
: return TBool
| TInitNull
;
561 case T::OptInt
: return TInt
| TInitNull
;
562 case T::OptSStr
: return TStaticStr
| TInitNull
;
563 case T::OptStr
: return TStr
| TInitNull
;
564 case T::OptDbl
: return TDbl
| TInitNull
;
565 case T::OptRes
: return TRes
| TInitNull
;
566 case T::OptObj
: return TObj
| TInitNull
;
568 case T::Uninit
: return TUninit
;
569 case T::InitNull
: return TInitNull
;
570 case T::Null
: return TNull
;
571 case T::Bool
: return TBool
;
572 case T::Int
: return TInt
;
573 case T::Dbl
: return TDbl
;
574 case T::Res
: return TRes
;
575 case T::SStr
: return TStaticStr
;
576 case T::Str
: return TStr
;
577 case T::Obj
: return TObj
;
579 case T::Cell
: return TCell
;
580 case T::Ref
: return TBoxedInitCell
;
581 case T::InitUnc
: return TUncountedInit
;
582 case T::Unc
: return TUncounted
;
583 case T::InitCell
: return TInitCell
;
584 case T::InitGen
: return TInitGen
;
585 case T::Gen
: return TGen
;
587 // TODO(#4205897): option specialized array types
588 case T::OptArr
: return TArr
| TInitNull
;
589 case T::OptSArr
: return TStaticArr
| TInitNull
;
592 if (auto const ar
= ty
.array()) return Type::StaticArray(ar
);
595 if (auto const ar
= ty
.array()) return Type::Array(ar
);
601 case T::OptExactObj
: {
604 if (auto const cls
= Unit::lookupClassOrUniqueClass(ty
.clsName())) {
605 if (ty
.tag() == T::ExactObj
|| ty
.tag() == T::OptExactObj
) {
606 base
= Type::ExactObj(cls
);
608 base
= Type::SubObj(cls
);
611 if (ty
.tag() == T::OptSubObj
|| ty
.tag() == T::OptExactObj
) {
620 //////////////////////////////////////////////////////////////////////
622 Type
ldRefReturn(Type typeParam
) {
623 // Guarding on specialized types and uncommon unions like {Int|Bool} is
624 // expensive enough that we only want to do it in situations where we've
625 // manually confirmed the benefit.
626 typeParam
= relaxToGuardable(typeParam
);
627 always_assert(typeParam
<= TCell
);
629 // Refs can never contain Uninit, so this lets us return UncountedInit rather
630 // than Uncounted, and InitCell rather than Cell.
631 return typeParam
- TUninit
;
634 Type
negativeCheckType(Type srcType
, Type typeParam
) {
635 if (srcType
<= typeParam
) return TBottom
;
636 if (!srcType
.maybe(typeParam
)) return srcType
;
637 // Checks relating to StaticStr and StaticArr are not, in general, precise.
638 // They may reject some Statics in some situations, where we only guard using
639 // the type tag and not by loading the count field.
640 auto tmp
= srcType
- typeParam
;
641 if (typeParam
.maybe(TPersistent
)) {
642 if (tmp
.maybe(TCountedStr
)) tmp
|= TStr
;
643 if (tmp
.maybe(TCountedArr
)) tmp
|= TArr
;
648 Type
boxType(Type t
) {
649 // If t contains Uninit, replace it with InitNull.
650 t
= t
.maybe(TUninit
) ? (t
- TUninit
) | TInitNull
: t
;
651 // We don't try to track when a BoxedStaticStr might be converted to
652 // a BoxedStr, and we never guard on staticness for strings, so
653 // boxing a string needs to forget this detail. Same thing for
657 } else if (t
<= TArr
) {
660 // When boxing an Object, if the inner class does not have AttrNoOverride,
661 // drop the class specialization.
662 if (t
< TObj
&& t
.clsSpec() &&
663 !(t
.clsSpec().cls()->attrs() & AttrNoOverride
)) {
664 t
= t
.unspecialize();
666 // Everything else is just a pure type-system boxing operation.
670 //////////////////////////////////////////////////////////////////////
672 static Type
relaxCell(Type t
, DataTypeCategory cat
) {
676 case DataTypeGeneric
:
679 case DataTypeCountness
:
680 return !t
.maybe(TCounted
) ? TUncounted
: t
.unspecialize();
682 case DataTypeCountnessInit
:
683 if (t
<= TUninit
) return TUninit
;
684 return (!t
.maybe(TCounted
) && !t
.maybe(TUninit
))
685 ? TUncountedInit
: t
.unspecialize();
687 case DataTypeSpecific
:
688 return t
.unspecialize();
690 case DataTypeSpecialized
:
691 assertx(t
.isSpecialized());
698 Type
relaxType(Type t
, DataTypeCategory cat
) {
699 always_assert_flog(t
<= TGen
&& t
!= TBottom
, "t = {}", t
);
700 if (cat
== DataTypeGeneric
) return TGen
;
702 (t
& TCell
) <= TBottom
? TBottom
: relaxCell(t
& TCell
, cat
);
703 return t
<= TCell
? relaxed
: relaxed
| TBoxedInitCell
;
706 Type
relaxToGuardable(Type ty
) {
708 ty
= ty
.unspecialize();
710 // ty is unspecialized and we don't support guarding on CountedArr or
711 // StaticArr, so widen any subtypes of Arr to Arr.
712 if (ty
<= TArr
) return TArr
;
714 // We can guard on StaticStr but not CountedStr.
715 if (ty
<= TCountedStr
) return TStr
;
717 if (ty
<= TBoxedCell
) return TBoxedCell
;
718 if (ty
.isKnownDataType()) return ty
;
719 if (ty
<= TUncountedInit
) return TUncountedInit
;
720 if (ty
<= TUncounted
) return TUncounted
;
721 if (ty
<= TCell
) return TCell
;
722 if (ty
<= TGen
) return TGen
;