in Jit/hir/ssa.cpp [275:595]
Type outputType(
const Instr& instr,
const std::function<Type(std::size_t)>& get_op_type) {
switch (instr.opcode()) {
case Opcode::kCallEx:
case Opcode::kCallExKw:
case Opcode::kCallMethod:
case Opcode::kCompare:
case Opcode::kBinaryOp:
case Opcode::kFillTypeAttrCache:
case Opcode::kGetIter:
case Opcode::kImportFrom:
case Opcode::kImportName:
case Opcode::kInPlaceOp:
case Opcode::kInvokeIterNext:
case Opcode::kInvokeMethod:
case Opcode::kLoadAttr:
case Opcode::kLoadAttrSpecial:
case Opcode::kLoadAttrSuper:
case Opcode::kLoadGlobal:
case Opcode::kLoadMethod:
case Opcode::kLoadMethodSuper:
case Opcode::kLoadTupleItem:
case Opcode::kUnaryOp:
case Opcode::kVectorCall:
case Opcode::kVectorCallKW:
case Opcode::kVectorCallStatic:
case Opcode::kWaitHandleLoadCoroOrResult:
return TObject;
case Opcode::kBuildString:
return TMortalUnicode;
// Many opcodes just return a possibly-null PyObject*. Some of these will
// be further specialized based on the input types in the hopefully near
// future.
case Opcode::kCallCFunc:
case Opcode::kLoadCellItem:
case Opcode::kLoadGlobalCached:
case Opcode::kStealCellItem:
case Opcode::kWaitHandleLoadWaiter:
case Opcode::kYieldAndYieldFrom:
case Opcode::kYieldFrom:
case Opcode::kYieldValue:
return TOptObject;
case Opcode::kFormatValue:
return TUnicode;
case Opcode::kLoadVarObjectSize:
return TCInt64;
case Opcode::kInvokeStaticFunction:
return static_cast<const InvokeStaticFunction&>(instr).ret_type();
case Opcode::kLoadArrayItem:
return static_cast<const LoadArrayItem&>(instr).type();
case Opcode::kLoadField:
return static_cast<const LoadField&>(instr).type();
case Opcode::kLoadFieldAddress:
return TCPtr;
case Opcode::kCallStatic: {
auto& call = static_cast<const CallStatic&>(instr);
return call.ret_type();
}
case Opcode::kIntConvert: {
auto& conv = static_cast<const IntConvert&>(instr);
return conv.type();
}
case Opcode::kIntBinaryOp: {
auto& binop = static_cast<const IntBinaryOp&>(instr);
if (binop.op() == BinaryOpKind::kPower ||
binop.op() == BinaryOpKind::kPowerUnsigned) {
return TCDouble;
}
return binop.left()->type().unspecialized();
}
case Opcode::kDoubleBinaryOp: {
return TCDouble;
}
case Opcode::kPrimitiveCompare:
return TCBool;
case Opcode::kPrimitiveUnaryOp:
// TODO if we have a specialized input type we should really be
// constant-folding
if (static_cast<const PrimitiveUnaryOp&>(instr).op() ==
PrimitiveUnaryOpKind::kNotInt) {
return TCBool;
}
return get_op_type(0).unspecialized();
// Some return something slightly more interesting.
case Opcode::kBuildSlice:
return TMortalSlice;
case Opcode::kGetTuple:
return TTupleExact;
case Opcode::kInitialYield:
return TOptNoneType;
case Opcode::kLoadArg: {
auto& loadarg = static_cast<const LoadArg&>(instr);
Type typ = loadarg.type();
return typ <= TCEnum ? TCInt64 : typ;
}
case Opcode::kLoadCurrentFunc:
return TFunc;
case Opcode::kLoadEvalBreaker:
return TCInt32;
case Opcode::kMakeCell:
return TMortalCell;
case Opcode::kMakeDict:
return TMortalDict;
case Opcode::kMakeCheckedDict: {
auto& makechkdict = static_cast<const MakeCheckedDict&>(instr);
return makechkdict.type();
}
case Opcode::kMakeCheckedList: {
auto& makechklist = static_cast<const MakeCheckedList&>(instr);
return makechklist.type();
}
case Opcode::kMakeFunction:
return TMortalFunc;
case Opcode::kMakeSet:
return TMortalSet;
case Opcode::kLongBinaryOp: {
auto& binop = static_cast<const LongBinaryOp&>(instr);
if (binop.op() == BinaryOpKind::kTrueDivide) {
return TFloatExact;
}
return TLongExact;
}
case Opcode::kLongCompare:
case Opcode::kRunPeriodicTasks:
return TBool;
// TODO(bsimmers): These wrap C functions that return 0 for success and -1
// for an error, which is converted into Py_None or nullptr,
// respectively. At some point we should get rid of this extra layer and
// deal with the int return value directly.
case Opcode::kListExtend:
case Opcode::kMergeDictUnpack:
case Opcode::kStoreAttr:
return TNoneType;
case Opcode::kListAppend:
case Opcode::kMergeSetUnpack:
case Opcode::kSetSetItem:
case Opcode::kSetDictItem:
case Opcode::kStoreSubscr:
return TCInt32;
case Opcode::kIsErrStopAsyncIteration:
return TCInt32;
case Opcode::kIsNegativeAndErrOccurred:
return TCInt64;
// Some compute their output type from either their inputs or some other
// source.
// Executing LoadTypeAttrCacheItem<cache_id, 1> is only legal if
// appropriately guarded by LoadTypeAttrCacheItem<cache_id, 0>, and the
// former will always produce a non-null object.
//
// TODO(bsimmers): We should probably split this into two instructions
// rather than changing the output type based on the item index.
case Opcode::kLoadTypeAttrCacheItem: {
auto item = static_cast<const LoadTypeAttrCacheItem&>(instr).item_idx();
return item == 1 ? TObject : TOptObject;
}
case Opcode::kAssign:
return get_op_type(0);
case Opcode::kLoadConst: {
return static_cast<const LoadConst&>(instr).type();
}
case Opcode::kMakeListTuple: {
auto is_tuple = static_cast<const MakeListTuple&>(instr).is_tuple();
return is_tuple ? TMortalTupleExact : TMortalListExact;
}
case Opcode::kMakeTupleFromList:
case Opcode::kUnpackExToTuple:
return TMortalTupleExact;
case Opcode::kPhi: {
auto ty = TBottom;
for (std::size_t i = 0, n = instr.NumOperands(); i < n; ++i) {
ty |= get_op_type(i);
}
return ty;
}
case Opcode::kCheckSequenceBounds: {
return TCInt64;
}
// 1 if comparison is true, 0 if not, -1 on error
case Opcode::kCompareBool:
case Opcode::kIsInstance:
// 1 if is subtype, 0 if not
case Opcode::kIsSubtype:
// 1, 0 if the value is truthy, not truthy
case Opcode::kIsTruthy: {
return TCInt32;
}
case Opcode::kLoadFunctionIndirect: {
return TObject;
}
case Opcode::kRepeatList: {
return TListExact;
}
case Opcode::kRepeatTuple: {
return TTupleExact;
}
case Opcode::kPrimitiveBox: {
// This duplicates the logic in Type::asBoxed(), but it has enough
// special cases (for exactness/optionality/nullptr) that it's not worth
// trying to reuse it here.
auto& pb = static_cast<const PrimitiveBox&>(instr);
if (pb.type() <= TCEnum) {
// Calling an enum type in JITRT_BoxEnum can raise an exception
return TOptObject;
}
if (pb.value()->type() <= TCDouble) {
return TOptFloatExact;
}
if (pb.value()->type() <= (TCUnsigned | TCSigned | TNullptr)) {
// Special Nullptr case for an uninitialized variable; load zero.
return TOptLongExact;
}
if (pb.value()->type() <= TCBool) {
// JITRT_BoxBool cannot fail since it returns one of two globals and
// does not allocate.
return TBool;
}
JIT_CHECK(
false,
"only primitive numeric types should be boxed. type verification"
"missed an unexpected type %s",
pb.value()->type());
}
case Opcode::kPrimitiveUnbox: {
auto& unbox = static_cast<const PrimitiveUnbox&>(instr);
Type typ = unbox.type();
return typ <= TCEnum ? TCInt64 : typ;
}
// Check opcodes return a copy of their input that is statically known to
// not be null.
case Opcode::kCheckExc:
case Opcode::kCheckField:
case Opcode::kCheckFreevar:
case Opcode::kCheckNeg:
case Opcode::kCheckVar: {
return get_op_type(0) - TNullptr;
}
case Opcode::kGuardIs: {
return Type::fromObject(static_cast<const GuardIs&>(instr).target());
}
case Opcode::kCast: {
auto& cast = static_cast<const Cast&>(instr);
Type to_type = Type::fromType(cast.pytype()) |
(cast.optional() ? TNoneType : TBottom);
return to_type;
}
case Opcode::kTpAlloc: {
auto& tp_alloc = static_cast<const TpAlloc&>(instr);
Type alloc_type = Type::fromTypeExact(tp_alloc.pytype());
return alloc_type;
}
// Refine type gives us more information about the type of its input.
case Opcode::kRefineType: {
auto type = static_cast<const RefineType&>(instr).type();
return get_op_type(0) & type;
}
case Opcode::kGuardType: {
auto type = static_cast<const GuardType&>(instr).target();
return get_op_type(0) & type;
}
// Finally, some opcodes have no destination.
case Opcode::kBatchDecref:
case Opcode::kBeginInlinedFunction:
case Opcode::kBranch:
case Opcode::kCallStaticRetVoid:
case Opcode::kClearError:
case Opcode::kCondBranch:
case Opcode::kCondBranchCheckType:
case Opcode::kCondBranchIterNotDone:
case Opcode::kDecref:
case Opcode::kDeleteAttr:
case Opcode::kDeleteSubscr:
case Opcode::kDeopt:
case Opcode::kDeoptPatchpoint:
case Opcode::kEndInlinedFunction:
case Opcode::kGuard:
case Opcode::kHintType:
case Opcode::kIncref:
case Opcode::kInitFunction:
case Opcode::kInitListTuple:
case Opcode::kRaise:
case Opcode::kRaiseAwaitableError:
case Opcode::kRaiseStatic:
case Opcode::kReturn:
case Opcode::kSetCurrentAwaiter:
case Opcode::kSetCellItem:
case Opcode::kSetFunctionAttr:
case Opcode::kSnapshot:
case Opcode::kStoreArrayItem:
case Opcode::kStoreField:
case Opcode::kUseType:
case Opcode::kWaitHandleRelease:
case Opcode::kXDecref:
case Opcode::kXIncref:
JIT_CHECK(false, "Opcode %s has no output", instr.opname());
}
JIT_CHECK(false, "Bad opcode %d", static_cast<int>(instr.opcode()));
}