Clean up irgen.h a bit
[hiphop-php.git] / hphp / runtime / vm / jit / type.cpp
blob52537c718d9862cc362b7b75e4df50000c1d003b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
41 #include <vector>
44 namespace HPHP { namespace jit {
46 TRACE_SET_MOD(hhir);
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());
56 if (*this <= TBool) {
57 return m_boolVal ? "true" : "false";
59 if (*this <= TInt) {
60 return folly::format("{}", m_intVal).str();
62 if (*this <= TDbl) {
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();
68 return s;
70 if (*this <= TStaticStr) {
71 auto str = m_strVal;
72 return folly::format("\"{}\"", escapeStringForCPP(str->data(),
73 str->size())).str();
75 if (*this <= TStaticArr) {
76 if (m_arrVal->empty()) {
77 return "array()";
79 return folly::format("Array({})", m_arrVal).str();
81 if (*this <= TFunc) {
82 return folly::format("Func({})", m_funcVal ? m_funcVal->fullName()->data()
83 : "nullptr").str();
85 if (*this <= TCls) {
86 return folly::format("Cls({})", m_clsVal ? m_clsVal->name()->data()
87 : "nullptr").str();
89 if (*this <= TCctx) {
90 if (!m_intVal) {
91 return "Cctx(Cls(nullptr))";
93 auto const cls = m_cctxVal.cls();
94 return folly::format("Cctx(Cls({}))", cls->name()).str();
96 if (*this <= TTCA) {
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}",
117 m_raw, m_extra);
120 static std::string show(Ptr ptr) {
121 always_assert(ptrSubsetOf(ptr, Ptr::Ptr));
123 switch (ptr) {
124 case Ptr::Bottom:
125 case Ptr::Top:
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)
131 #undef PTRT
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)
138 #undef PTRT
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},
144 #define IRTP IRT
145 IR_TYPES
146 #undef IRT
147 #undef IRTP
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>(
157 "Nullptr|",
158 (*this - TNullptr).toString()
162 if (*this <= TBoxedCell) {
163 return folly::to<std::string>("Boxed", inner().toString());
166 if (m_hasConstVal) {
167 if (*this <= TCls) {
168 return folly::sformat("Cls={}", m_clsVal->name()->data());
170 return folly::sformat("{}<{}>",
171 dropConstVal().toString(), constValString());
174 auto t = *this;
176 if (t.maybe(TPtrToGen)) {
177 assertx(!t.m_hasConstVal);
178 auto ret = "PtrTo" +
179 show(t.ptrKind() & Ptr::Ptr) +
180 (t & TPtrToGen).deref().toString();
182 t -= TPtrToGen;
183 if (t != TBottom) ret += "|" + t.toString();
184 return ret;
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);
198 t -= TAnyObj;
199 } else if (auto arrSpec = t.arrSpec()) {
200 auto str = Type(m_bits & kArrSpecBits, t.ptrKind()).toString();
201 if (auto const kind = arrSpec.kind()) {
202 str += "=";
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);
212 t -= TAnyArr;
213 } else {
214 not_reached();
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, ...)
221 IRT_PRIMITIVE
222 # undef IRT
223 # undef IRTP
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) {
231 return t.toString();
234 ///////////////////////////////////////////////////////////////////////////////
236 bool Type::checkValid() const {
237 // Note: be careful, the TFoo objects aren't all constructed yet in this
238 // function.
239 if (m_extra) {
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}",
248 m_raw, m_extra);
251 return true;
254 Type::bits_t Type::bitsFromDataType(DataType outer, DataType inner) {
255 assertx(inner != KindOfRef);
256 assertx(inner == KindOfUninit || outer == KindOfRef);
258 switch (outer) {
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;
271 case KindOfRef:
272 assertx(inner != KindOfUninit);
273 return bitsFromDataType(inner, KindOfUninit) << kBoxShift;
275 not_reached();
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 ///////////////////////////////////////////////////////////////////////////////
302 // Combinators.
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))) {
312 return *this;
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()) {
336 case A::Tag::Packed:
337 if (arr->size() != rat_type->size()) break;
338 // fall through
339 case A::Tag::PackedN: {
340 int64_t k = 0;
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()),
346 specElemType)) {
347 break;
350 if (k == arr->size()) return true;
351 break;
357 if (arr->isStruct()) {
358 if (StructArray::asStructArray(arr)->shape() == spec.shape()) return true;
361 return false;
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) {
369 return false;
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())) {
383 return false;
386 // If rhs isn't specialized no further checking is needed.
387 if (!rhs.isSpecialized()) {
388 return true;
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
394 // any.
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 {
405 auto lhs = *this;
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 {
424 auto lhs = *this;
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
428 // Bottom otherwise.
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;
443 // ptr
444 if ((bits & kGen) == 0) ptr = Ptr::Bottom;
446 // specs
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 {
454 auto lhs = *this;
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;
466 return cns(res);
468 return lhs;
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
481 // present in rhs.
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
487 // original value.
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);
504 // bits
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;
509 // ptr
510 if (have_gen_bits || have_arr_spec || (have_cls_spec && have_ptr_cls)) {
511 ptr = lhs.ptrKind();
514 // specs
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 ///////////////////////////////////////////////////////////////////////////////
522 // Conversions.
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
532 // types instead.
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;
559 switch (ty.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;
591 case T::SArr:
592 if (auto const ar = ty.array()) return Type::StaticArray(ar);
593 return TStaticArr;
594 case T::Arr:
595 if (auto const ar = ty.array()) return Type::Array(ar);
596 return TArr;
598 case T::SubObj:
599 case T::ExactObj:
600 case T::OptSubObj:
601 case T::OptExactObj: {
602 auto base = TObj;
604 if (auto const cls = Unit::lookupClassOrUniqueClass(ty.clsName())) {
605 if (ty.tag() == T::ExactObj || ty.tag() == T::OptExactObj) {
606 base = Type::ExactObj(cls);
607 } else {
608 base = Type::SubObj(cls);
611 if (ty.tag() == T::OptSubObj || ty.tag() == T::OptExactObj) {
612 base |= TInitNull;
614 return base;
617 not_reached();
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;
645 return tmp;
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
654 // arrays.
655 if (t <= TStr) {
656 t = TStr;
657 } else if (t <= TArr) {
658 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.
667 return t.box();
670 //////////////////////////////////////////////////////////////////////
672 static Type relaxCell(Type t, DataTypeCategory cat) {
673 assertx(t <= TCell);
675 switch (cat) {
676 case DataTypeGeneric:
677 return TGen;
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());
692 return t;
695 not_reached();
698 Type relaxType(Type t, DataTypeCategory cat) {
699 always_assert_flog(t <= TGen && t != TBottom, "t = {}", t);
700 if (cat == DataTypeGeneric) return TGen;
701 auto const relaxed =
702 (t & TCell) <= TBottom ? TBottom : relaxCell(t & TCell, cat);
703 return t <= TCell ? relaxed : relaxed | TBoxedInitCell;
706 Type relaxToGuardable(Type ty) {
707 assertx(ty <= TGen);
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;
723 not_reached();