SSATmp* TraceBuilder::preOptimizeAssertLoc(IRInstruction* inst) { auto const locId = inst->extra<AssertLoc>()->locId; auto const prevType = localType(locId, DataTypeGeneric); auto const typeParam = inst->typeParam(); if (prevType.not(typeParam)) { TRACE_PUNT("Invalid AssertLoc"); } if (shouldElideAssertType(prevType, typeParam, nullptr)) { return inst->src(0); } if (filterAssertType(inst, prevType)) { constrainLocal(locId, categoryForType(prevType), "AssertLoc"); } return nullptr; }
SSATmp* IRBuilder::preOptimizeAssertTypeOp(IRInstruction* inst, Type oldType, ConstraintFunc constrain) { auto const newType = inst->typeParam(); if (oldType.not(newType)) { // If both types are boxed this is ok and even expected as a means to // update the hint for the inner type. if (oldType.isBoxed() && newType.isBoxed()) return nullptr; // We got external information (probably from static analysis) that // conflicts with what we've built up so far. There's no reasonable way to // continue here: we can't properly fatal the request because we can't make // a catch trace or SpillStack without HhbcTranslator, we can't punt on // just this instruction because we might not be in the initial translation // phase, and we can't just plow on forward since we'll probably generate // malformed IR. Since this case is very rare, just punt on the whole trace // so it gets interpreted. TRACE_PUNT("Invalid AssertTypeOp"); } // Asserting in these situations doesn't add any information. if (oldType <= Type::Cls || newType == Type::Gen) return inst->src(0); // We're asserting a strict subtype of the old type, so keep the assert // around. if (newType < oldType) return nullptr; // oldType is at least as good as the new type. Kill this assert op but // preserve the type we were asserting in case the source type gets relaxed // past it. if (newType >= oldType) { constrain({DataTypeGeneric, newType}); return inst->src(0); } // AssertLoc is special here because it's the one AssertTypeOp that doesn't // do its own filtering of the destination type based on the input type and // the asserted type. This will hopefully be fixed soon. if (inst->is(AssertLoc)) { // Now we're left with cases where neither type is a subtype of the other // but they have some nonzero intersection. We want to end up asserting the // intersection, but we have to constrain the input to avoid reintroducing // types that were removed from the original typeParam. auto const intersect = newType & oldType; inst->setTypeParam(intersect); TypeConstraint tc; if (intersect != newType) { Type relaxed; // Find the most general constraint that doesn't modify the type being // asserted. while ((relaxed = newType & relaxType(oldType, tc)) != intersect) { if (tc.category > DataTypeGeneric && relaxed.maybeBoxed() && intersect.maybeBoxed() && (relaxed & Type::Cell) == (intersect & Type::Cell)) { // If the inner type is why we failed, constrain that a level. incCategory(tc.innerCat); } else { incCategory(tc.category); } } } constrain(tc); } return nullptr; }