Exemple #1
0
TEST(Type, RelaxType) {
  EXPECT_EQ(Type::Gen, relaxType(Type::BoxedStr, {DataTypeGeneric}));
  EXPECT_EQ(Type::BoxedInitCell | Type::Uncounted,
            relaxType(Type::BoxedObj | Type::InitNull,
                      {DataTypeCountness, DataTypeGeneric}));


  auto inner = TypeConstraint{DataTypeSpecialized};
  inner.setDesiredClass(SystemLib::s_IteratorClass);
  inner.innerCat = DataTypeSpecialized;
  inner.category = DataTypeSpecific;
  auto type = Type::Obj.specialize(SystemLib::s_IteratorClass).box();
  EXPECT_EQ("BoxedObj<Iterator>", type.toString());
  EXPECT_EQ(type, relaxType(type, inner));
}
/*
 * 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;
}
    void
    NonlinearSolver<PhysicalModel>::stabilizeNonlinearUpdate(V& dx, V& dxOld, const double omega) const
    {
        // The dxOld is updated with dx.
        // If omega is equal to 1., no relaxtion will be appiled.

        const V tempDxOld = dxOld;
        dxOld = dx;

        switch (relaxType()) {
            case DAMPEN:
                if (omega == 1.) {
                    return;
                }
                dx = dx*omega;
                return;
            case SOR:
                if (omega == 1.) {
                    return;
                }
                dx = dx*omega + (1.-omega)*tempDxOld;
                return;
            default:
                OPM_THROW(std::runtime_error, "Can only handle DAMPEN and SOR relaxation type.");
        }

        return;
    }
/*
 * Returns the least specific supertype of t that maintains the properties
 * required by cat.
 */
Type relaxType(Type t, DataTypeCategory cat) {
  always_assert(t.subtypeOf(Type::Gen));

  switch (cat) {
    case DataTypeGeneric:
      return Type::Gen;

    case DataTypeCountness:
      return t.notCounted() ? Type::Uncounted : t.unspecialize();

    case DataTypeCountnessInit:
      if (t.subtypeOf(Type::Uninit)) return Type::Uninit;
      return relaxType(t, DataTypeCountness);

    case DataTypeSpecific:
      assert(t.isKnownDataType());
      return t.unspecialize();

    case DataTypeSpecialized:
      assert(t.isSpecialized());
      return t;
  }

  not_reached();
}
Exemple #5
0
TEST(Type, RelaxType) {
  EXPECT_EQ(Type::Gen, relaxType(Type::BoxedStr, {DataTypeGeneric}));
  EXPECT_EQ(Type::BoxedInitCell | Type::Uncounted,
            relaxType(Type::BoxedObj | Type::InitNull,
                      {DataTypeCountness}));


  auto tc = TypeConstraint{DataTypeSpecialized};
  tc.setDesiredClass(SystemLib::s_IteratorClass);
  tc.category = DataTypeSpecialized;
  auto type = Type::Obj.specialize(SystemLib::s_IteratorClass);
  EXPECT_EQ("Obj<=Iterator", type.toString());
  EXPECT_EQ(type, relaxType(type, tc));

  EXPECT_EQ(Type::BoxedInitCell,
            relaxType(Type::BoxedInitCell, DataTypeCountnessInit));
  EXPECT_EQ(Type::BoxedInitCell,
            relaxType(Type::BoxedInitCell, DataTypeCountness));
}
Exemple #6
0
GuardConstraint relaxConstraint(GuardConstraint origGc,
                                Type knownType, Type toRelax) {
  ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n",
         origGc, knownType, toRelax);
  Trace::Indent _i;

  // AssertType can be given TCtx, which should never relax.
  if (toRelax.maybe(TCctx)) {
    always_assert(toRelax <= TCtx);
    return origGc;
  }

  auto const dstType = knownType & toRelax;
  always_assert_flog(typeFitsConstraint(dstType, origGc),
                     "refine({}, {}) doesn't fit {}",
                     knownType, toRelax, origGc);

  // Preserve origGc's weak property.
  GuardConstraint newGc{DataTypeGeneric};
  newGc.weak = origGc.weak;

  while (true) {
    if (newGc.isSpecialized()) {
      // We need to ask for the right kind of specialization, so grab it from
      // origGc.
      if (origGc.wantArrayKind()) newGc.setWantArrayKind();
      if (origGc.wantClass()) newGc.setDesiredClass(origGc.desiredClass());
    }

    auto const relaxed = relaxType(toRelax, newGc.category);
    auto const newDstType = relaxed & knownType;
    if (typeFitsConstraint(newDstType, origGc)) break;

    ITRACE(5, "newDstType = {}, newGc = {}; incrementing constraint\n",
      newDstType, newGc);
    incCategory(newGc.category);
  }
  // DataTypeCountness can be relaxed to DataTypeGeneric in
  // optimizeProfiledGuards, so we can't rely on this category to give type
  // information through guards.  Since relaxConstraint is used to relax the
  // DataTypeCategory for guards, we cannot return DataTypeCountness unless we
  // already had it to start with.  Instead, we return DataTypeBoxCountness,
  // which won't be further relaxed by optimizeProfiledGuards.
  if (newGc.category == DataTypeCountness && origGc != DataTypeCountness) {
    newGc.category = DataTypeBoxAndCountness;
  }
  ITRACE(4, "Returning {}\n", newGc);
  // newGc shouldn't be any more specific than origGc.
  always_assert(newGc.category <= origGc.category);
  return newGc;
}
Exemple #7
0
TEST(Type, Relax) {
  EXPECT_EQ(Type::BoxedInitCell | Type::InitNull,
            relaxType(Type::BoxedObj |Type::InitNull,
                      {DataTypeCountness, DataTypeGeneric}));

  EXPECT_EQ(TypeConstraint(DataTypeCountness, DataTypeCountness),
            relaxConstraint(TypeConstraint{DataTypeSpecific, DataTypeSpecific},
                            Type::BoxedCell,
                            Type::BoxedArr));

  EXPECT_EQ(TypeConstraint(DataTypeGeneric, DataTypeGeneric),
            relaxConstraint(TypeConstraint{DataTypeCountness, DataTypeSpecific},
                            Type::BoxedArr,
                            Type::BoxedCell));
}
Exemple #8
0
/*
 * relaxConstraint returns the least specific TypeConstraint 'tc' that doesn't
 * prevent the intersection of knownType and relaxType(toRelax, tc) from
 * satisfying origTc. It is used in IRBuilder::constrainValue and
 * IRBuilder::constrainStack to determine how to constrain the typeParam and
 * src values of CheckType/CheckStk instructions, and the src values of
 * AssertType/AssertStk instructions.
 *
 * AssertType example:
 * t24:Obj<C> = AssertType<{Obj<C>|InitNull}> t4:Obj
 *
 * If constrainValue is called with (t24, DataTypeSpecialized), relaxConstraint
 * will be called with (DataTypeSpecialized, Obj<C>|InitNull, Obj). After a few
 * iterations it will determine that constraining Obj with DataTypeCountness
 * will still allow the result type of the AssertType instruction to satisfy
 * DataTypeSpecialized, because relaxType(Obj, DataTypeCountness) == Obj.
 */
TypeConstraint relaxConstraint(const TypeConstraint origTc,
                               const Type knownType, const Type toRelax) {
  ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n",
         origTc, knownType, toRelax);
  Trace::Indent _i;

  auto const dstType = refineType(knownType, toRelax);
  always_assert_flog(typeFitsConstraint(dstType, origTc),
                     "refine({}, {}) doesn't fit {}",
                     knownType, toRelax, origTc);

  // Preserve origTc's weak property.
  TypeConstraint newTc{DataTypeGeneric, DataTypeGeneric};
  newTc.weak = origTc.weak;

  while (true) {
    if (newTc.isSpecialized()) {
      // We need to ask for the right kind of specialization, so grab it from
      // origTc.
      if (origTc.wantArrayKind()) newTc.setWantArrayKind();
      if (origTc.wantClass()) newTc.setDesiredClass(origTc.desiredClass());
    }

    auto const relaxed = relaxType(toRelax, newTc);
    auto const newDstType = refineType(relaxed, knownType);
    if (typeFitsConstraint(newDstType, origTc)) break;

    ITRACE(5, "newDstType = {}, newTc = {}; ", newDstType, newTc);
    if (newTc.category == DataTypeGeneric ||
        !typeFitsOuterConstraint(newDstType, origTc)) {
      FTRACE(5, "incrementing outer\n");
      incCategory(newTc.category);
    } else if (!typeFitsInnerConstraint(newDstType, origTc)) {
      FTRACE(5, "incrementing inner\n");
      incCategory(newTc.innerCat);
    } else {
      not_reached();
    }
  }

  ITRACE(4, "Returning {}\n", newTc);
  // newTc shouldn't be any more specific than origTc.
  always_assert(newTc.category <= origTc.category &&
                newTc.innerCat <= origTc.innerCat);
  return newTc;
}
/*
 * 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;
}
    void
    NonlinearSolver<PhysicalModel>::stabilizeNonlinearUpdate(BVector& dx, BVector& dxOld, const double omega) const
    {
        // The dxOld is updated with dx.
        // If omega is equal to 1., no relaxtion will be appiled.

        BVector tempDxOld = dxOld;
        dxOld = dx;

        switch (relaxType()) {
            case DAMPEN: {
                if (omega == 1.) {
                    return;
                }
                auto i = dx.size();
                for (i = 0; i < dx.size(); ++i) {
                    dx[i] *= omega;
                }
                return;
            }
            case SOR: {
                if (omega == 1.) {
                    return;
                }
                auto i = dx.size();
                for (i = 0; i < dx.size(); ++i) {
                    dx[i] *= omega;
                    tempDxOld[i] *= (1.-omega);
                    dx[i] += tempDxOld[i];
                }
                return;
            }
            default:
                OPM_THROW(std::runtime_error, "Can only handle DAMPEN and SOR relaxation type.");
        }

        return;
    }
Exemple #11
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& constraints,
                 RelaxGuardsFlags flags) {
  Timer _t(Timer::optimize_relaxGuards);
  ITRACE(2, "entering relaxGuards\n");
  Indent _i;
  bool simple = flags & RelaxSimple;
  bool reflow = flags & RelaxReflow;
  splitCriticalEdges(unit);
  auto& guards = constraints.guards;
  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;
      ITRACE(2, "relaxGuards processing {} with constraint {}\n",
             inst, constraint);

      auto simplifyCategory = [simple](DataTypeCategory& cat) {
        if (simple && cat > DataTypeGeneric && cat < DataTypeSpecific) {
          cat = DataTypeSpecific;
        }
      };
      simplifyCategory(constraint.category);
      simplifyCategory(constraint.innerCat);

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

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

  if (!changed) return false;
  if (!reflow) return true;

  // Make a second pass to reflow types, with some special logic for loads.
  FrameState state{unit, unit.entry()->front().marker()};
  for (auto* block : blocks) {
    ITRACE(2, "relaxGuards reflow entering B{}\n", block->id());
    Indent _i;
    state.startBlock(block, block->front().marker());

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

    state.finishBlock(block);
  }

  return true;
}
Exemple #12
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
}
Exemple #13
0
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;
}
Exemple #14
0
bool IRBuilder::constrainStack(SSATmp* sp, int32_t idx,
                               TypeConstraint tc) {
  if (!shouldConstrainGuards()) return false;
  always_assert(IMPLIES(tc.innerCat > DataTypeGeneric,
                        tc.category >= DataTypeCountness));

  ITRACE(1, "constrainStack({}, {}, {})\n", *sp->inst(), idx, tc);
  Indent _i;
  assert(sp->isA(Type::StkPtr));

  // We've hit a LdStack. If getStackValue gives us a value, recurse on
  // that. Otherwise, look at the instruction that gave us the type of the
  // stack element. If it's a GuardStk or CheckStk, it's our target. If it's
  // anything else, the value is new so there's no guard to relax.
  auto stackInfo = getStackValue(sp, idx);

  // 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(stackInfo.knownType, tc)) return false;

  IRInstruction* typeSrc = stackInfo.typeSrc;
  if (stackInfo.value) {
    ITRACE(1, "value = {}\n", *stackInfo.value->inst());
    return constrainValue(stackInfo.value, tc);
  } else if (typeSrc->is(AssertStk)) {
    // If the immutable typeParam fits the constraint, we're done.
    auto const typeParam = typeSrc->typeParam();
    if (typeFitsConstraint(typeParam, tc)) return false;

    auto const srcIdx = typeSrc->extra<StackOffset>()->offset;
    auto const srcType = getStackValue(typeSrc->src(0), srcIdx).knownType;
    auto const newTc = relaxConstraint(tc, typeParam, srcType);
    ITRACE(1, "tracing through {}, orig tc: {}, new tc: {}\n",
           *typeSrc, tc, newTc);
    return constrainStack(typeSrc->src(0), srcIdx, newTc);
  } else if (typeSrc->is(CheckStk)) {
    auto changed = false;
    auto const typeParam = typeSrc->typeParam();
    auto const srcIdx = typeSrc->extra<StackOffset>()->offset;
    auto const srcType = getStackValue(typeSrc->src(0), srcIdx).knownType;

    // 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(typeSrc, 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[typeSrc];
    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",
             *typeSrc, tc, newTc);
      changed = constrainStack(typeSrc->src(0), srcIdx, newTc) || changed;
    }
    return changed;
  } else {
    ITRACE(1, "typeSrc = {}\n", *typeSrc);
    return typeSrc->is(GuardStk) && constrainGuard(typeSrc, tc);
  }
}
Exemple #15
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;
}
    int
    NewtonSolver<PhysicalModel>::
    step(const double dt,
         ReservoirState& reservoir_state,
         WellState& well_state)
    {
        // Do model-specific once-per-step calculations.
        model_->prepareStep(dt, reservoir_state, well_state);

        // For each iteration we store in a vector the norms of the residual of
        // the mass balance for each active phase, the well flux and the well equations.
        std::vector<std::vector<double>> residual_norms_history;

        // Assemble residual and Jacobian, store residual norms.
        model_->assemble(reservoir_state, well_state, true);
        residual_norms_history.push_back(model_->computeResidualNorms());

        // Set up for main Newton loop.
        double omega = 1.0;
        int iteration = 0;
        bool converged = model_->getConvergence(dt, iteration);
        const int sizeNonLinear = model_->sizeNonLinear();
        V dxOld = V::Zero(sizeNonLinear);
        bool isOscillate = false;
        bool isStagnate = false;
        const enum RelaxType relaxtype = relaxType();
        int linearIterations = 0;

        // ----------  Main Newton loop  ----------
        while ( (!converged && (iteration < maxIter())) || (minIter() > iteration)) {
            // Compute the Newton update to the primary variables.
            V dx = model_->solveJacobianSystem();

            // Store number of linear iterations used.
            linearIterations += model_->linearIterationsLastSolve();

            // Stabilize the Newton update.
            detectNewtonOscillations(residual_norms_history, iteration, relaxRelTol(), isOscillate, isStagnate);
            if (isOscillate) {
                omega -= relaxIncrement();
                omega = std::max(omega, relaxMax());
                if (model_->terminalOutputEnabled()) {
                    std::cout << " Oscillating behavior detected: Relaxation set to " << omega << std::endl;
                }
            }
            stabilizeNewton(dx, dxOld, omega, relaxtype);

            // Apply the update, the model may apply model-dependent
            // limitations and chopping of the update.
            model_->updateState(dx, reservoir_state, well_state);

            // Assemble residual and Jacobian, store residual norms.
            model_->assemble(reservoir_state, well_state, false);
            residual_norms_history.push_back(model_->computeResidualNorms());

            // increase iteration counter
            ++iteration;

            converged = model_->getConvergence(dt, iteration);
        }

        if (!converged) {
            if (model_->terminalOutputEnabled()) {
                std::cerr << "WARNING: Failed to compute converged solution in " << iteration << " iterations." << std::endl;
            }
            return -1; // -1 indicates that the solver has to be restarted
        }

        linearIterations_ += linearIterations;
        newtonIterations_ += iteration;
        linearIterationsLast_ = linearIterations;
        newtonIterationsLast_ = iteration;

        // Do model-specific post-step actions.
        model_->afterStep(dt, reservoir_state, well_state);

        return linearIterations;
    }
Exemple #17
0
TEST(Type, Relax) {
  EXPECT_EQ(Type::BoxedInitCell | Type::InitNull,
            relaxType(Type::BoxedObj | Type::InitNull,
                      {DataTypeCountness, Type::Gen, DataTypeGeneric}));
}
Exemple #18
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;
}