void cgStElem(IRLS& env, const IRInstruction* inst) { auto const rbase = srcLoc(env, inst, 0).reg(); auto const ridx = srcLoc(env, inst, 1).reg(); auto const idx = inst->src(1); auto& v = vmain(env); if (idx->hasConstVal() && deltaFits(idx->intVal(), sz::dword)) { storeTV(v, rbase[idx->intVal()], srcLoc(env, inst, 2), inst->src(2)); } else { storeTV(v, rbase[ridx], srcLoc(env, inst, 2), inst->src(2)); } }
void cgLdElem(IRLS& env, const IRInstruction* inst) { auto const rbase = srcLoc(env, inst, 0).reg(); auto const ridx = srcLoc(env, inst, 1).reg(); auto const idx = inst->src(1); auto& v = vmain(env); if (idx->hasConstVal() && deltaFits(idx->intVal(), sz::dword)) { loadTV(v, inst->dst(), dstLoc(env, inst, 0), rbase[idx->intVal()]); } else { loadTV(v, inst->dst(), dstLoc(env, inst, 0), rbase[ridx]); } }
void cgCheckRange(IRLS& env, const IRInstruction* inst) { auto valTmp = inst->src(0); auto dst = dstLoc(env, inst, 0).reg(); auto val = srcLoc(env, inst, 0).reg(); auto limit = srcLoc(env, inst, 1).reg(); auto& v = vmain(env); ConditionCode cc; auto const sf = v.makeReg(); if (valTmp->hasConstVal()) { // Try to put the constant in a position that can get imm-folded. A // suffiently smart imm-folder could handle this for us. Note that this is // an arch-agnostic API bleed. v << cmpq{val, limit, sf}; cc = CC_A; } else { v << cmpq{limit, val, sf}; cc = CC_B; } v << setcc{cc, sf, dst}; }
bool Type::operator<=(Type rhs) const { auto const lhs = *this; // Check for any members in lhs.m_bits that aren't in rhs.m_bits. if ((lhs.m_bits & rhs.m_bits) != lhs.m_bits) { return false; } // Check for Bottom; all the remaining cases assume `lhs' is not Bottom. if (lhs.m_bits == kBottom) return true; // If `rhs' is a constant, we must be the same constant. if (rhs.m_hasConstVal) { assertx(!rhs.isUnion()); return lhs.m_hasConstVal && lhs.m_extra == rhs.m_extra; } // Make sure lhs's ptr kind is a subtype of rhs's. if (!ptrSubsetOf(lhs.ptrKind(), rhs.ptrKind())) { return false; } // If rhs isn't specialized no further checking is needed. if (!rhs.isSpecialized()) { return true; } if (lhs.hasConstVal(TArr)) { // Arrays can be specialized in different ways, here we check if the // constant array fits the kind()/type() of the specialization of rhs, if // any. auto const lhs_arr = lhs.arrVal(); auto const rhs_as = rhs.arrSpec(); return arrayFitsSpec(lhs_arr, rhs_as); } // Compare specializations only if `rhs' is specialized. return lhs.spec() <= rhs.spec(); }
Type Type::operator-(Type rhs) const { auto lhs = *this; if (rhs == TBottom) return lhs; if (lhs <= rhs) return TBottom; if (lhs.hasConstVal()) return lhs; // not covered by rhs. // If `rhs' has a constant value, but `lhs' doesn't, conservatively return // `lhs', rather than trying to represent things like "everything except // Int<24>". Boolean is a special case. if (rhs.m_hasConstVal) { if (rhs <= TBool && lhs <= TBool) { auto const res = !rhs.boolVal(); if (lhs.hasConstVal() && lhs.boolVal() != res) return TBottom; return cns(res); } return lhs; } // For each component C, we should subtract C_rhs from C_lhs iff every other // component of lhs that can intersect with C is subsumed by the // corresponding component of rhs. This prevents us from removing members of // lhs that weren't present in rhs, but would be casualties of removing // certain bits in lhs. // // As an example, consider PtrToRMembInt - PtrToRefStr. Simple subtraction of // each component would yield PtrToMembInt, but that would mean we removed // PtrToRefInt from the lhs despite it not being in rhs. Checking if Int is a // subset of Str shows us that removing Ref from lhs would erase types not // present in rhs. // // In practice, it's more concise to eagerly do each subtraction, then check // for components that went to Bottom as a way of seeing which components of // lhs were subsets of the corresponding components in rhs. When we find a // component that we weren't supposed to subtract, just restore lhs's // original value. auto bits = lhs.m_bits & ~rhs.m_bits; auto ptr = lhs.ptrKind() - rhs.ptrKind(); auto arrSpec = lhs.arrSpec() - rhs.arrSpec(); auto clsSpec = lhs.clsSpec() - rhs.clsSpec(); auto const have_gen_bits = (bits & kGen) != 0; auto const have_arr_bits = supports(bits, SpecKind::Array); auto const have_cls_bits = supports(bits, SpecKind::Class); auto const have_ptr = ptr != Ptr::Bottom; auto const have_arr_spec = arrSpec != ArraySpec::Bottom; auto const have_cls_spec = clsSpec != ClassSpec::Bottom; // ptr can only interact with clsSpec if lhs.m_bits has at least one kGen // member of kClsSpecBits. auto const have_ptr_cls = supports(lhs.m_bits & kGen, SpecKind::Class); // bits if (have_ptr) bits |= lhs.m_bits & kGen; if (have_arr_spec) bits |= lhs.m_bits & kArrSpecBits; if (have_cls_spec) bits |= lhs.m_bits & kClsSpecBits; // ptr if (have_gen_bits || have_arr_spec || (have_cls_spec && have_ptr_cls)) { ptr = lhs.ptrKind(); } // specs if (have_ptr || have_arr_bits) arrSpec = lhs.arrSpec(); if ((have_ptr && have_ptr_cls) || have_cls_bits) clsSpec = lhs.clsSpec(); return Type{bits, ptr}.specialize({arrSpec, clsSpec}); }
TEST(Type, Const) { auto five = Type::cns(5); EXPECT_LT(five, TInt); EXPECT_NE(five, TInt); EXPECT_TRUE(five.hasConstVal()); EXPECT_EQ(5, five.intVal()); EXPECT_TRUE(five.hasConstVal(TInt)); EXPECT_TRUE(five.hasConstVal(5)); EXPECT_FALSE(five.hasConstVal(5.0)); EXPECT_TRUE(TGen.maybe(five)); EXPECT_EQ(TInt, five | TInt); EXPECT_EQ(TInt, five | Type::cns(10)); EXPECT_EQ(five, five | Type::cns(5)); EXPECT_EQ(five, Type::cns(5) & five); EXPECT_EQ(five, five & TInt); EXPECT_EQ(five, TGen & five); EXPECT_EQ("Int<5>", five.toString()); EXPECT_EQ(five, five - TArr); EXPECT_EQ(five, five - Type::cns(1)); EXPECT_EQ(TInt, TInt - five); // conservative EXPECT_EQ(TBottom, five - TInt); EXPECT_EQ(TBottom, five - five); EXPECT_EQ(TPtrToGen, (TPtrToGen|TNullptr) - TNullptr); EXPECT_EQ(TInt, five.dropConstVal()); EXPECT_TRUE(!five.maybe(Type::cns(2))); auto True = Type::cns(true); EXPECT_EQ("Bool<true>", True.toString()); EXPECT_LT(True, TBool); EXPECT_NE(True, TBool); EXPECT_TRUE(True.hasConstVal()); EXPECT_EQ(true, True.boolVal()); EXPECT_TRUE(TUncounted.maybe(True)); EXPECT_FALSE(five <= True); EXPECT_FALSE(five > True); EXPECT_TRUE(!five.maybe(True)); EXPECT_EQ(TInt | TBool, five | True); EXPECT_EQ(TBottom, five & True); EXPECT_EQ(Type::cns(false), TBool - True); auto array = make_packed_array(1, 2, 3, 4); auto arrData = ArrayData::GetScalarArray(array.get()); auto constArray = Type::cns(arrData); auto packedArray = Type::Array(ArrayData::kPackedKind); auto mixedArray = Type::Array(ArrayData::kMixedKind); EXPECT_TRUE(constArray <= packedArray); EXPECT_TRUE(constArray < packedArray); EXPECT_FALSE(packedArray <= constArray); EXPECT_TRUE(constArray <= constArray); EXPECT_FALSE(packedArray <= mixedArray); EXPECT_FALSE(mixedArray <= packedArray); EXPECT_FALSE(constArray <= mixedArray); EXPECT_EQ(constArray, constArray & packedArray); ArrayTypeTable::Builder ratBuilder; auto rat1 = ratBuilder.packedn(RepoAuthType::Array::Empty::No, RepoAuthType(RepoAuthType::Tag::Str)); auto ratArray1 = Type::Array(rat1); auto rat2 = ratBuilder.packedn(RepoAuthType::Array::Empty::No, RepoAuthType(RepoAuthType::Tag::Int)); auto ratArray2 = Type::Array(rat2); EXPECT_EQ(TArr, ratArray1 & ratArray2); EXPECT_TRUE(ratArray1 < TArr); EXPECT_TRUE(ratArray1 <= ratArray1); EXPECT_TRUE(ratArray1 < (TArr|TObj)); EXPECT_FALSE(ratArray1 < ratArray2); EXPECT_NE(ratArray1, ratArray2); auto packedRat = packedArray & ratArray1; EXPECT_EQ("Arr=PackedKind:N([Str])", packedRat.toString()); EXPECT_TRUE(packedRat <= packedArray); EXPECT_TRUE(packedRat < packedArray); EXPECT_TRUE(packedRat <= ratArray1); EXPECT_TRUE(packedRat < ratArray1); EXPECT_EQ(packedRat, packedRat & packedArray); EXPECT_EQ(packedRat, packedRat & ratArray1); }