GenTree* gtNewBitCastNode(var_types type, GenTree* arg);
public:
- GenTree* gtNewStructVal(ClassLayout* layout, GenTree* addr, GenTreeFlags indirFlags = GTF_EMPTY);
-
GenTreeCall* gtNewCallNode(gtCallTypes callType,
CORINFO_METHOD_HANDLE handle,
var_types type,
GenTreeIndir* gtNewIndir(var_types typ, GenTree* addr, GenTreeFlags indirFlags = GTF_EMPTY);
+ GenTree* gtNewLoadValueNode(
+ var_types type, ClassLayout* layout, GenTree* addr, GenTreeFlags indirFlags = GTF_EMPTY);
+
+ GenTree* gtNewLoadValueNode(ClassLayout* layout, GenTree* addr, GenTreeFlags indirFlags = GTF_EMPTY)
+ {
+ return gtNewLoadValueNode(layout->GetType(), layout, addr, indirFlags);
+ }
+
+ GenTree* gtNewLoadValueNode(var_types type, GenTree* addr, GenTreeFlags indirFlags = GTF_EMPTY)
+ {
+ return gtNewLoadValueNode(type, nullptr, addr, indirFlags);
+ }
+
GenTree* gtNewNullCheck(GenTree* addr, BasicBlock* basicBlock);
var_types gtTypeForNullCheck(GenTree* tree);
CORINFO_ACCESS_FLAGS access,
CORINFO_FIELD_INFO* pFieldInfo,
var_types lclTyp,
- CORINFO_CLASS_HANDLE structType,
GenTree* assg);
GenTree* gtNewNothingNode();
// Get the number of a layout for the specified class handle.
unsigned typGetObjLayoutNum(CORINFO_CLASS_HANDLE classHandle);
+ var_types TypeHandleToVarType(CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr);
+ var_types TypeHandleToVarType(CorInfoType jitType, CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout = nullptr);
+
//-------------------------- Global Compiler Data ------------------------------------
#ifdef DEBUG
inline GenTreeIndir* Compiler::gtNewIndexIndir(GenTreeIndexAddr* indexAddr)
{
GenTreeIndir* index;
- if (varTypeIsStruct(indexAddr->gtElemType))
+ if (indexAddr->gtElemType == TYP_STRUCT)
{
index = gtNewBlkIndir(typGetObjLayout(indexAddr->gtStructElemClass), indexAddr);
}
return arrOp;
}
-//------------------------------------------------------------------------
-// gtNewBlkIndir: Create a struct indirection node.
-//
-// Arguments:
-// layout - The struct layout
-// addr - Address of the indirection
-// indirFlags - Indirection flags
-//
-// Return Value:
-// The created GT_BLK node.
-//
-inline GenTreeBlk* Compiler::gtNewBlkIndir(ClassLayout* layout, GenTree* addr, GenTreeFlags indirFlags)
-{
- assert((indirFlags & ~GTF_IND_FLAGS) == GTF_EMPTY);
-
- GenTreeBlk* blkNode = new (this, GT_BLK) GenTreeBlk(GT_BLK, layout->GetType(), addr, layout);
- blkNode->gtFlags |= indirFlags;
- blkNode->SetIndirExceptionFlags(this);
-
- if ((indirFlags & GTF_IND_INVARIANT) == 0)
- {
- blkNode->gtFlags |= GTF_GLOB_REF;
- }
-
- if ((indirFlags & GTF_IND_VOLATILE) != 0)
- {
- blkNode->gtFlags |= GTF_ORDER_SIDEEFF;
- }
-
- return blkNode;
-}
-
-//------------------------------------------------------------------------------
-// gtNewIndir : Create an indirection node.
-//
-// Arguments:
-// typ - Type of the node
-// addr - Address of the indirection
-// indirFlags - Indirection flags
-//
-// Return Value:
-// The created GT_IND node.
-//
-inline GenTreeIndir* Compiler::gtNewIndir(var_types typ, GenTree* addr, GenTreeFlags indirFlags)
-{
- assert((indirFlags & ~GTF_IND_FLAGS) == GTF_EMPTY);
-
- GenTree* indir = gtNewOperNode(GT_IND, typ, addr);
- indir->gtFlags |= indirFlags;
- indir->SetIndirExceptionFlags(this);
-
- return indir->AsIndir();
-}
-
//------------------------------------------------------------------------------
// gtNewNullCheck : Helper to create a null check node.
//
return ((var_types)preciseVarTypeMap[type]);
};
+inline var_types Compiler::TypeHandleToVarType(CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout)
+{
+ CorInfoType jitType = info.compCompHnd->asCorInfoType(handle);
+ var_types type = TypeHandleToVarType(jitType, handle, pLayout);
+
+ return type;
+}
+
+inline var_types Compiler::TypeHandleToVarType(CorInfoType jitType, CORINFO_CLASS_HANDLE handle, ClassLayout** pLayout)
+{
+ ClassLayout* layout = nullptr;
+ var_types type = JITtype2varType(jitType);
+ if (type == TYP_STRUCT)
+ {
+ layout = typGetObjLayout(handle);
+ type = layout->GetType();
+ }
+
+ if (pLayout != nullptr)
+ {
+ *pLayout = layout;
+ }
+
+ return type;
+}
+
inline CORINFO_CALLINFO_FLAGS combine(CORINFO_CALLINFO_FLAGS flag1, CORINFO_CALLINFO_FLAGS flag2)
{
return (CORINFO_CALLINFO_FLAGS)(flag1 | flag2);
}
else if (helper == CORINFO_HELP_GETSHARED_NONGCTHREADSTATIC_BASE_NOCTOR_OPTIMIZED)
{
- result = gtNewHelperCallNode(helper, type, gtNewIconNode(typeIndex, TYP_UINT));
+ result = gtNewHelperCallNode(helper, type, gtNewIconNode(typeIndex));
result->SetExpTLSFieldAccess();
}
else
GenTreeIntCon* Compiler::gtNewIconNode(ssize_t value, var_types type)
{
+ assert(genActualType(type) == type);
return new (this, GT_CNS_INT) GenTreeIntCon(type, value);
}
}
#endif // FEATURE_SIMD
- switch (genActualType(type))
+ type = genActualType(type);
+ switch (type)
{
case TYP_INT:
case TYP_REF:
return fieldNode;
}
+//------------------------------------------------------------------------
+// gtNewLoadValueNode: Return a node that represents a loaded value.
+//
+// Arguments:
+// type - Type to load
+// layout - Struct layout to load
+// addr - The address
+// indirFlags - Indirection flags
+//
+// Return Value:
+// A "BLK/IND" node, or "LCL_VAR" if "addr" points to a compatible local.
+//
+GenTree* Compiler::gtNewLoadValueNode(var_types type, ClassLayout* layout, GenTree* addr, GenTreeFlags indirFlags)
+{
+ assert(((indirFlags & ~GTF_IND_FLAGS) == 0) && ((type != TYP_STRUCT) || (layout != nullptr)));
+
+ if (((indirFlags & GTF_IND_VOLATILE) == 0) && addr->IsLclVarAddr())
+ {
+ unsigned lclNum = addr->AsLclFld()->GetLclNum();
+ LclVarDsc* varDsc = lvaGetDesc(lclNum);
+ if (!lvaIsImplicitByRefLocal(lclNum) && (varDsc->TypeGet() == type) &&
+ ((type != TYP_STRUCT) || ClassLayout::AreCompatible(layout, varDsc->GetLayout())))
+ {
+ return gtNewLclvNode(lclNum, type);
+ }
+ }
+
+ GenTree* node;
+ if (type == TYP_STRUCT)
+ {
+ node = gtNewBlkIndir(layout, addr, indirFlags);
+ }
+ else
+ {
+ node = gtNewIndir(type, addr, indirFlags);
+ node->gtFlags |= GTF_GLOB_REF;
+ }
+
+ return node;
+}
+
+//------------------------------------------------------------------------
+// gtNewBlkIndir: Create a struct indirection node.
+//
+// Arguments:
+// layout - The struct layout
+// addr - Address of the indirection
+// indirFlags - Indirection flags
+//
+// Return Value:
+// The created GT_BLK node.
+//
+GenTreeBlk* Compiler::gtNewBlkIndir(ClassLayout* layout, GenTree* addr, GenTreeFlags indirFlags)
+{
+ assert((indirFlags & ~GTF_IND_FLAGS) == GTF_EMPTY);
+
+ GenTreeBlk* blkNode = new (this, GT_BLK) GenTreeBlk(GT_BLK, layout->GetType(), addr, layout);
+ blkNode->gtFlags |= indirFlags;
+ blkNode->SetIndirExceptionFlags(this);
+
+ if ((indirFlags & GTF_IND_INVARIANT) == 0)
+ {
+ blkNode->gtFlags |= GTF_GLOB_REF;
+ }
+
+ if ((indirFlags & GTF_IND_VOLATILE) != 0)
+ {
+ blkNode->gtFlags |= GTF_ORDER_SIDEEFF;
+ }
+
+ return blkNode;
+}
+
+//------------------------------------------------------------------------------
+// gtNewIndir : Create an indirection node.
+//
+// Arguments:
+// typ - Type of the node
+// addr - Address of the indirection
+// indirFlags - Indirection flags
+//
+// Return Value:
+// The created GT_IND node.
+//
+GenTreeIndir* Compiler::gtNewIndir(var_types typ, GenTree* addr, GenTreeFlags indirFlags)
+{
+ assert((indirFlags & ~GTF_IND_FLAGS) == GTF_EMPTY);
+
+ GenTree* indir = gtNewOperNode(GT_IND, typ, addr);
+ indir->gtFlags |= indirFlags;
+ indir->SetIndirExceptionFlags(this);
+
+ if ((indirFlags & GTF_IND_VOLATILE) != 0)
+ {
+ indir->gtFlags |= GTF_ORDER_SIDEEFF;
+ }
+
+ return indir->AsIndir();
+}
+
/*****************************************************************************
*
* Create a node that will assign 'src' to 'dst'.
dst->gtFlags |= GTF_DONT_CSE;
#if defined(FEATURE_SIMD) && !defined(TARGET_X86)
- // TODO-CQ: x86 Windows supports multi-reg returns but not SIMD multi-reg returns
-
- if (varTypeIsSIMD(dst->gtType))
+ // TODO-Cleanup: enable on Windows x86.
+ if (varTypeIsSIMD(dst))
{
// We want to track SIMD assignments as being intrinsics since they
// are functionally SIMD `mov` instructions and are more efficient
}
//------------------------------------------------------------------------
-// gtNewStructVal: Return a node that represents a struct or block value
-//
-// Arguments:
-// layout - The struct's layout
-// addr - The struct's address
-// indirFlags - Indirection flags
-//
-// Return Value:
-// An "OBJ/BLK" node, or "LCL_VAR" node if "addr" points to a local
-// with a layout compatible with "layout".
-//
-GenTree* Compiler::gtNewStructVal(ClassLayout* layout, GenTree* addr, GenTreeFlags indirFlags)
-{
- assert((indirFlags & ~GTF_IND_FLAGS) == 0);
-
- if (((indirFlags & GTF_IND_VOLATILE) == 0) && addr->IsLclVarAddr())
- {
- unsigned lclNum = addr->AsLclFld()->GetLclNum();
- LclVarDsc* varDsc = lvaGetDesc(lclNum);
- if (!lvaIsImplicitByRefLocal(lclNum) && varTypeIsStruct(varDsc) &&
- ClassLayout::AreCompatible(layout, varDsc->GetLayout()))
- {
- return gtNewLclvNode(lclNum, varDsc->TypeGet());
- }
- }
-
- return gtNewBlkIndir(layout, addr, indirFlags);
-}
-
-//------------------------------------------------------------------------
// FixupInitBlkValue: Fixup the init value for an initBlk operation
//
// Arguments:
//
GenTree* Compiler::gtNewBlkOpNode(GenTree* dst, GenTree* srcOrFillVal, bool isVolatile)
{
- assert(varTypeIsStruct(dst) && (dst->OperIsBlk() || dst->OperIsLocal() || dst->OperIs(GT_FIELD)));
+ assert(varTypeIsStruct(dst) && (dst->OperIsIndir() || dst->OperIsLocal() || dst->OperIs(GT_FIELD)));
bool isCopyBlock = srcOrFillVal->TypeGet() == dst->TypeGet();
if (!isCopyBlock) // InitBlk
{
// TODO-Cleanup: similar logic already exists in "gtNewAssignNode",
// however, it is not enabled for x86. Fix that and delete this code.
- GenTreeLclVarCommon* dstLclNode = nullptr;
-
- if (dst->OperIs(GT_LCL_VAR))
- {
- dstLclNode = dst->AsLclVar();
- }
- else if (dst->OperIsBlk() && dst->AsIndir()->Addr()->IsLclVarAddr())
- {
- dstLclNode = dst->AsIndir()->Addr()->AsLclFld();
- }
-
- if ((dstLclNode != nullptr) && varTypeIsStruct(lvaGetDesc(dstLclNode)))
- {
- setLclRelatedToSIMDIntrinsic(dstLclNode);
- }
+ SetOpLclRelatedToSIMDIntrinsic(dst);
}
#endif // FEATURE_SIMD
}
else if ((dstTyp == TYP_STRUCT) && (valTyp == TYP_INT))
{
- // It could come from `ASG(struct, 0)` that was propagated to `RETURN struct(0)`,
- // and now it is merging to a struct again.
- assert(tmp == genReturnLocal);
+ assert(val->IsInitVal());
ok = true;
}
GenTree* asg;
GenTree* dest = gtNewLclvNode(tmp, dstTyp);
- if (val->IsConstInitVal())
+ if (val->IsInitVal())
{
asg = gtNewAssignNode(dest, val);
}
CORINFO_ACCESS_FLAGS access,
CORINFO_FIELD_INFO* pFieldInfo,
var_types lclTyp,
- CORINFO_CLASS_HANDLE structType,
GenTree* assg)
{
assert(pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_HELPER ||
GenTree* args[4];
size_t nArgs = 0;
/* If we can't access it directly, we need to call a helper function */
- var_types helperType = TYP_BYREF;
+ var_types helperType = TYP_BYREF;
+ CORINFO_CLASS_HANDLE structType = pFieldInfo->structType;
if (pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_HELPER)
{
// helper needs pointer to struct, not struct itself
if (pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT)
{
- assert(structType != nullptr);
assg = impGetStructAddr(assg, structType, CHECK_SPILL_ALL, true);
}
else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT)
// OK, now do the indirection
if (access & CORINFO_ACCESS_GET)
{
- if (varTypeIsStruct(lclTyp))
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(pFieldInfo->fieldType, structType, &layout);
+ if (lclTyp == TYP_STRUCT)
{
- result = gtNewBlkIndir(typGetObjLayout(structType), result);
+ result = gtNewBlkIndir(layout, result);
}
else
{
// as used by a SIMD intrinsic, and if so, set that local var appropriately.
//
// Arguments:
-// op - The tree, to be an operand of a new GT_SIMD node, to check.
+// op - The tree, to be an operand of a new SIMD-related node, to check.
//
void Compiler::SetOpLclRelatedToSIMDIntrinsic(GenTree* op)
{
- if (op == nullptr)
- {
- return;
- }
-
- if (op->OperIs(GT_LCL_VAR))
+ if ((op != nullptr) && op->OperIs(GT_LCL_VAR))
{
setLclRelatedToSIMDIntrinsic(op);
}
- else if (op->OperIsBlk() && op->AsIndir()->Addr()->IsLclVarAddr())
- {
- setLclRelatedToSIMDIntrinsic(op->AsIndir()->Addr());
- }
}
void GenTreeMultiOp::ResetOperandArray(size_t newOperandCount,
return OperIsInitVal(OperGet());
}
+ bool IsInitVal() const
+ {
+ return IsIntegralConst(0) || OperIsInitVal();
+ }
+
bool IsConstInitVal() const
{
return (gtOper == GT_CNS_INT) || (OperIsInitVal() && (gtGetOp1()->gtOper == GT_CNS_INT));
BasicBlock* block /* = NULL */
)
{
- GenTree* dst = gtNewStructVal(typGetObjLayout(structHnd), destAddr);
- return impAssignStruct(dst, src, curLevel, pAfterStmt, di, block);
+ var_types type = src->TypeGet();
+ ClassLayout* layout = (type == TYP_STRUCT) ? src->GetLayout(this) : nullptr;
+ GenTree* dst = gtNewLoadValueNode(type, layout, destAddr);
+ GenTree* store = impAssignStruct(dst, src, curLevel, pAfterStmt, di, block);
+
+ return store;
}
//------------------------------------------------------------------------
else
#endif
{
- noway_assert(blockNode->OperIsBlk() || blockNode->OperIs(GT_FIELD));
+ noway_assert(blockNode->OperIsIndir() || blockNode->OperIs(GT_FIELD));
// Sink the GT_COMMA below the blockNode addr.
// That is GT_COMMA(op1, op2=blockNode) is transformed into
if (!(access & CORINFO_ACCESS_ADDRESS))
{
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(pFieldInfo->fieldType, pFieldInfo->structType, &layout);
+
// TODO-CQ: mark the indirections non-faulting.
- if (varTypeIsStruct(lclTyp))
+ if (lclTyp == TYP_STRUCT)
{
- op1 = gtNewBlkIndir(typGetObjLayout(pFieldInfo->structType), op1);
+ op1 = gtNewBlkIndir(layout, op1);
}
else
{
JITDUMP(" %08X", resolvedToken.token);
ldelemClsHnd = resolvedToken.hClass;
- lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(ldelemClsHnd));
+ lclTyp = TypeHandleToVarType(ldelemClsHnd);
tiRetVal = verMakeTypeInfo(ldelemClsHnd);
tiRetVal.NormaliseForStack();
goto ARR_LD;
JITDUMP(" %08X", resolvedToken.token);
stelemClsHnd = resolvedToken.hClass;
- lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(stelemClsHnd));
+ lclTyp = TypeHandleToVarType(stelemClsHnd);
if (lclTyp != TYP_REF)
{
case CEE_LDFLDA:
case CEE_LDSFLDA:
{
-
bool isLoadAddress = (opcode == CEE_LDFLDA || opcode == CEE_LDSFLDA);
bool isLoadStatic = (opcode == CEE_LDSFLD || opcode == CEE_LDSFLDA);
eeGetFieldInfo(&resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo);
- // Figure out the type of the member. We always call canAccessField, so you always need this
- // handle
- CorInfoType ciType = fieldInfo.fieldType;
- clsHnd = fieldInfo.structType;
-
- lclTyp = JITtype2varType(ciType);
+ // Note we avoid resolving the normalized (struct) type just yet; we may not need it (for ld[s]flda).
+ lclTyp = JITtype2varType(fieldInfo.fieldType);
+ clsHnd = fieldInfo.structType;
if (compIsForInlining())
{
case CORINFO_FIELD_INSTANCE_ADDR_HELPER:
case CORINFO_FIELD_STATIC_ADDR_HELPER:
case CORINFO_FIELD_STATIC_TLS:
-
compInlineResult->NoteFatal(InlineObservation::CALLEE_LDFLD_NEEDS_HELPER);
return;
break;
}
- if (!isLoadAddress && (fieldInfo.fieldFlags & CORINFO_FLG_FIELD_STATIC) && lclTyp == TYP_STRUCT &&
- clsHnd)
+ if (!isLoadAddress && (fieldInfo.fieldFlags & CORINFO_FLG_FIELD_STATIC) && (lclTyp == TYP_STRUCT))
{
if ((info.compCompHnd->getTypeForPrimitiveValueClass(clsHnd) == CORINFO_TYPE_UNDEF) &&
!(info.compFlags & CORINFO_FLG_FORCEINLINE))
}
}
- tiRetVal = verMakeTypeInfo(ciType, clsHnd);
+ tiRetVal = verMakeTypeInfo(fieldInfo.fieldType, clsHnd);
if (isLoadAddress)
{
tiRetVal.MakeByRef();
obj = nullptr;
}
- /* Preserve 'small' int types */
- if (!varTypeIsSmall(lclTyp))
- {
- lclTyp = genActualType(lclTyp);
- }
-
bool usesHelper = false;
switch (fieldInfo.fieldAccessor)
if (!isLoadAddress)
{
- if (varTypeIsStruct(lclTyp))
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(fieldInfo.fieldType, fieldInfo.structType, &layout);
+ if (lclTyp == TYP_STRUCT)
{
- op1 = gtNewBlkIndir(typGetObjLayout(fieldInfo.structType), op1, GTF_IND_NONFAULTING);
+ op1 = gtNewBlkIndir(layout, op1, GTF_IND_NONFAULTING);
}
else
{
case CORINFO_FIELD_INSTANCE_HELPER:
case CORINFO_FIELD_INSTANCE_ADDR_HELPER:
op1 = gtNewRefCOMfield(obj, &resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo, lclTyp,
- clsHnd, nullptr);
+ nullptr);
usesHelper = true;
break;
bool isStoreStatic = (opcode == CEE_STSFLD);
- CORINFO_CLASS_HANDLE fieldClsHnd; // class of the field (if it's a ref type)
-
/* Get the CP_Fieldref index */
assertImp(sz == sizeof(unsigned));
eeGetFieldInfo(&resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo);
- // Figure out the type of the member. We always call canAccessField, so you always need this
- // handle
- CorInfoType ciType = fieldInfo.fieldType;
- fieldClsHnd = fieldInfo.structType;
-
- lclTyp = JITtype2varType(ciType);
+ lclTyp = JITtype2varType(fieldInfo.fieldType);
if (compIsForInlining())
{
case CORINFO_FIELD_INSTANCE_ADDR_HELPER:
case CORINFO_FIELD_STATIC_ADDR_HELPER:
case CORINFO_FIELD_STATIC_TLS:
-
compInlineResult->NoteFatal(InlineObservation::CALLEE_STFLD_NEEDS_HELPER);
return;
obj = nullptr;
}
- /* Preserve 'small' int types */
- if (!varTypeIsSmall(lclTyp))
- {
- lclTyp = genActualType(lclTyp);
- }
-
switch (fieldInfo.fieldAccessor)
{
case CORINFO_FIELD_INSTANCE:
op1 = gtNewFieldAddrNode(TYP_I_IMPL, resolvedToken.hField, nullptr, fieldInfo.offset);
op1->gtFlags |= GTF_FLD_TLS; // fgMorphExpandTlsField will handle the transformation.
- if (varTypeIsStruct(lclTyp))
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(fieldInfo.fieldType, fieldInfo.structType, &layout);
+ if (lclTyp == TYP_STRUCT)
{
- op1 = gtNewBlkIndir(typGetObjLayout(fieldInfo.structType), op1, GTF_IND_NONFAULTING);
+ op1 = gtNewBlkIndir(layout, op1, GTF_IND_NONFAULTING);
}
else
{
case CORINFO_FIELD_INSTANCE_HELPER:
case CORINFO_FIELD_INSTANCE_ADDR_HELPER:
op1 = gtNewRefCOMfield(obj, &resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo, lclTyp,
- clsHnd, op2);
+ op2);
goto SPILL_APPEND;
case CORINFO_FIELD_STATIC_TLS_MANAGED:
assert(!"Unexpected fieldAccessor");
}
- if (lclTyp == TYP_STRUCT)
+ if (varTypeIsStruct(lclTyp))
{
op1 = impAssignStruct(op1, op2, CHECK_SPILL_ALL);
}
GenTree* boxPayloadOffset = gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL);
GenTree* boxPayloadAddress = gtNewOperNode(GT_ADD, TYP_BYREF, op1, boxPayloadOffset);
impPushOnStack(boxPayloadAddress, tiRetVal);
- oper = GT_BLK;
goto OBJ;
}
else
{
// Normal unbox helper returns a TYP_BYREF.
impPushOnStack(op1, tiRetVal);
- oper = GT_BLK;
goto OBJ;
}
// Make sure the right type is placed on the operand type stack.
impPushOnStack(op1, tiRetVal);
- // Load the struct.
- oper = GT_BLK;
-
assert(op1->gtType == TYP_BYREF);
-
goto OBJ;
}
else
JITDUMP(" %08X", resolvedToken.token);
- lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(resolvedToken.hClass));
-
- ClassLayout* layout = nullptr;
-
- if (lclTyp == TYP_STRUCT)
- {
- layout = typGetObjLayout(resolvedToken.hClass);
- lclTyp = layout->GetType();
- }
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(resolvedToken.hClass, &layout);
if (lclTyp != TYP_STRUCT)
{
- op2 = gtNewZeroConNode(genActualType(lclTyp));
+ op2 = gtNewZeroConNode(lclTyp);
goto STIND_VALUE;
}
op1 = impPopStack().val;
- op1 = gtNewStructVal(layout, op1);
+ op1 = gtNewLoadValueNode(layout, op1);
op2 = gtNewIconNode(0);
op1 = gtNewBlkOpNode(op1, op2, (prefixFlags & PREFIX_VOLATILE) != 0);
goto SPILL_APPEND;
}
ClassLayout* layout = typGetBlkLayout(static_cast<unsigned>(op3->AsIntConCommon()->IconValue()));
- op1 = gtNewStructVal(layout, op1, indirFlags);
- op2 = opcode == CEE_INITBLK ? op2 : gtNewStructVal(layout, op2, indirFlags);
+ op1 = gtNewLoadValueNode(layout, op1, indirFlags);
+ op2 = opcode == CEE_INITBLK ? op2 : gtNewLoadValueNode(layout, op2, indirFlags);
op1 = gtNewBlkOpNode(op1, op2, (indirFlags & GTF_IND_VOLATILE) != 0);
}
else
JITDUMP(" %08X", resolvedToken.token);
- lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(resolvedToken.hClass));
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(resolvedToken.hClass, &layout);
if (lclTyp != TYP_STRUCT)
{
- op1 = impPopStack().val; // address to load from
-
- op1 = gtNewIndir(lclTyp, op1);
- op1->gtFlags |= GTF_GLOB_REF;
-
- impPushOnStack(op1, typeInfo());
- goto STIND;
+ op2 = impPopStack().val; // address to load from
+ op2 = gtNewIndir(lclTyp, op2);
+ op2->gtFlags |= GTF_GLOB_REF;
+ goto STIND_VALUE;
}
op2 = impPopStack().val; // Src addr
op1 = impPopStack().val; // Dest addr
- ClassLayout* layout = typGetObjLayout(resolvedToken.hClass);
- op1 = gtNewStructVal(layout, op1);
- op2 = gtNewStructVal(layout, op2);
- op1 = gtNewBlkOpNode(op1, op2, ((prefixFlags & PREFIX_VOLATILE) != 0));
+ op1 = gtNewLoadValueNode(layout, op1);
+ op2 = gtNewLoadValueNode(layout, op2);
+ op1 = gtNewBlkOpNode(op1, op2, ((prefixFlags & PREFIX_VOLATILE) != 0));
goto SPILL_APPEND;
}
JITDUMP(" %08X", resolvedToken.token);
- lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(resolvedToken.hClass));
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(resolvedToken.hClass, &layout);
- if (lclTyp != TYP_STRUCT)
+ if (!varTypeIsStruct(lclTyp))
{
goto STIND;
}
- GenTreeFlags indirFlags = impPrefixFlagsToIndirFlags(prefixFlags);
- op2 = impPopStack().val; // Value
- op1 = impPopStack().val; // Ptr
-
+ op2 = impPopStack().val; // Value
+ op1 = impPopStack().val; // Ptr
assertImp(varTypeIsStruct(op2));
- op1 = gtNewStructVal(typGetObjLayout(resolvedToken.hClass), op1, indirFlags);
+
+ op1 = gtNewLoadValueNode(layout, op1, impPrefixFlagsToIndirFlags(prefixFlags));
op1 = impAssignStruct(op1, op2, CHECK_SPILL_ALL);
goto SPILL_APPEND;
}
case CEE_LDOBJ:
{
- oper = GT_BLK;
assertImp(sz == sizeof(unsigned));
_impResolveToken(CORINFO_TOKENKIND_Class);
JITDUMP(" %08X", resolvedToken.token);
OBJ:
- lclTyp = JITtype2varType(info.compCompHnd->asCorInfoType(resolvedToken.hClass));
+ ClassLayout* layout;
+ lclTyp = TypeHandleToVarType(resolvedToken.hClass, &layout);
tiRetVal = verMakeTypeInfo(resolvedToken.hClass);
- if (lclTyp != TYP_STRUCT)
- {
- goto LDIND;
- }
-
- GenTreeFlags indirFlags = impPrefixFlagsToIndirFlags(prefixFlags);
- op1 = impPopStack().val;
-
+ op1 = impPopStack().val;
assertImp((genActualType(op1) == TYP_I_IMPL) || op1->TypeIs(TYP_BYREF));
- op1 = gtNewBlkIndir(typGetObjLayout(resolvedToken.hClass), op1, indirFlags);
- op1->gtFlags |= GTF_EXCEPT; // TODO-1stClassStructs-Cleanup: delete this zero-diff quirk.
-
+ op1 = gtNewLoadValueNode(lclTyp, layout, op1, impPrefixFlagsToIndirFlags(prefixFlags));
impPushOnStack(op1, tiRetVal);
break;
}
ClassLayout* blkLayout = typGetBlkLayout(blkSize);
GenTree* dstAddr = gtNewOperNode(GT_ADD, TYP_BYREF, arrayLocalNode, gtNewIconNode(dataOffset, TYP_I_IMPL));
- GenTree* dst = gtNewStructVal(blkLayout, dstAddr);
+ GenTree* dst = gtNewLoadValueNode(blkLayout, dstAddr);
dst->gtFlags |= GTF_GLOB_REF;
GenTree* srcAddr = gtNewIconHandleNode((size_t)initData, GTF_ICON_CONST_PTR);
- GenTree* src = gtNewStructVal(blkLayout, srcAddr);
+ GenTree* src = gtNewLoadValueNode(blkLayout, srcAddr);
#ifdef DEBUG
src->gtGetOp1()->AsIntCon()->gtTargetHandle = THT_InitializeArrayIntrinsics;
GenTree* obj = thisPtr;
assert(obj->TypeGet() == TYP_BYREF || obj->TypeGet() == TYP_I_IMPL);
- obj = gtNewBlkIndir(typGetObjLayout(pConstrainedResolvedToken->hClass), obj);
-
- CorInfoType jitTyp = info.compCompHnd->asCorInfoType(pConstrainedResolvedToken->hClass);
- if (impIsPrimitive(jitTyp))
- {
- if (obj->OperIsBlk())
- {
- obj->ChangeOperUnchecked(GT_IND);
- obj->AsOp()->gtOp2 = nullptr; // must be zero for tree walkers
- }
-
- obj->gtType = JITtype2varType(jitTyp);
- assert(varTypeIsArithmetic(obj->gtType));
- }
+ ClassLayout* layout;
+ var_types objType = TypeHandleToVarType(pConstrainedResolvedToken->hClass, &layout);
+ obj = (objType == TYP_STRUCT) ? gtNewBlkIndir(layout, obj) : gtNewIndir(objType, obj);
// This pushes on the dereferenced byref
// This is then used immediately to box.
return nullptr;
}
- CORINFO_CLASS_HANDLE arrElemClsHnd = nullptr;
- var_types elemType = JITtype2varType(info.compCompHnd->getChildType(clsHnd, &arrElemClsHnd));
+ CORINFO_CLASS_HANDLE elemClsHnd = NO_CLASS_HANDLE;
+ CorInfoType elemJitType = info.compCompHnd->getChildType(clsHnd, &elemClsHnd);
+ ClassLayout* elemLayout = nullptr;
+ var_types elemType = TypeHandleToVarType(elemJitType, elemClsHnd, &elemLayout);
// For the ref case, we will only be able to inline if the types match
// (verifier checks for this, we don't care for the nonverified case and the
}
}
- unsigned arrayElemSize;
- if (elemType == TYP_STRUCT)
- {
- assert(arrElemClsHnd);
- arrayElemSize = info.compCompHnd->getClassSize(arrElemClsHnd);
- }
- else
- {
- arrayElemSize = genTypeSize(elemType);
- }
+ unsigned arrayElemSize = (elemType == TYP_STRUCT) ? elemLayout->GetSize() : genTypeSize(elemType);
- if ((unsigned char)arrayElemSize != arrayElemSize)
+ if (!FitsIn<unsigned char>(arrayElemSize))
{
// arrayElemSize would be truncated as an unsigned char.
// This means the array element is too large. Don't do the optimization.
{
// Assignment of a struct is more work, and there are more gets than sets.
// TODO-CQ: support SET (`a[i,j,k] = s`) for struct element arrays.
- if (elemType == TYP_STRUCT)
+ if (varTypeIsStruct(elemType))
{
JITDUMP("impArrayAccessIntrinsic: rejecting SET array intrinsic because elemType is TYP_STRUCT"
" (implementation limitation)\n",
if (intrinsicName != NI_Array_Address)
{
- if (varTypeIsStruct(elemType))
+ if (elemType == TYP_STRUCT)
{
- arrElem = gtNewBlkIndir(typGetObjLayout(sig->retTypeClass), arrElem);
+ arrElem = gtNewBlkIndir(elemLayout, arrElem);
}
else
{
+ // TODO-Bug: GTF_GLOB_REF is missing.
arrElem = gtNewOperNode(GT_IND, elemType, arrElem);
}
}
GenTree* argNode;
if (lclNode->TypeIs(TYP_STRUCT))
{
- argNode = gtNewStructVal(lclNode->GetLayout(this), argAddr);
+ argNode = gtNewLoadValueNode(lclNode->GetLayout(this), argAddr);
}
else
{
{
if (argNodeType == TYP_STRUCT)
{
- newArgNode = gtNewStructVal(argNodeLayout, addrNode);
+ newArgNode = gtNewLoadValueNode(argNodeLayout, addrNode);
}
else
{
// Only do this optimization when we are in the global optimizer. Doing this after value numbering
// could result in an invalid value number for the newly generated GT_IND node.
// We skip INDs with GTF_DONT_CSE which is set if the IND is a location.
- if (op1->OperIs(GT_COMMA) && fgGlobalMorph && ((tree->gtFlags & GTF_DONT_CSE) == 0))
+ if (!varTypeIsSIMD(tree) && op1->OperIs(GT_COMMA) && fgGlobalMorph && ((tree->gtFlags & GTF_DONT_CSE) == 0))
{
// Perform the transform IND(COMMA(x, ..., z)) -> COMMA(x, ..., IND(z)).
GenTree* commaNode = op1;
{
assert(ClassLayout::AreCompatible(m_blockLayout, m_src->GetLayout(m_comp)));
}
- // TODO-1stClassStructs: produce simple "IND<simd>" nodes in importer.
- else if (m_src->OperIsBlk())
- {
- m_src->SetOper(GT_IND);
- }
}
// TrySpecialCases: check special cases that require special transformations.
if ((size != 0) && FitsIn<int32_t>(size))
{
ClassLayout* layout = typGetBlkLayout(static_cast<unsigned>(size));
- GenTree* dst = gtNewStructVal(layout, tree->Addr(), tree->gtFlags & GTF_IND_FLAGS);
+ GenTree* dst = gtNewLoadValueNode(layout, tree->Addr(), tree->gtFlags & GTF_IND_FLAGS);
dst->gtFlags |= GTF_GLOB_REF;
GenTree* src = tree->Data();
RewriteAssignment(use);
break;
- case GT_BLK:
- if (varTypeIsSIMD(node))
- {
- node->SetOper(GT_IND);
- }
- break;
-
case GT_CALL:
// In linear order we no longer need to retain the stores in early
// args as these have now been sequenced.
tree = gtNewOperNode(GT_IND, type, tree);
}
+ // TODO-ADDR: delete this.
if (tree->OperIsIndir() && tree->AsIndir()->Addr()->IsLclVarAddr())
{
GenTreeLclFld* lclAddr = tree->AsIndir()->Addr()->AsLclFld();
if (copyBlkDst != nullptr)
{
assert(copyBlkSrc != nullptr);
-
- // At this point, we have a tree that we are going to store into a destination.
- // TODO-1stClassStructs: This should be a simple store or assignment, and should not require
- // GTF_ALL_EFFECT for the dest. This is currently emulating the previous behavior of
- // block ops.
-
- GenTree* dest = gtNewStructVal(typGetBlkLayout(simdSize), copyBlkDst);
-
- dest->gtType = simdType;
+ GenTree* dest = gtNewLoadValueNode(simdType, copyBlkDst);
dest->gtFlags |= GTF_GLOB_REF;
GenTree* retNode = gtNewBlkOpNode(dest, copyBlkSrc);