MemoryEffects memoryEffects()

in Jit/hir/memory_effects.cpp [27:282]


MemoryEffects memoryEffects(const Instr& inst) {
  switch (inst.opcode()) {
    // Instructions that don't produce a borrowed reference, don't steal any
    // inputs, and don't write to heap locations that we track.
    case Opcode::kAssign:
    case Opcode::kBuildSlice:
    case Opcode::kBuildString:
    case Opcode::kDeopt:
    case Opcode::kDeoptPatchpoint:
    case Opcode::kDoubleBinaryOp:
    case Opcode::kFormatValue:
    case Opcode::kGuardType:
    case Opcode::kHintType:
    case Opcode::kIntBinaryOp:
    case Opcode::kIsSubtype:
    case Opcode::kPrimitiveUnaryOp:
    case Opcode::kPrimitiveBox:
    case Opcode::kPrimitiveCompare:
    case Opcode::kIntConvert:
    case Opcode::kPrimitiveUnbox:
    case Opcode::kIsNegativeAndErrOccurred:
    case Opcode::kIsErrStopAsyncIteration:
    case Opcode::kLoadEvalBreaker:
    case Opcode::kLoadVarObjectSize:
    case Opcode::kLongCompare:
    case Opcode::kMakeCell:
    case Opcode::kMakeCheckedDict:
    case Opcode::kMakeCheckedList:
    case Opcode::kMakeDict:
    case Opcode::kMakeFunction:
    case Opcode::kMakeListTuple:
    case Opcode::kMakeSet:
    case Opcode::kMakeTupleFromList:
    case Opcode::kRefineType:
    case Opcode::kSnapshot:
    case Opcode::kTpAlloc:
    case Opcode::kUseType:
    case Opcode::kWaitHandleLoadCoroOrResult:
    case Opcode::kWaitHandleLoadWaiter:
      return commonEffects(inst, AEmpty);

    // These push/pop shadow frames and should not get DCE'd.
    case Opcode::kBeginInlinedFunction:
    case Opcode::kEndInlinedFunction:
      return commonEffects(inst, AAny);

    // Can write to fields of its operands.
    case Opcode::kWaitHandleRelease:
    case Opcode::kSetCurrentAwaiter:
      return commonEffects(inst, AOther);

    // These can deopt but don't write to any memory locations when they fall
    // through.
    case Opcode::kCheckExc:
    case Opcode::kCheckField:
    case Opcode::kCheckFreevar:
    case Opcode::kCheckNeg:
    case Opcode::kCheckSequenceBounds:
    case Opcode::kCheckVar:
    case Opcode::kGuard:
      return commonEffects(inst, AEmpty);

    // Instructions that don't produce a borrowed reference, don't steal any
    // inputs, and may write all memory locations (usually from invoking
    // arbitrary user code).
    case Opcode::kBinaryOp:
    case Opcode::kCallEx:
    case Opcode::kCallExKw:
    case Opcode::kCallMethod:
    case Opcode::kCallStatic:
    case Opcode::kCallStaticRetVoid:
    case Opcode::kClearError:
    case Opcode::kCompare:
    case Opcode::kDeleteAttr:
    case Opcode::kDeleteSubscr:
    case Opcode::kCompareBool:
    case Opcode::kFillTypeAttrCache:
    case Opcode::kGetIter:
    case Opcode::kInPlaceOp:
    case Opcode::kInvokeIterNext:
    case Opcode::kInvokeStaticFunction:
    case Opcode::kInvokeMethod:
    case Opcode::kIsInstance:
    case Opcode::kIsTruthy:
    case Opcode::kLoadAttr:
    case Opcode::kLoadAttrSpecial:
    case Opcode::kLoadAttrSuper:
    case Opcode::kLoadGlobal:
    case Opcode::kLoadMethod:
    case Opcode::kLoadMethodSuper:
    case Opcode::kLongBinaryOp:
    case Opcode::kRepeatList:
    case Opcode::kRepeatTuple:
    case Opcode::kUnaryOp:
    case Opcode::kImportFrom:
    case Opcode::kImportName:
    case Opcode::kUnpackExToTuple:
    case Opcode::kVectorCall:
    case Opcode::kVectorCallKW:
    case Opcode::kVectorCallStatic:
      return commonEffects(inst, AManagedHeapAny);

    // Steals the reference to its second input and gives it to the cell
    case Opcode::kSetCellItem:
      return {true, AEmpty, {inst.NumOperands(), 2}, ACellItem};

    // Returns a stolen (from the cell), not borrowed, reference.
    case Opcode::kStealCellItem:
      return commonEffects(inst, AEmpty);

    // Instructions that return nullptr or a borrowed reference to a singleton
    // (usually None or True), and can invoke user code.
    case Opcode::kRunPeriodicTasks:
    case Opcode::kMergeDictUnpack:
    case Opcode::kMergeSetUnpack:
    case Opcode::kSetDictItem:
    case Opcode::kSetSetItem:
    case Opcode::kStoreAttr:
    case Opcode::kStoreSubscr:
      return {true, AEmpty, {}, AManagedHeapAny};

    case Opcode::kListAppend:
    case Opcode::kListExtend:
      return {true, AEmpty, {inst.NumOperands()}, AListItem};

    case Opcode::kIncref:
    case Opcode::kXIncref:
      return {false, AEmpty, {inst.NumOperands()}, AOther};

    case Opcode::kBatchDecref:
    case Opcode::kDecref:
    case Opcode::kXDecref:
      return {false, AEmpty, {1, 1}, AManagedHeapAny};

    case Opcode::kInitFunction:
      // InitFunction mostly writes to a bunch of func fields we don't track,
      // but it can also invoke the JIT which may at some point have effects
      // worth tracking.
      return commonEffects(inst, AOther);

    case Opcode::kInitListTuple: {
      // Steal all inputs except the first, which is the container to
      // initialize.
      util::BitVector inputs{inst.NumOperands()};
      inputs.fill(true);
      inputs.SetBit(0, false);
      auto may_store = static_cast<const InitListTuple&>(inst).is_tuple()
          ? ATupleItem
          : AListItem;
      return {false, AEmpty, std::move(inputs), may_store};
    }

    case Opcode::kStoreField:
      JIT_DCHECK(inst.NumOperands() == 3, "Unexpected number of operands");
      return {false, AEmpty, {3, 2}, AInObjectAttr};

    case Opcode::kLoadArg:
    case Opcode::kLoadCurrentFunc:
      return borrowFrom(inst, AFuncArgs);

    case Opcode::kLoadConst:
    case Opcode::kGuardIs:
      return borrowFrom(inst, AEmpty);

    case Opcode::kLoadCellItem:
      return borrowFrom(inst, ACellItem);

    case Opcode::kLoadField: {
      auto& ldfld = static_cast<const LoadField&>(inst);
      if (ldfld.borrowed()) {
        return borrowFrom(inst, AInObjectAttr);
      }
      return commonEffects(inst, AEmpty);
    }

    case Opcode::kLoadFieldAddress:
      return commonEffects(inst, AEmpty);

    case Opcode::kLoadFunctionIndirect:

    case Opcode::kLoadGlobalCached:
      return borrowFrom(inst, AGlobal);

    case Opcode::kLoadTupleItem:
      return borrowFrom(inst, ATupleItem);

    case Opcode::kLoadArrayItem:
      return borrowFrom(inst, AArrayItem | AListItem);
    case Opcode::kStoreArrayItem:
      // we steal a ref to our third operand, the value being stored
      return {
          false, AEmpty, {inst.NumOperands(), 1 << 2}, AArrayItem | AListItem};
    case Opcode::kLoadTypeAttrCacheItem:
      return borrowFrom(inst, ATypeAttrCache);

    case Opcode::kReturn:
      return {false, AEmpty, {1, 1}, AManagedHeapAny};

    case Opcode::kSetFunctionAttr: {
      JIT_DCHECK(inst.NumOperands() == 2, "Unexpected number of operands");
      return {false, AEmpty, {2, 1}, AFuncAttr};
    }

    case Opcode::kRaise:
      return {false, AEmpty, stealAllInputs(inst), AEmpty};

    case Opcode::kRaiseAwaitableError:
    case Opcode::kRaiseStatic:
      return commonEffects(inst, AManagedHeapAny);

    // The outputs of InitialYield and YieldValue are the `arg` argument to
    // _PyJIT_GenSend(), which is borrowed from its caller like all arguments
    // to C functions.
    case Opcode::kInitialYield:
      return {true, AFuncArgs, {inst.NumOperands()}, AAny};
    case Opcode::kYieldValue:
      return {true, AFuncArgs, {1, 1}, AAny};

    // YieldFrom's output is either the yielded value from the subiter or the
    // final result from a StopIteration, and is owned in either case.
    case Opcode::kYieldFrom: {
      return commonEffects(inst, AAny);
    }
    // YieldAndYieldFrom is equivalent to YieldFrom composed with YieldValue,
    // and steals the value it yields to the caller.
    case Opcode::kYieldAndYieldFrom: {
      return {false, AEmpty, {2, 1}, AAny};
    }

    case Opcode::kCast: {
      auto& cast = static_cast<const Cast&>(inst);
      if (cast.iserror()) {
        return commonEffects(inst, AEmpty);
      }
      // returns borrowed ref to Py_True/Py_False
      return borrowFrom(inst, AGlobal);
    }

    case Opcode::kCallCFunc:
      return commonEffects(inst, AManagedHeapAny);

    case Opcode::kBranch:
    case Opcode::kCondBranch:
    case Opcode::kCondBranchIterNotDone:
    case Opcode::kCondBranchCheckType:
    case Opcode::kPhi:
      JIT_CHECK(
          false,
          "Opcode %s doesn't have well-defined memory effects",
          inst.opname());
    case Opcode::kGetTuple:
      return commonEffects(inst, AAny);
  }

  JIT_CHECK(false, "Bad opcode %d", static_cast<int>(inst.opcode()));
}