Beispiel #1
0
bool IRInstruction::consumesReference(int srcNo) const {
  if (!consumesReferences()) {
    return false;
  }
  // CheckType/AssertType consume a reference if we're guarding from a
  // maybeCounted type to a notCounted type.
  if (m_op == CheckType || m_op == AssertType) {
    assert(srcNo == 0);
    return src(0)->type().maybeCounted() && typeParam().notCounted();
  }
  // SpillStack consumes inputs 2 and onward
  if (m_op == SpillStack) return srcNo >= 2;
  // Call consumes inputs 3 and onward
  if (m_op == Call) return srcNo >= 3;
  // StRetVal only consumes input 1
  if (m_op == StRetVal) return srcNo == 1;

  if (m_op == StLoc || m_op == StLocNT) {
    // StLoc[NT] <stkptr>, <value>
    return srcNo == 1;
  }
  if (m_op == StProp || m_op == StPropNT || m_op == StMem || m_op == StMemNT) {
    // StProp[NT]|StMem[NT] <base>, <offset>, <value>
    return srcNo == 2;
  }
  if (m_op == ArraySet || m_op == ArraySetRef) {
    // Only consumes the reference to its input array
    return srcNo == 1;
  }
  return true;
}
Beispiel #2
0
void Seqbyte_Type_append(Seqbyte st,Type s)
  {
  string nm;Type tp;Seqob tps;
  ob_push2(st,s);
  tp = typeParam(s);
  tps = typeParams(s);
  if (tps) 
  {
	  ob_push(tps);
	  Seqbyte_charp_append(st,"Function(");
	  Seqbyte_Type_append(st,tp);
	  if ((tps->data->length)>0) 
	  {
		  Seqbyte_charp_append(st,",");
		  Seqbyte_Types_append(st,tps);
	  }
	  ob_pop();
	  return;
  }
  if (tp) 
  {
      ob_push(tp);
	  Seqbyte_charp_append(st,"SeqOf(");
	  Seqbyte_Type_append(st,tp);
	  Seqbyte_charp_append(st,")");
	  ob_pop();
	  return;
  }  
  nm = ob_name(s);
  if (nm) Seqbyte_string_append(st,nm);
  else Seqbyte_charp_append(st,"<unnamed>");
  ob_popn(2);
  }
Beispiel #3
0
void TraceBuilder::constrainLocal(uint32_t locId, SSATmp* valSrc,
                                  DataTypeCategory cat,
                                  const std::string& why) {
  FTRACE(1, "constrainLocal({}, {}, {}, {})\n",
         locId, valSrc ? valSrc->inst()->toString() : "null", cat, why);

  if (!valSrc) return;
  if (!valSrc->isA(Type::FramePtr)) {
    constrainValue(valSrc, cat);
    return;
  }

  // When valSrc is a FramePtr, that means we loaded the value the local had
  // coming into the trace. Trace through the FramePtr chain, looking for a
  // guard for this local id. If we find it, constrain the guard. If we don't
  // find it, there wasn't a guard for this local so there's nothing to
  // constrain.
  for (auto fpInst = valSrc->inst();
       fpInst->op() != DefFP && fpInst->op() != DefInlineFP;
       fpInst = fpInst->src(0)->inst()) {
    FTRACE(2, "    - fp = {}\n", *fpInst);
    assert(fpInst->dst()->isA(Type::FramePtr));

    auto instLoc = [fpInst]{ return fpInst->extra<LocalId>()->locId; };
    switch (fpInst->op()) {
      case GuardLoc:
      case CheckLoc:
        if (instLoc() != locId) break;
        FTRACE(2, "    - found guard to constrain\n");
        constrainGuard(fpInst, cat);
        return;

      case AssertLoc:
        if (instLoc() != locId) break;
        // If the refined the type of the local satisfies the constraint we're
        // trying to apply, we can stop here. This can happen if we assert a
        // more general type than what we already know. Otherwise we need to
        // keep tracing back to the guard.
        if (typeFitsConstraint(fpInst->typeParam(), cat)) return;
        break;

      case FreeActRec:
        always_assert(0 && "Attempt to read a local after freeing its frame");

      default:
        not_reached();
    }
  }

  FTRACE(2, "    - no guard to constrain\n");
}
Beispiel #4
0
bool IRBuilder::constrainLocal(uint32_t locId, SSATmp* typeSrc,
                               TypeConstraint tc,
                               const std::string& why) {
  if (!shouldConstrainGuards()) return false;
  always_assert(IMPLIES(tc.innerCat > DataTypeGeneric,
                        tc.category >= DataTypeCountness));

  ITRACE(1, "constrainLocal({}, {}, {}, {})\n",
         locId, typeSrc ? typeSrc->inst()->toString() : "null", tc, why);
  Indent _i;

  if (!typeSrc) return false;
  if (!typeSrc->isA(Type::FramePtr)) {
    return constrainValue(typeSrc, tc);
  }

  // When typeSrc is a FramePtr, that means we loaded the value the local had
  // coming into the trace. Trace through the FramePtr chain, looking for a
  // guard for this local id. If we find it, constrain the guard. If we don't
  // find it, there wasn't a guard for this local so there's nothing to
  // constrain.
  auto guard = guardForLocal(locId, typeSrc);
  while (guard) {
    if (guard->is(AssertLoc)) {
      // If the refined type of the local satisfies the constraint we're
      // trying to apply, we can stop here. This can happen if we assert a
      // more general type than what we already know. Otherwise we need to
      // keep tracing back to the guard.
      if (typeFitsConstraint(guard->typeParam(), tc)) return false;
      guard = guardForLocal(locId, guard->src(0));
    } else {
      assert(guard->is(GuardLoc, CheckLoc));
      ITRACE(2, "found guard to constrain\n");
      return constrainGuard(guard, tc);
    }
  }

  ITRACE(2, "no guard to constrain\n");
  return false;
}
Beispiel #5
0
void TraceBuilder::constrainLocal(uint32_t locId, SSATmp* valSrc,
                                  TypeConstraint tc,
                                  const std::string& why) {
  if (!shouldConstrainGuards()) return;

  FTRACE(1, "constrainLocal({}, {}, {}, {})\n",
         locId, valSrc ? valSrc->inst()->toString() : "null", tc, why);

  if (!valSrc) return;
  if (!valSrc->isA(Type::FramePtr)) {
    constrainValue(valSrc, tc);
    return;
  }

  // When valSrc is a FramePtr, that means we loaded the value the local had
  // coming into the trace. Trace through the FramePtr chain, looking for a
  // guard for this local id. If we find it, constrain the guard. If we don't
  // find it, there wasn't a guard for this local so there's nothing to
  // constrain.
  auto guard = guardForLocal(locId, valSrc);
  while (guard) {
    if (guard->is(AssertLoc)) {
      // If the refined the type of the local satisfies the constraint we're
      // trying to apply, we can stop here. This can happen if we assert a
      // more general type than what we already know. Otherwise we need to
      // keep tracing back to the guard.
      if (typeFitsConstraint(guard->typeParam(), tc.category)) return;
      guard = guardForLocal(locId, guard->src(0));
    } else {
      assert(guard->is(GuardLoc, AssertLoc));
      FTRACE(2, "    - found guard to constrain\n");
      constrainGuard(guard, tc);
      return;
    }
  }

  FTRACE(2, "    - no guard to constrain\n");
}
Beispiel #6
0
/**
 * Trace back to the guard that provided the type of val, if
 * any. Constrain it so its type will not be relaxed beyond the given
 * DataTypeCategory. Returns true iff one or more guard instructions
 * were constrained.
 */
bool IRBuilder::constrainValue(SSATmp* const val,
                               TypeConstraint tc) {
  if (!shouldConstrainGuards()) return false;

  if (!val) {
    FTRACE(1, "constrainValue(nullptr, {}), bailing\n", tc);
    return false;
  }

  FTRACE(1, "constrainValue({}, {})\n", *val->inst(), tc);

  auto inst = val->inst();
  if (inst->is(LdLoc, LdLocAddr)) {
    // We've hit a LdLoc(Addr). If the source of the value is non-null and not
    // a FramePtr, it's a real value that was killed by a Call. The value won't
    // be live but it's ok to use it to track down the guard.

    auto source = inst->extra<LocalData>()->typeSrc;
    if (!source) {
      // val was newly created in this trace. Nothing to constrain.
      FTRACE(2, "  - typeSrc is null, bailing\n");
      return false;
    }

    // If valSrc is a FramePtr, it represents the frame the value was
    // originally loaded from. Look for the guard for this local.
    if (source->isA(Type::FramePtr)) {
      return constrainLocal(inst->extra<LocalId>()->locId, source, tc,
                            "constrainValue");
    }

    // Otherwise, keep chasing down the source of val.
    return constrainValue(source, tc);
  } else if (inst->is(LdStack, LdStackAddr)) {
    return constrainStack(inst->src(0), inst->extra<StackOffset>()->offset,
                          tc);
  } else if (inst->is(CheckType, AssertType)) {
    // If the dest type of the instruction fits the constraint we want, we can
    // stop here without constraining any further. Otherwise, continue through
    // to the source.
    auto changed = false;
    if (inst->is(CheckType)) changed = constrainGuard(inst, tc) || changed;

    auto dstType = inst->typeParam();
    if (!typeFitsConstraint(dstType, tc.category)) {
      changed = constrainValue(inst->src(0), tc) || changed;
    }
    return changed;
  } else if (inst->is(StRef)) {
    // StRef requires that src(0) is boxed so we're relying on callers to
    // appropriately constrain the values they pass to it. Any innerCat in tc
    // should be applied to the value being stored.

    tc.category = tc.innerCat;
    tc.innerCat = DataTypeGeneric;
    tc.assertedType = Type::Gen;
    return constrainValue(inst->src(1), tc);
  } else if (inst->is(Box, BoxPtr, Unbox, UnboxPtr)) {
    // All Box/Unbox opcodes are similar to StRef/LdRef in some situations and
    // Mov in others (determined at runtime), so we need to constrain both
    // outer and inner.

    auto maxCat = std::max(tc.category, tc.innerCat);
    tc.category = maxCat;
    tc.innerCat = maxCat;
    tc.assertedType = Type::Gen;
    return constrainValue(inst->src(0), tc);
  } else if (inst->is(LdRef)) {
    // Like StRef, we're relying on the caller to have appropriately
    // constrained the outer type of the box. Constrain the inner type of the
    // box with tc.

    tc.innerCat = tc.category;
    tc.category = DataTypeGeneric;
    tc.assertedType = Type::Gen;
    return constrainValue(inst->src(0), tc);
  } else if (inst->isPassthrough()) {
    return constrainValue(inst->getPassthroughValue(), tc);
  } else {
    // Any instructions not special cased above produce a new value, so
    // there's no guard for us to constrain.
    FTRACE(2, "  - value is new in this trace, bailing\n");
    return false;
  }
  // TODO(t2598894): Should be able to do something with LdMem<T> here
}
Beispiel #7
0
/**
 * Trace back to the guard that provided the type of val, if
 * any. Constrain it so its type will not be relaxed beyond the given
 * DataTypeCategory. Returns true iff one or more guard instructions
 * were constrained.
 */
bool TraceBuilder::constrainValue(SSATmp* const val,
                                  TypeConstraint tc) {
  if (!shouldConstrainGuards()) return false;

  if (!val) {
    FTRACE(1, "constrainValue(nullptr, {}), bailing\n", tc);
    return false;
  }

  FTRACE(1, "constrainValue({}, {})\n", *val->inst(), tc);

  auto inst = val->inst();
  if (inst->is(LdLoc, LdLocAddr)) {
    // We've hit a LdLoc(Addr). If the source of the value is non-null and not
    // a FramePtr, it's a real value that was killed by a Call. The value won't
    // be live but it's ok to use it to track down the guard.

    auto source = inst->extra<LocalData>()->valSrc;
    if (!source) {
      // val was newly created in this trace. Nothing to constrain.
      FTRACE(2, "  - valSrc is null, bailing\n");
      return false;
    }

    // If valSrc is a FramePtr, it represents the frame the value was
    // originally loaded from. Look for the guard for this local.
    if (source->isA(Type::FramePtr)) {
      return constrainLocal(inst->extra<LocalId>()->locId, source, tc,
                            "constrainValue");
    }

    // Otherwise, keep chasing down the source of val.
    return constrainValue(source, tc);
  } else if (inst->is(LdStack, LdStackAddr)) {
    return constrainStack(inst->src(0), inst->extra<StackOffset>()->offset,
                          tc);
  } else if (inst->is(CheckType, AssertType)) {
    // If the dest type of the instruction fits the constraint we want, we can
    // stop here without constraining any further. Otherwise, continue through
    // to the source.
    auto changed = false;
    if (inst->is(CheckType)) changed = constrainGuard(inst, tc) || changed;

    auto dstType = inst->typeParam();
    if (!typeFitsConstraint(dstType, tc.category)) {
      changed = constrainValue(inst->src(0), tc) || changed;
    }
    return changed;
  } else if (inst->is(StRef, StRefNT, Box, BoxPtr)) {
    // If our caller cares about the inner type, propagate that through.
    // Otherwise we're done.
    if (tc.innerCat) {
      auto src = inst->src(inst->is(StRef, StRefNT) ? 1 : 0);
      tc.innerCat.reset();
      return constrainValue(src, tc);
    }
    return false;
  } else if (inst->is(LdRef, Unbox, UnboxPtr)) {
    // Pass through to the source of the box, remembering that we care about
    // the inner type of the box.
    assert(!tc.innerCat);
    tc.innerCat = tc.category;
    return constrainValue(inst->src(0), tc);
  } else if (inst->isPassthrough()) {
    return constrainValue(inst->getPassthroughValue(), tc);
  } else {
    // Any instructions not special cased above produce a new value, so
    // there's no guard for us to constrain.
    FTRACE(2, "  - value is new in this trace, bailing\n");
    return false;
  }
  // TODO(t2598894): Should be able to do something with LdMem<T> here
}
Beispiel #8
0
/**
 * Trace back to the guard that provided the type of val, if
 * any. Constrain it so its type will not be relaxed beyond the given
 * DataTypeCategory. Returns true iff one or more guard instructions
 * were constrained.
 */
bool IRBuilder::constrainValue(SSATmp* const val, TypeConstraint tc) {
  if (!shouldConstrainGuards()) return false;
  always_assert(IMPLIES(tc.innerCat > DataTypeGeneric,
                        tc.category >= DataTypeCountness));

  if (!val) {
    ITRACE(1, "constrainValue(nullptr, {}), bailing\n", tc);
    return false;
  }

  ITRACE(1, "constrainValue({}, {})\n", *val->inst(), tc);
  Indent _i;

  auto inst = val->inst();
  if (inst->is(LdLoc, LdLocAddr)) {
    // We've hit a LdLoc(Addr). If the source of the value is non-null and not
    // a FramePtr, it's a real value that was killed by a Call. The value won't
    // be live but it's ok to use it to track down the guard.

    auto source = inst->extra<LocalData>()->typeSrc;
    if (!source) {
      // val was newly created in this trace. Nothing to constrain.
      ITRACE(2, "typeSrc is null, bailing\n");
      return false;
    }

    // If typeSrc is a FramePtr, it represents the frame the value was
    // originally loaded from. Look for the guard for this local.
    if (source->isA(Type::FramePtr)) {
      return constrainLocal(inst->extra<LocalId>()->locId, source, tc,
                            "constrainValue");
    }

    // Otherwise, keep chasing down the source of val.
    return constrainValue(source, tc);
  } else if (inst->is(LdStack, LdStackAddr)) {
    return constrainStack(inst->src(0), inst->extra<StackOffset>()->offset,
                          tc);
  } else if (inst->is(AssertType)) {
    // Sometimes code in HhbcTranslator asks for a value with DataTypeSpecific
    // but can tolerate a less specific value. If that happens, there's nothing
    // to constrain.
    if (!typeFitsConstraint(val->type(), tc)) return false;

    // If the immutable typeParam fits the constraint, we're done.
    auto const typeParam = inst->typeParam();
    if (typeFitsConstraint(typeParam, tc)) return false;

    auto const newTc = relaxConstraint(tc, typeParam, inst->src(0)->type());
    ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
           *inst, tc, newTc);
    return constrainValue(inst->src(0), newTc);
  } else if (inst->is(CheckType)) {
    // Sometimes code in HhbcTranslator asks for a value with DataTypeSpecific
    // but can tolerate a less specific value. If that happens, there's nothing
    // to constrain.
    if (!typeFitsConstraint(val->type(), tc)) return false;

    bool changed = false;
    auto const typeParam = inst->typeParam();
    auto const srcType = inst->src(0)->type();

    // Constrain the guard on the CheckType, but first relax the constraint
    // based on what's known about srcType.
    auto const guardTc = relaxConstraint(tc, srcType, typeParam);
    changed = constrainGuard(inst, guardTc) || changed;

    // Relax typeParam with its current constraint. This is used below to
    // recursively relax the constraint on the source, if needed.
    auto constraint = m_guardConstraints[inst];
    constraint.category = std::max(constraint.category, guardTc.category);
    constraint.innerCat = std::max(constraint.innerCat, guardTc.innerCat);
    auto const knownType = refineType(relaxType(typeParam, constraint),
                                      constraint.assertedType);

    if (!typeFitsConstraint(knownType, tc)) {
      auto const newTc = relaxConstraint(tc, knownType, srcType);
      ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
             *inst, tc, newTc);
      changed = constrainValue(inst->src(0), newTc) || changed;
    }
    return changed;
  } else if (inst->is(StRef)) {
    // StRef requires that src(0) is boxed so we're relying on callers to
    // appropriately constrain the values they pass to it. Any innerCat in tc
    // should be applied to the value being stored.

    tc.category = tc.innerCat;
    tc.innerCat = DataTypeGeneric;
    tc.assertedType = Type::Gen;
    return constrainValue(inst->src(1), tc);
  } else if (inst->is(Box, BoxPtr, Unbox, UnboxPtr)) {
    // All Box/Unbox opcodes are similar to StRef/LdRef in some situations and
    // Mov in others (determined at runtime), so we need to constrain both
    // outer and inner.

    auto maxCat = std::max(tc.category, tc.innerCat);
    tc.category = maxCat;
    tc.innerCat = maxCat;
    tc.assertedType = Type::Gen;
    return constrainValue(inst->src(0), tc);
  } else if (inst->is(LdRef)) {
    // Constrain the inner type of the box with tc, using DataTypeCountness for
    // the outer constraint to preserve the fact that it's a box.

    tc.innerCat = tc.category;
    tc.category = DataTypeCountness;
    tc.assertedType = Type::Gen;
    return constrainValue(inst->src(0), tc);
  } else if (inst->isPassthrough()) {
    return constrainValue(inst->getPassthroughValue(), tc);
  } else {
    // Any instructions not special cased above produce a new value, so
    // there's no guard for us to constrain.
    ITRACE(2, "value is new in this trace, bailing\n");
    return false;
  }
  // TODO(t2598894): Should be able to do something with LdMem<T> here
}