}
//------------------------------------------------------------------------
-// fgMorphInitBlock: Perform the Morphing of a GT_INITBLK node
+// fgMorphInitBlock: Morph a block initialization assignment tree.
//
// Arguments:
-// tree - a tree node with a gtOper of GT_INITBLK
-// the child nodes for tree have already been Morphed
+// tree - A GT_ASG tree that performs block initialization
//
// Return Value:
-// We can return the orginal GT_INITBLK unmodified (least desirable, but always correct)
-// We can return a single assignment, when fgMorphOneAsgBlockOp transforms it (most desirable)
-// If we have performed struct promotion of the Dest() then we will try to
-// perform a field by field assignment for each of the promoted struct fields
+// A single assignment, when fgMorphOneAsgBlockOp transforms it.
+//
+// If the destination is a promoted struct local variable then we will try to
+// perform a field by field assignment for each of the promoted struct fields.
+// This is not always possible (e.g. if the struct has holes and custom layout).
+//
+// Otherwise the orginal GT_ASG tree is returned unmodified (always correct but
+// least desirable because it prevents enregistration and/or blocks independent
+// struct promotion).
+//
+// Assumptions:
+// GT_ASG's children have already been morphed.
//
-// Notes:
-// If we leave it as a GT_INITBLK we will call lvaSetVarDoNotEnregister() with a reason of DNER_BlockOp
-// if the Dest() is a a struct that has a "CustomLayout" and "ConstainsHoles" then we
-// can not use a field by field assignment and must the orginal GT_INITBLK unmodified.
-
GenTree* Compiler::fgMorphInitBlock(GenTree* tree)
{
// We must have the GT_ASG form of InitBlkOp.
bool morphed = false;
#endif // DEBUG
- GenTree* asg = tree;
GenTree* src = tree->gtGetOp2();
GenTree* origDest = tree->gtGetOp1();
}
else
{
- GenTree* destAddr = nullptr;
- GenTree* initVal = src->OperIsInitVal() ? src->gtGetOp1() : src;
- GenTree* blockSize = nullptr;
- unsigned blockWidth = 0;
- FieldSeqNode* destFldSeq = nullptr;
- LclVarDsc* destLclVar = nullptr;
- bool destDoFldAsg = false;
- unsigned destLclNum = BAD_VAR_NUM;
- bool blockWidthIsConst = false;
- GenTreeLclVarCommon* lclVarTree = nullptr;
+ GenTreeLclVarCommon* destLclNode = nullptr;
+ unsigned destLclNum = BAD_VAR_NUM;
+ LclVarDsc* destLclVar = nullptr;
+ GenTree* initVal = src->OperIsInitVal() ? src->gtGetOp1() : src;
+ unsigned blockSize = 0;
+
if (dest->IsLocal())
{
- lclVarTree = dest->AsLclVarCommon();
+ destLclNode = dest->AsLclVarCommon();
+ destLclNum = destLclNode->GetLclNum();
+ destLclVar = lvaGetDesc(destLclNum);
+ blockSize = varTypeIsStruct(destLclVar) ? destLclVar->lvExactSize : genTypeSize(destLclVar->TypeGet());
}
else
{
- if (dest->OperIsBlk())
+ if (dest->OperIs(GT_IND))
{
- destAddr = dest->AsBlk()->Addr();
- blockWidth = dest->AsBlk()->gtBlkSize;
+ assert(dest->TypeGet() == TYP_STRUCT);
+ blockSize = genTypeSize(dest->TypeGet());
}
- else
+ else if (dest->OperIs(GT_DYN_BLK))
{
- assert((dest->gtOper == GT_IND) && (dest->TypeGet() != TYP_STRUCT));
- destAddr = dest->gtGetOp1();
- blockWidth = genTypeSize(dest->TypeGet());
- }
- }
- if (lclVarTree != nullptr)
- {
- destLclNum = lclVarTree->gtLclNum;
- destLclVar = &lvaTable[destLclNum];
- blockWidth = varTypeIsStruct(destLclVar) ? destLclVar->lvExactSize : genTypeSize(destLclVar);
- blockWidthIsConst = true;
- }
- else
- {
- if (dest->gtOper == GT_DYN_BLK)
- {
- // The size must be an integer type
- blockSize = dest->AsBlk()->gtDynBlk.gtDynamicSize;
- assert(varTypeIsIntegral(blockSize->gtType));
+ blockSize = 0;
}
else
{
- assert(blockWidth != 0);
- blockWidthIsConst = true;
+ assert(dest->OperIs(GT_BLK, GT_OBJ));
+ blockSize = dest->AsBlk()->Size();
+ assert(blockSize != 0);
}
- if ((destAddr != nullptr) && destAddr->IsLocalAddrExpr(this, &lclVarTree, &destFldSeq))
+ FieldSeqNode* destFldSeq = nullptr;
+ if (dest->AsIndir()->Addr()->IsLocalAddrExpr(this, &destLclNode, &destFldSeq))
{
- destLclNum = lclVarTree->gtLclNum;
- destLclVar = &lvaTable[destLclNum];
+ destLclNum = destLclNode->GetLclNum();
+ destLclVar = lvaGetDesc(destLclNum);
}
}
+
+ bool destDoFldAsg = false;
+
if (destLclNum != BAD_VAR_NUM)
{
#if LOCAL_ASSERTION_PROP
// Kill everything about destLclNum (and its field locals)
- if (optLocalAssertionProp)
+ if (optLocalAssertionProp && (optAssertionCount > 0))
{
- if (optAssertionCount > 0)
- {
- fgKillDependentAssertions(destLclNum DEBUGARG(tree));
- }
+ fgKillDependentAssertions(destLclNum DEBUGARG(tree));
}
#endif // LOCAL_ASSERTION_PROP
- if (destLclVar->lvPromoted && blockWidthIsConst)
+ if (destLclVar->lvPromoted)
{
- assert(initVal->OperGet() == GT_CNS_INT);
- noway_assert(varTypeIsStruct(destLclVar));
- noway_assert(!opts.MinOpts());
- if (destLclVar->lvAddrExposed & destLclVar->lvContainsHoles)
- {
- JITDUMP(" dest is address exposed");
- }
- else
+ GenTree* newTree = fgMorphPromoteLocalInitBlock(destLclNode->AsLclVar(), initVal, blockSize);
+
+ if (newTree != nullptr)
{
- if (blockWidth == destLclVar->lvExactSize)
- {
- JITDUMP(" (destDoFldAsg=true)");
- // We may decide later that a copyblk is required when this struct has holes
- destDoFldAsg = true;
- }
- else
- {
- JITDUMP(" with mismatched size");
- }
+ tree = newTree;
+ destDoFldAsg = true;
+ INDEBUG(morphed = true);
}
}
- }
- // Can we use field by field assignment for the dest?
- if (destDoFldAsg && destLclVar->lvCustomLayout && destLclVar->lvContainsHoles)
- {
- JITDUMP(" dest contains holes");
- destDoFldAsg = false;
- }
-
- JITDUMP(destDoFldAsg ? " using field by field initialization.\n" : " this requires an InitBlock.\n");
-
- // If we're doing an InitBlock and we've transformed the dest to a non-Blk
- // we need to change it back.
- if (!destDoFldAsg && !dest->OperIsBlk())
- {
- noway_assert(blockWidth != 0);
- tree->gtOp.gtOp1 = origDest;
- tree->gtType = origDest->gtType;
- }
-
- if (!destDoFldAsg && (destLclVar != nullptr))
- {
// If destLclVar is not a reg-sized non-field-addressed struct, set it as DoNotEnregister.
- if (!destLclVar->lvRegStruct)
+ if (!destDoFldAsg && !destLclVar->lvRegStruct)
{
- // Mark it as DoNotEnregister.
lvaSetVarDoNotEnregister(destLclNum DEBUGARG(DNER_BlockOp));
}
}
- // Mark the dest struct as DoNotEnreg
- // when they are LclVar structs and we are using a CopyBlock
- // or the struct is not promoted
- //
if (!destDoFldAsg)
{
- dest = fgMorphBlockOperand(dest, dest->TypeGet(), blockWidth, true);
+ // If we're doing an InitBlock and we've transformed the dest to a non-Blk
+ // we need to change it back.
+ if (!dest->OperIsBlk())
+ {
+ noway_assert(blockSize != 0);
+ tree->gtOp.gtOp1 = origDest;
+ tree->gtType = origDest->gtType;
+ }
+
+ dest = fgMorphBlockOperand(dest, dest->TypeGet(), blockSize, true);
tree->gtOp.gtOp1 = dest;
tree->gtFlags |= (dest->gtFlags & GTF_ALL_EFFECT);
}
- else
- {
- // The initVal must be a constant of TYP_INT
- noway_assert(initVal->OperGet() == GT_CNS_INT);
- noway_assert(genActualType(initVal->gtType) == TYP_INT);
+ }
- // The dest must be of a struct type.
- noway_assert(varTypeIsStruct(destLclVar));
+#ifdef DEBUG
+ if (morphed)
+ {
+ tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
- //
- // Now, convert InitBlock to individual assignments
- //
+ if (verbose)
+ {
+ printf("fgMorphInitBlock (after):\n");
+ gtDispTree(tree);
+ }
+ }
+#endif
+
+ return tree;
+}
- tree = nullptr;
- INDEBUG(morphed = true);
+//------------------------------------------------------------------------
+// fgMorphPromoteLocalInitBlock: Attempts to promote a local block init tree
+// to a tree of promoted field initialization assignments.
+//
+// Arguments:
+// destLclNode - The destination LclVar node
+// initVal - The initialization value
+// blockSize - The amount of bytes to initialize
+//
+// Return Value:
+// A tree that performs field by field initialization of the destination
+// struct variable if various conditions are met, nullptr otherwise.
+//
+// Notes:
+// This transforms a single block initialization assignment like:
+//
+// * ASG struct (init)
+// +--* BLK(12) struct
+// | \--* ADDR long
+// | \--* LCL_VAR struct(P) V02 loc0
+// | \--* int V02.a (offs=0x00) -> V06 tmp3
+// | \--* ubyte V02.c (offs=0x04) -> V07 tmp4
+// | \--* float V02.d (offs=0x08) -> V08 tmp5
+// \--* INIT_VAL int
+// \--* CNS_INT int 42
+//
+// into a COMMA tree of assignments that initialize each promoted struct
+// field:
+//
+// * COMMA void
+// +--* COMMA void
+// | +--* ASG int
+// | | +--* LCL_VAR int V06 tmp3
+// | | \--* CNS_INT int 0x2A2A2A2A
+// | \--* ASG ubyte
+// | +--* LCL_VAR ubyte V07 tmp4
+// | \--* CNS_INT int 42
+// \--* ASG float
+// +--* LCL_VAR float V08 tmp5
+// \--* CNS_DBL float 1.5113661732714390e-13
+//
+GenTree* Compiler::fgMorphPromoteLocalInitBlock(GenTreeLclVar* destLclNode, GenTree* initVal, unsigned blockSize)
+{
+ assert(destLclNode->OperIs(GT_LCL_VAR));
- GenTree* dest;
- GenTree* srcCopy;
- unsigned fieldLclNum;
- unsigned fieldCnt = destLclVar->lvFieldCnt;
+ LclVarDsc* destLclVar = lvaGetDesc(destLclNode);
+ assert(varTypeIsStruct(destLclVar->TypeGet()));
+ assert(destLclVar->lvPromoted);
- for (unsigned i = 0; i < fieldCnt; ++i)
- {
- fieldLclNum = destLclVar->lvFieldLclStart + i;
- dest = gtNewLclvNode(fieldLclNum, lvaTable[fieldLclNum].TypeGet());
+ if (blockSize == 0)
+ {
+ JITDUMP(" size is not known.\n");
+ return nullptr;
+ }
- noway_assert(lclVarTree->gtOper == GT_LCL_VAR);
- // If it had been labeled a "USEASG", assignments to the the individual promoted fields are not.
- dest->gtFlags |= (lclVarTree->gtFlags & ~(GTF_NODE_MASK | GTF_VAR_USEASG));
+ if (destLclVar->lvAddrExposed && destLclVar->lvContainsHoles)
+ {
+ JITDUMP(" dest is address exposed and contains holes.\n");
+ return nullptr;
+ }
- srcCopy = gtCloneExpr(initVal);
- noway_assert(srcCopy != nullptr);
+ if (destLclVar->lvCustomLayout && destLclVar->lvContainsHoles)
+ {
+ JITDUMP(" dest has custom layout and contains holes.\n");
+ return nullptr;
+ }
- // need type of oper to be same as tree
- if (dest->gtType == TYP_LONG)
- {
- srcCopy->ChangeOperConst(GT_CNS_NATIVELONG);
- // copy and extend the value
- srcCopy->gtIntConCommon.SetLngValue(initVal->gtIntConCommon.IconValue());
- /* Change the types of srcCopy to TYP_LONG */
- srcCopy->gtType = TYP_LONG;
- }
- else if (varTypeIsFloating(dest->gtType))
- {
- srcCopy->ChangeOperConst(GT_CNS_DBL);
- // setup the bit pattern
- memset(&srcCopy->gtDblCon.gtDconVal, (int)initVal->gtIntCon.gtIconVal,
- sizeof(srcCopy->gtDblCon.gtDconVal));
- /* Change the types of srcCopy to TYP_DOUBLE */
- srcCopy->gtType = TYP_DOUBLE;
- }
- else
- {
- noway_assert(srcCopy->gtOper == GT_CNS_INT);
- noway_assert(srcCopy->TypeGet() == TYP_INT);
- // setup the bit pattern
- memset(&srcCopy->gtIntCon.gtIconVal, (int)initVal->gtIntCon.gtIconVal,
- sizeof(srcCopy->gtIntCon.gtIconVal));
- }
+ if (destLclVar->lvExactSize != blockSize)
+ {
+ JITDUMP(" dest size mismatch.\n");
+ return nullptr;
+ }
- srcCopy->gtType = dest->TypeGet();
+ if (!initVal->OperIs(GT_CNS_INT))
+ {
+ JITDUMP(" source is not constant.\n");
+ return nullptr;
+ }
- asg = gtNewAssignNode(dest, srcCopy);
+ const int64_t initPattern = (initVal->AsIntCon()->IconValue() & 0xFF) * 0x0101010101010101LL;
-#if LOCAL_ASSERTION_PROP
- if (optLocalAssertionProp)
- {
- optAssertionGen(asg);
- }
-#endif // LOCAL_ASSERTION_PROP
+ if (initPattern != 0)
+ {
+ for (unsigned i = 0; i < destLclVar->lvFieldCnt; ++i)
+ {
+ LclVarDsc* fieldDesc = lvaGetDesc(destLclVar->lvFieldLclStart + i);
- if (tree)
- {
- tree = gtNewOperNode(GT_COMMA, TYP_VOID, tree, asg);
- }
- else
- {
- tree = asg;
- }
+ if (varTypeIsSIMD(fieldDesc->TypeGet()) || varTypeIsGC(fieldDesc->TypeGet()))
+ {
+ // Cannot initialize GC or SIMD types with a non-zero constant.
+ // The former is completly bogus. The later restriction could be
+ // lifted by supporting non-zero SIMD constants or by generating
+ // field initialization code that converts an integer constant to
+ // the appropiate SIMD value. Unlikely to be very useful, though.
+ JITDUMP(" dest contains GC and/or SIMD fields and source constant is not 0.\n");
+ return nullptr;
}
}
}
-#ifdef DEBUG
- if (morphed)
+ JITDUMP(" using field by field initialization.\n");
+
+ GenTree* tree = nullptr;
+
+ for (unsigned i = 0; i < destLclVar->lvFieldCnt; ++i)
{
- tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
+ unsigned fieldLclNum = destLclVar->lvFieldLclStart + i;
+ LclVarDsc* fieldDesc = lvaGetDesc(fieldLclNum);
+ GenTree* dest = gtNewLclvNode(fieldLclNum, fieldDesc->TypeGet());
+ // If it had been labeled a "USEASG", assignments to the the individual promoted fields are not.
+ dest->gtFlags |= (destLclNode->gtFlags & ~(GTF_NODE_MASK | GTF_VAR_USEASG));
- if (verbose)
+ GenTree* src;
+
+ switch (dest->TypeGet())
{
- printf("fgMorphInitBlock (after):\n");
- gtDispTree(tree);
+ case TYP_BOOL:
+ case TYP_BYTE:
+ case TYP_UBYTE:
+ case TYP_SHORT:
+ case TYP_USHORT:
+ // Promoted fields are expected to be "normalize on load". If that changes then
+ // we may need to adjust this code to widen the constant correctly.
+ assert(fieldDesc->lvNormalizeOnLoad());
+ __fallthrough;
+ case TYP_INT:
+ {
+ int64_t mask = (int64_t(1) << (genTypeSize(dest->TypeGet()) * 8)) - 1;
+ src = gtNewIconNode(static_cast<int32_t>(initPattern & mask));
+ break;
+ }
+ case TYP_LONG:
+ src = gtNewLconNode(initPattern);
+ break;
+ case TYP_FLOAT:
+ float floatPattern;
+ memcpy(&floatPattern, &initPattern, sizeof(floatPattern));
+ src = gtNewDconNode(floatPattern, dest->TypeGet());
+ break;
+ case TYP_DOUBLE:
+ double doublePattern;
+ memcpy(&doublePattern, &initPattern, sizeof(doublePattern));
+ src = gtNewDconNode(doublePattern, dest->TypeGet());
+ break;
+ case TYP_REF:
+ case TYP_BYREF:
+#ifdef FEATURE_SIMD
+ case TYP_SIMD8:
+ case TYP_SIMD12:
+ case TYP_SIMD16:
+ case TYP_SIMD32:
+#endif // FEATURE_SIMD
+ assert(initPattern == 0);
+ src = gtNewIconNode(0, dest->TypeGet());
+ break;
+ default:
+ unreached();
+ }
+
+ GenTree* asg = gtNewAssignNode(dest, src);
+
+#if LOCAL_ASSERTION_PROP
+ if (optLocalAssertionProp)
+ {
+ optAssertionGen(asg);
+ }
+#endif // LOCAL_ASSERTION_PROP
+
+ if (tree != nullptr)
+ {
+ tree = gtNewOperNode(GT_COMMA, TYP_VOID, tree, asg);
+ }
+ else
+ {
+ tree = asg;
}
}
-#endif
return tree;
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern System.Runtime { }
+.assembly GitHub_21761 { }
+
+// Some basic tests to ensure that the JIT handles non-zero
+// initialization correctly.
+
+.class sequential sealed Point extends [System.Runtime]System.ValueType
+{
+ .field public int32 X
+ .field public int32 Y
+}
+
+.method static bool ConstFixedSizeInitBlk() cil managed noinlining
+{
+ .locals init (valuetype Point a)
+
+ ldloca a
+ ldc.i4 42
+ sizeof Point
+ initblk
+
+ ldloca a
+ ldind.i4
+ ldc.i4 0x2a2a2a2a
+ ceq
+ ret
+}
+
+.method static bool NonConstFixedSizeInitBlk() cil managed noinlining
+{
+ .locals init (valuetype Point a, int32 s)
+
+ ldloca a
+ ldloc s
+ sizeof Point
+ initblk
+
+ ldloca a
+ ldfld int32 Point::X
+ ldc.i4 0
+ ceq
+ ret
+}
+
+.class sequential sealed Point64 extends [System.Runtime]System.ValueType
+{
+ .field public int64 X
+ .field public int64 Y
+}
+
+.method static bool ConstFixedSizeInitBlk64() cil managed noinlining
+{
+ .locals init (valuetype Point64 a)
+
+ ldloca a
+ ldc.i4 42
+ sizeof Point64
+ initblk
+
+ ldloca a
+ ldind.i8
+ ldc.i8 0x2a2a2a2a2a2a2a2a
+ ceq
+ ret
+}
+
+.method static bool NonConstFixedSizeInitBlk64() cil managed noinlining
+{
+ .locals init (valuetype Point64 a, int32 s)
+
+ ldloca a
+ ldloc s
+ sizeof Point64
+ initblk
+
+ ldloca a
+ ldfld int64 Point64::X
+ ldc.i8 0
+ ceq
+ ret
+}
+
+// Small int promoted fields are supposed to be "normalize on load" so
+// no special care is needed when initializing them. Still, make sure
+// that field by field initialization handles small ints correctly.
+
+.class sequential sealed SmallInts extends [System.Runtime]System.ValueType
+{
+ .field public int8 I8;
+ .field public uint8 U8;
+ .field public int16 I16;
+}
+
+.method static bool SmallIntsInitBlk() cil managed noinlining
+{
+ .locals init (valuetype SmallInts a)
+
+ ldloca a
+ ldc.i4 42
+ sizeof SmallInts
+ initblk
+
+ ldloca a
+ ldind.i1
+ ldc.i4 0x2a
+ bne.un FAIL
+
+ ldloca a
+ ldind.u1
+ ldc.i4 0x2a
+ bne.un FAIL
+
+ ldloca a
+ ldind.i2
+ ldc.i4 0x2a2a
+ bne.un FAIL
+
+ ldc.i4 1
+ ret
+
+FAIL:
+ ldc.i4 0
+ ret
+}
+
+.method static bool SmallIntsSignedInitBlk() cil managed noinlining
+{
+ .locals init (valuetype SmallInts a)
+
+ ldloca a
+ ldc.i4 0x8a
+ sizeof SmallInts
+ initblk
+
+ ldloca a
+ ldind.i1
+ ldc.i4 0xFFFFFF8a
+ bne.un FAIL
+
+ ldloca a
+ ldind.u1
+ ldc.i4 0x8a
+ bne.un FAIL
+
+ ldloca a
+ ldind.i2
+ ldc.i4 0xFFFF8a8a
+ bne.un FAIL
+
+ ldc.i4 1
+ ret
+
+FAIL:
+ ldc.i4 0
+ ret
+}
+
+// If floating point fields are involved, special care is needed
+// since floating point constants having a required bit pattern
+// have to be generated.
+
+.class sequential sealed F32Vec3 extends [System.Runtime]System.ValueType
+{
+ .field public float32 X
+ .field public float32 Y
+ .field public float32 Z
+}
+
+// Make sure that the JIT produces an appropiate floating point constant.
+// JIT's internal representation uses doubles to store constants, even
+// when constants are float-typed. This means that the JIT should first
+// create a float value having the required bit pattern and then convert
+// it double. Directly creating a double having the required bit pattern
+// is not valid.
+
+.method static bool Float32InitBlk() cil managed noinlining
+{
+ .locals init (valuetype F32Vec3 a)
+
+ ldloca a
+ ldc.i4 42
+ sizeof F32Vec3
+ initblk
+
+ ldloca a
+ ldind.i4
+ ldc.i4 0x2a2a2a2a
+ ceq
+ ret
+}
+
+// Initializing a float value with 255, 255, 255... is a special case
+// because the result is a NaN. And since the JIT uses double to store
+// float constants, this means that the JIT may end up producing a float
+// NaN value, convert it to double and then convert it back to float
+// during codegen. Will the NaN payload be preserved through conversions?
+// This may depend on the host's floating point implementation.
+
+.method static bool Float32NaNInitBlk() cil managed noinlining
+{
+ .locals init (valuetype F32Vec3 a)
+
+ ldloca a
+ ldc.i4 255
+ sizeof F32Vec3
+ initblk
+
+ ldloca a
+ ldind.i4
+ ldc.i4 0xFFFFFFFF
+ ceq
+ ret
+}
+
+// Non-zero initialization of a GC reference is not exactly a valid scenario.
+// Still, the JIT shouldn't end up generating invalid IR (non-zero GC typed
+// constant nodes).
+
+.class sequential sealed Pair extends [System.Runtime]System.ValueType
+{
+ .field public int64 Key
+ .field public class [System.Runtime]System.Object Value
+}
+
+.method static bool ObjRefInitBlk() cil managed noinlining
+{
+ .locals init (valuetype Pair a)
+
+ ldloca a
+ ldc.i4 1
+ sizeof Pair
+ initblk
+
+ ldloca a
+ ldind.i8
+ ldc.i8 0x0101010101010101
+ ceq
+ ret
+}
+
+// Non-zero SIMD constants are not supported so field by field initialization
+// should not be attempted.
+
+.class sequential sealed Wrapper extends [System.Runtime]System.ValueType
+{
+ .field public valuetype [System.Numerics.Vectors]System.Numerics.Vector4 Value
+}
+
+.method static bool SimdInitBlk() cil managed noinlining
+{
+ .locals init (valuetype Wrapper a, valuetype [System.Numerics.Vectors]System.Numerics.Vector4 v, float32 len)
+
+ ldloca a
+ ldc.i4 42
+ ldc.i4 16
+ initblk
+
+ ldloca a
+ ldflda valuetype [System.Numerics.Vectors]System.Numerics.Vector4 Wrapper::Value
+ call instance float32 [System.Numerics.Vectors]System.Numerics.Vector4::Length()
+ stloc len
+
+ ldloca a
+ ldfld valuetype [System.Numerics.Vectors]System.Numerics.Vector4 Wrapper::Value
+ stloc v
+
+ ldloca v
+ ldind.i4
+ ldc.i4 0x2a2a2a2a
+ ceq
+ ret
+}
+
+.method hidebysig static int32 Main() cil managed
+{
+ .entrypoint
+ .locals init (valuetype Point a)
+
+ call bool ConstFixedSizeInitBlk()
+ brfalse FAIL
+
+ call bool NonConstFixedSizeInitBlk()
+ brfalse FAIL
+
+ call bool ConstFixedSizeInitBlk64()
+ brfalse FAIL
+
+ call bool NonConstFixedSizeInitBlk64()
+ brfalse FAIL
+
+ call bool SmallIntsInitBlk()
+ brfalse FAIL
+
+ call bool SmallIntsSignedInitBlk()
+ brfalse FAIL
+
+ call bool Float32InitBlk()
+ brfalse FAIL
+
+ call bool Float32NaNInitBlk()
+ brfalse FAIL
+
+ call bool ObjRefInitBlk()
+ brfalse FAIL
+
+ call bool SimdInitBlk()
+ brfalse FAIL
+
+ ldc.i4 100
+ ret
+ FAIL:
+ ldc.i4 1
+ ret
+}