Exemplo n.º 1
0
/*
 * For all guard instructions in trace, check to see if we can relax the
 * destination type to something less specific. The GuardConstraints map
 * contains information about what properties of the guarded type matter for
 * each instruction.
 */
bool relaxGuards(IRTrace* trace, const IRFactory& factory,
                 const GuardConstraints& guards) {
  FTRACE(1, "relaxing guards for trace {}\n", trace);
  auto blocks = rpoSortCfg(trace, factory);
  Block* reflowBlock = nullptr;

  for (auto* block : blocks) {
    for (auto& inst : *block) {
      if (!isGuardOp(inst.op())) continue;

      auto it = guards.find(inst.id());
      auto category = it == guards.end() ? DataTypeGeneric : it->second;

      auto const oldType = inst.typeParam();
      auto newType = relaxType(oldType, category);

      if (!oldType.equals(newType)) {
        FTRACE(1, "relaxGuards changing {}'s type to {}\n", inst, newType);
        inst.setTypeParam(newType);
        if (!reflowBlock) reflowBlock = block;
      }
    }
  }

  // TODO(t2598894): For now we require regenerating the IR after guard
  // relaxation, so it's only useful in the tracelet region selector.
  if (false && reflowBlock) reflowTypes(reflowBlock, blocks);

  return (bool)reflowBlock;
}
Exemplo n.º 2
0
/*
 * For all guard instructions in trace, check to see if we can relax the
 * destination type to something less specific. The GuardConstraints map
 * contains information about what properties of the guarded type matter for
 * each instruction. Returns true iff any changes were made to the trace.
 */
bool relaxGuards(const IRUnit& unit, const GuardConstraints& guards) {
  FTRACE(1, "relaxing guards for trace {}\n", unit.main());
  auto blocks = rpoSortCfg(unit);
  Block* reflowBlock = nullptr;

  for (auto* block : blocks) {
    for (auto& inst : *block) {
      if (!isGuardOp(inst.op())) continue;

      auto it = guards.find(&inst);
      auto constraint = it == guards.end() ? TypeConstraint() : it->second;

      // TODO(t2598894): Support relaxing inner types
      auto const oldType = inst.typeParam();
      auto newType = relaxType(oldType, constraint.category);

      if (constraint.knownType <= newType) {
        // If the known type is at least as good as the relaxed type, we can
        // replace the guard with an assert.
        auto newOp = guardToAssert(inst.op());
        FTRACE(1, "relaxGuards changing {}'s type to {}, op to {}\n",
               inst, constraint.knownType, newOp);
        inst.setTypeParam(constraint.knownType);
        inst.setOpcode(newOp);
        inst.setTaken(nullptr);

        if (!reflowBlock) reflowBlock = block;
      } else if (!oldType.equals(newType)) {
        FTRACE(1, "relaxGuards changing {}'s type to {}\n", inst, newType);
        inst.setTypeParam(newType);

        if (!reflowBlock) reflowBlock = block;
      }
    }
  }

  if (reflowBlock) reflowTypes(reflowBlock, blocks);

  return (bool)reflowBlock;
}
Exemplo n.º 3
0
/*
 * For all guard instructions in trace, check to see if we can relax the
 * destination type to something less specific. The GuardConstraints map
 * contains information about what properties of the guarded type matter for
 * each instruction. If simple is true, guards will not be relaxed past
 * DataTypeSpecific except guards which are relaxed all the way to
 * DataTypeGeneric. Returns true iff any changes were made to the trace.
 */
bool relaxGuards(IRUnit& unit, const GuardConstraints& guards, bool simple) {
  auto blocks = rpoSortCfg(unit);
  auto changed = false;

  for (auto* block : blocks) {
    for (auto& inst : *block) {
      if (!isGuardOp(inst.op())) continue;

      auto it = guards.find(&inst);
      auto constraint = it == guards.end() ? TypeConstraint() : it->second;

      if (simple && constraint.category > DataTypeGeneric &&
          constraint.category < DataTypeSpecific) {
        constraint.category = DataTypeSpecific;
      }

      // TODO(t2598894): Support relaxing inner types
      auto const oldType = inst.typeParam();
      auto newType = relaxType(oldType, constraint.category);

      if (constraint.knownType <= newType) {
        // If the known type is at least as good as the relaxed type, we can
        // replace the guard with an assert.
        auto newOp = guardToAssert(inst.op());
        auto newType = std::min(constraint.knownType, previousGuardType(&inst));
        FTRACE(1, "relaxGuards changing {}'s type to {}, op to {}\n",
               inst, newType, newOp);
        assert(!hasEdges(newOp));
        if (inst.hasEdges()) {
          block->push_back(unit.gen(Jmp, inst.marker(), inst.next()));
        }
        inst.setTypeParam(newType);
        inst.setOpcode(newOp);
        changed = true;
      } else if (!oldType.equals(newType)) {
        FTRACE(1, "relaxGuards changing {}'s type to {}\n", inst, newType);
        inst.setTypeParam(newType);
        changed = true;
      }
    }
  }

  if (!changed) return false;

  // Make a second pass to reflow types, with some special logic for loads.
  FrameState state(unit);
  for (auto* block : blocks) {
    state.startBlock(block);

    for (auto& inst : *block) {
      state.setMarker(inst.marker());
      visitLoad(&inst, state);
      retypeDests(&inst);
      state.update(&inst);
    }

    state.finishBlock(block);
  }

  return true;
}
Exemplo n.º 4
0
/*
 * For all guard instructions in unit, check to see if we can relax the
 * destination type to something less specific. The GuardConstraints map
 * contains information about what properties of the guarded type matter for
 * each instruction. If simple is true, guards will not be relaxed past
 * DataTypeSpecific except guards which are relaxed all the way to
 * DataTypeGeneric. Returns true iff any changes were made to the trace.
 */
bool relaxGuards(IRUnit& unit, const GuardConstraints& guards, bool simple) {
  Timer _t("optimize_relaxGuards");

  splitCriticalEdges(unit);
  auto blocks = rpoSortCfg(unit);
  auto changed = false;

  for (auto* block : blocks) {
    for (auto& inst : *block) {
      if (!isGuardOp(inst.op())) continue;

      auto it = guards.find(&inst);
      auto constraint = it == guards.end() ? TypeConstraint() : it->second;
      FTRACE(2, "relaxGuards processing {} with constraint {}\n",
             inst, constraint);

      if (simple && constraint.category > DataTypeGeneric &&
          constraint.category < DataTypeSpecific) {
        constraint.category = DataTypeSpecific;
      }

      auto const oldType = inst.typeParam();
      auto newType = relaxType(oldType, constraint);

      // Sometimes we (legitimately) end up with a guard like this:
      //
      // t4:StkPtr = GuardStk<BoxedArr,0,<DataTypeGeneric,
      //                                  inner:DataTypeSpecific,
      //                                  Type::BoxedCell>> t2:StkPtr
      //
      // The outer category is DataTypeGeneric because we know from eval stack
      // flavors that the top of the stack here is always boxed. The inner
      // category is DataTypeSpecific, indicating we care what the inner type
      // is, even though it's just a hint. If we treated this like any other
      // guard, we would relax the typeParam to Type::Gen and insert an assert
      // to Type::BoxedCell right after it. Unfortunately, this loses the hint
      // that the inner type is Arr. Eventually we should have some side
      // channel for passing around hints for inner ref types, but for now the
      // best we can do is forcibly keep the guard around, preserving the inner
      // type hint.
      if (constraint.assertedType.isBoxed() &&
          oldType < constraint.assertedType) {
        auto relaxedInner = relaxInner(oldType, constraint);

        if (relaxedInner < Type::BoxedCell && newType >= Type::BoxedCell) {
          FTRACE(1, "relaxGuards changing newType to {}\n", newType);
          newType = relaxedInner;
        }
      }

      if (constraint.assertedType < newType) {
        // If the asserted type is more specific than the new guarded type, set
        // the guard to the relaxed type but insert an assert operation between
        // the instruction and its dst. We go from something like this:
        //
        // t5:FramePtr = GuardLoc<Int, 4, <DataTypeGeneric,Int>> t4:FramePtr
        //
        // to this:
        //
        // t6:FramePtr = GuardLoc<Gen, 4> t4:FramePtr
        // t5:FramePtr = AssertLoc<Int, 4> t6:FramePtr

        auto* oldDst = inst.dst();
        auto* newDst = unit.genDst(&inst);
        auto* newAssert = [&] {
          switch (inst.op()) {
            case GuardLoc:
            case CheckLoc:
              return unit.genWithDst(oldDst,
                                     guardToAssert(inst.op()),
                                     inst.marker(),
                                     *inst.extra<LocalId>(),
                                     constraint.assertedType,
                                     newDst);

            case GuardStk:
            case CheckStk:
              return unit.genWithDst(oldDst,
                                     guardToAssert(inst.op()),
                                     inst.marker(),
                                     *inst.extra<StackOffset>(),
                                     constraint.assertedType,
                                     newDst);

            case CheckType:
              return unit.genWithDst(oldDst,
                                     guardToAssert(inst.op()),
                                     inst.marker(),
                                     constraint.assertedType,
                                     newDst);

            default: always_assert(false);
          }
        }();

        FTRACE(1, "relaxGuards inserting {} between {} and its dst, "
               "changing typeParam to {}\n",
               *newAssert, inst, newType);
        inst.setTypeParam(newType);

        // Now, insert the assert after the guard. For control flow guards,
        // this means inserting it on the next edge.
        if (inst.isControlFlow()) {
          auto* block = inst.next();
          block->insert(block->skipHeader(), newAssert);
        } else {
          auto* block = inst.block();
          auto it = block->iteratorTo(&inst);
          ++it;
          block->insert(it, newAssert);
        }

        changed = true;
      } else if (oldType != newType) {
        FTRACE(1, "relaxGuards changing {}'s type to {}\n", inst, newType);
        inst.setTypeParam(newType);
        changed = true;
      }
    }
  }

  if (!changed) return false;

  // Make a second pass to reflow types, with some special logic for loads.
  FrameState state(unit);
  for (auto* block : blocks) {
    state.startBlock(block);

    for (auto& inst : *block) {
      state.setMarker(inst.marker());
      copyProp(&inst);
      visitLoad(&inst, state);
      if (!removeGuard(unit, &inst, state)) {
        retypeDests(&inst);
        state.update(&inst);
      }
    }

    state.finishBlock(block);
  }

  return true;
}