From 1e54aa0d067f0bffef2427841fabed128798ee10 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 00:41:45 +0200 Subject: [PATCH 01/50] Intrinsify Unsafe.Read/Write, handle struct BitCast --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/gentree.cpp | 4 +- src/coreclr/jit/importer.cpp | 51 ++++++++------- src/coreclr/jit/importercalls.cpp | 77 ++++++++--------------- src/coreclr/jit/importervectorization.cpp | 2 +- 5 files changed, 54 insertions(+), 82 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 86588d1c74f11..6dfac35fd377e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4051,7 +4051,7 @@ class Compiler const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); - GenTree* impGetStructAddr(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, bool willDeref); + GenTree* impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, bool willDeref); var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoType* simdBaseJitType = nullptr); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 8bb778f9d326e..95191fd58df3e 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15937,7 +15937,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, // helper needs pointer to struct, not struct itself if (pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT) { - assg = impGetStructAddr(assg, structType, CHECK_SPILL_ALL, true); + assg = impGetNodeAddr(assg, structType, CHECK_SPILL_ALL, true); } else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT) { @@ -16013,7 +16013,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, if (!varTypeIsStruct(lclTyp)) { // get the result as primitive type - result = impGetStructAddr(result, structType, CHECK_SPILL_ALL, true); + result = impGetNodeAddr(result, structType, CHECK_SPILL_ALL, true); result = gtNewIndir(lclTyp, result); } } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 75f71b552d0a3..c75fb19dbc3c1 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -957,7 +957,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, WellKnownArg wellKnownArgType = srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; - GenTree* destAddr = impGetStructAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, /* willDeref */ true); + GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, /* willDeref */ true); NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); #if !defined(TARGET_ARM) @@ -1059,7 +1059,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, if (call->ShouldHaveRetBufArg()) { // insert the return value buffer into the argument list as first byref parameter after 'this' - GenTree* destAddr = impGetStructAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, /* willDeref */ true); + GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, /* willDeref */ true); call->gtArgs.InsertAfterThisOrFirst(this, NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer)); @@ -1076,7 +1076,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // Since we are assigning the result of a GT_MKREFANY, "destAddr" must point to a refany. // TODO-CQ: we can do this without address-exposing the local on the LHS. - GenTree* destAddr = impGetStructAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, /* willDeref */ true); + GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, /* willDeref */ true); GenTree* destAddrClone; destAddr = impCloneExpr(destAddr, &destAddrClone, NO_CLASS_HANDLE, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); @@ -1182,44 +1182,43 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, } //------------------------------------------------------------------------ -// impGetStructAddr: Get the address of a struct value. +// impGetNodeAddr: Get the address of a value. // // Arguments: -// structVal - The value in question -// structHnd - The struct handle for "structVal" +// val - The value in question +// typeHnd - The type handle for "val" // curLevel - Stack level for spilling // willDeref - Whether the caller will dereference the address // // Return Value: -// In case "structVal" can represent locations (is an indirection/local), +// In case "val" can represent locations (is an indirection/local), // will return its address. Otherwise, address of a temporary assigned -// the value of "structVal" will be returned. +// the value of "val" will be returned. // -GenTree* Compiler::impGetStructAddr(GenTree* structVal, - CORINFO_CLASS_HANDLE structHnd, - unsigned curLevel, - bool willDeref) +GenTree* Compiler::impGetNodeAddr(GenTree* val, + CORINFO_CLASS_HANDLE typeHnd, + unsigned curLevel, + bool willDeref) { - assert(varTypeIsStruct(structVal)); - switch (structVal->OperGet()) + switch (val->OperGet()) { case GT_BLK: case GT_IND: if (willDeref) { - return structVal->AsIndir()->Addr(); + return val->AsIndir()->Addr(); } break; case GT_LCL_VAR: - return gtNewLclVarAddrNode(structVal->AsLclVar()->GetLclNum(), TYP_BYREF); + return gtNewLclVarAddrNode(val->AsLclVar()->GetLclNum(), TYP_BYREF); case GT_LCL_FLD: - return gtNewLclAddrNode(structVal->AsLclFld()->GetLclNum(), structVal->AsLclFld()->GetLclOffs(), TYP_BYREF); + return gtNewLclAddrNode(val->AsLclFld()->GetLclNum(), val->AsLclFld()->GetLclOffs(), TYP_BYREF); case GT_FIELD: { - GenTreeField* fieldNode = structVal->AsField(); + GenTreeField* fieldNode = val->AsField(); GenTreeField* fieldAddr = new (this, GT_FIELD_ADDR) GenTreeField(GT_FIELD_ADDR, TYP_BYREF, fieldNode->GetFldObj(), fieldNode->gtFldHnd, fieldNode->gtFldOffset); @@ -1231,15 +1230,15 @@ GenTree* Compiler::impGetStructAddr(GenTree* structVal, } case GT_COMMA: - impAppendTree(structVal->AsOp()->gtGetOp1(), curLevel, impCurStmtDI); - return impGetStructAddr(structVal->AsOp()->gtGetOp2(), structHnd, curLevel, willDeref); + impAppendTree(val->AsOp()->gtGetOp1(), curLevel, impCurStmtDI); + return impGetStructAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, willDeref); default: break; } unsigned lclNum = lvaGrabTemp(true DEBUGARG("location for address-of(RValue)")); - impAssignTempGen(lclNum, structVal, structHnd, curLevel); + impAssignTempGen(lclNum, val, typeHnd, curLevel); // The 'return value' is now address of the temp itself. return gtNewLclVarAddrNode(lclNum, TYP_BYREF); @@ -3272,7 +3271,7 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, GenTree* objToBox = impPopStack().val; // Spill struct to get its address (to access hasValue field) - objToBox = impGetStructAddr(objToBox, nullableCls, CHECK_SPILL_ALL, true); + objToBox = impGetNodeAddr(objToBox, nullableCls, CHECK_SPILL_ALL, true); impPushOnStack(gtNewFieldRef(TYP_BOOL, hasValueFldHnd, objToBox, 0), typeInfo(TI_INT)); @@ -3625,7 +3624,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) return; } - op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetStructAddr(exprToBox, operCls, CHECK_SPILL_ALL, true)); + op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, operCls, CHECK_SPILL_ALL, true)); } /* Push the result back on the stack, */ @@ -8293,7 +8292,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) } else { - op1 = impGetStructAddr(op1, clsHnd, CHECK_SPILL_ALL, false); + op1 = impGetNodeAddr(op1, clsHnd, CHECK_SPILL_ALL, false); } JITDUMP("\n ... optimized to ...\n"); @@ -9256,7 +9255,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { BADCODE("top of stack must be a value type"); } - obj = impGetStructAddr(obj, objType, CHECK_SPILL_ALL, true); + obj = impGetNodeAddr(obj, objType, CHECK_SPILL_ALL, true); } if (isLoadAddress) @@ -10012,7 +10011,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) else { // Get the address of the refany - op1 = impGetStructAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, /* willDeref */ true); + op1 = impGetNodeAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, /* willDeref */ true); // Fetch the type from the correct slot op1 = gtNewOperNode(GT_ADD, TYP_BYREF, op1, diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 1cd5cc5dd18ff..b2ebec8d109cf 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4037,40 +4037,22 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, return nullptr; } - CorInfoType fromJitType = info.compCompHnd->asCorInfoType(fromTypeHnd); - var_types fromType = JITtype2varType(fromJitType); + ClassLayout* fromLayout = nullptr; + var_types fromType = TypeHandleToVarType(fromTypeHnd, &fromLayout); - CorInfoType toJitType = info.compCompHnd->asCorInfoType(toTypeHnd); - var_types toType = JITtype2varType(toJitType); + ClassLayout* toLayout = nullptr; + var_types toType = TypeHandleToVarType(toTypeHnd, &toLayout); - bool involvesStructType = false; - - if (fromType == TYP_STRUCT) - { - involvesStructType = true; - - if (toType == TYP_STRUCT) - { - ClassLayout* fromLayout = typGetObjLayout(fromTypeHnd); - ClassLayout* toLayout = typGetObjLayout(toTypeHnd); - - if (ClassLayout::AreCompatible(fromLayout, toLayout)) - { - // Handle compatible struct layouts where we can simply return op1 - return impPopStack().val; - } - } - } - else if (toType == TYP_STRUCT) + if (fromLayout != nullptr && toLayout != nullptr && ClassLayout::AreCompatible(fromLayout, toLayout)) { - involvesStructType = true; + // Handle compatible struct layouts where we can simply return op1 + return impPopStack().val; } - if (involvesStructType) + if (varTypeIsStruct(fromType) || varTypeIsStruct(toType)) { - // TODO-CQ: Handle this by getting the address of `op1` and then dereferencing - // that as TTo, much as `Unsafe.As(ref op1)` would work. - return nullptr; + GenTree* addr = impGetNodeAddr(impPopStack().val, fromTypeHnd, CHECK_SPILL_ALL, true); + return gtNewLoadValueNode(toType, toLayout, addr); } if (varTypeIsFloating(fromType)) @@ -4300,26 +4282,21 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, } case NI_SRCS_UNSAFE_Read: - { - assert(sig->sigInst.methInstCount == 1); - - // ldarg.0 - // ldobj !!T - // ret - - return nullptr; - } - case NI_SRCS_UNSAFE_ReadUnaligned: { assert(sig->sigInst.methInstCount == 1); // ldarg.0 - // unaligned. 0x1 + // if NI_SRCS_UNSAFE_ReadUnaligned: unaligned. 0x1 // ldobj !!T // ret - return nullptr; + CORINFO_CLASS_HANDLE typeHnd = sig->sigInst.methInst[0]; + ClassLayout* layout = nullptr; + var_types type = TypeHandleToVarType(typeHnd, &layout); + GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_ReadUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; + + return gtNewLoadValueNode(type, layout, impPopStack().val, flags); } case NI_SRCS_UNSAFE_SizeOf: @@ -4410,28 +4387,24 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, } case NI_SRCS_UNSAFE_Write: + case NI_SRCS_UNSAFE_WriteUnaligned: { assert(sig->sigInst.methInstCount == 1); // ldarg.0 // ldarg.1 + // if NI_SRCS_UNSAFE_WriteUnaligned: unaligned. 0x01 // stobj !!T // ret - return nullptr; - } - - case NI_SRCS_UNSAFE_WriteUnaligned: - { - assert(sig->sigInst.methInstCount == 1); + GenTree* op1 = impPopStack().val; + GenTree* op2 = impPopStack().val; - // ldarg.0 - // ldarg.1 - // unaligned. 0x01 - // stobj !!T - // ret + CORINFO_CLASS_HANDLE typeHnd = sig->sigInst.methInst[0]; + var_types type = TypeHandleToVarType(typeHnd, nullptr); + GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; - return nullptr; + return gtNewAssignNode(gtNewIndir(type, op1, flags), op2); } default: diff --git a/src/coreclr/jit/importervectorization.cpp b/src/coreclr/jit/importervectorization.cpp index 2a36b09b96988..0d37868dc3e7e 100644 --- a/src/coreclr/jit/importervectorization.cpp +++ b/src/coreclr/jit/importervectorization.cpp @@ -795,7 +795,7 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* if (unrolled != nullptr) { // We succeeded, fill the placeholders: - impAssignTempGen(spanObjRef, impGetStructAddr(spanObj, spanCls, CHECK_SPILL_NONE, true)); + impAssignTempGen(spanObjRef, impGetNodeAddr(spanObj, spanCls, CHECK_SPILL_NONE, true)); impAssignTempGen(spanDataTmp, spanData); if (unrolled->OperIs(GT_QMARK)) { From 5e78c9cee61a7760fee336047886c520e0de6c90 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 01:02:31 +0200 Subject: [PATCH 02/50] Fix broken rebase --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index c75fb19dbc3c1..b178231f756a4 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1231,7 +1231,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, case GT_COMMA: impAppendTree(val->AsOp()->gtGetOp1(), curLevel, impCurStmtDI); - return impGetStructAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, willDeref); + return impGetNodeAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, willDeref); default: break; From 93399c2d02472977082a63256d4039c8d3574c63 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 01:19:34 +0200 Subject: [PATCH 03/50] Handle small type extension --- src/coreclr/jit/importercalls.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index b2ebec8d109cf..9d80bb523bd37 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4051,7 +4051,18 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if (varTypeIsStruct(fromType) || varTypeIsStruct(toType)) { - GenTree* addr = impGetNodeAddr(impPopStack().val, fromTypeHnd, CHECK_SPILL_ALL, true); + GenTree* addr; + GenTree* val = impPopStack().val; + if (val->OperIsIndir() && (fromSize != val->AsIndir()->Size())) + { + unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); + impAssignTempGen(lclNum, val, fromTypeHnd, CHECK_SPILL_ALL); + addr = gtNewLclVarAddrNode(lclNum, TYP_BYREF); + } + else + { + addr = impGetNodeAddr(val, fromTypeHnd, CHECK_SPILL_ALL, true); + } return gtNewLoadValueNode(toType, toLayout, addr); } From 8987b69f9d99316b7b2ac69f72f443653d7a577b Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 01:32:19 +0200 Subject: [PATCH 04/50] Fix formatting --- src/coreclr/jit/importer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index b178231f756a4..cbe55115c7c44 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1195,10 +1195,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // will return its address. Otherwise, address of a temporary assigned // the value of "val" will be returned. // -GenTree* Compiler::impGetNodeAddr(GenTree* val, - CORINFO_CLASS_HANDLE typeHnd, - unsigned curLevel, - bool willDeref) +GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, bool willDeref) { switch (val->OperGet()) { From 858f39c12883153909138a0a5df7909b22201c81 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 02:31:03 +0200 Subject: [PATCH 05/50] Fix swapped operands --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 9d80bb523bd37..b7a0e35e79f05 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4415,7 +4415,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, var_types type = TypeHandleToVarType(typeHnd, nullptr); GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; - return gtNewAssignNode(gtNewIndir(type, op1, flags), op2); + return gtNewAssignNode(gtNewIndir(type, op2, flags), op1); } default: From a19335069c5b3b765a4f427e1cbd45e3f6997ccf Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 02:56:12 +0200 Subject: [PATCH 06/50] Use right helper --- src/coreclr/jit/importercalls.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index b7a0e35e79f05..11eb61ea15385 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4412,10 +4412,11 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTree* op2 = impPopStack().val; CORINFO_CLASS_HANDLE typeHnd = sig->sigInst.methInst[0]; - var_types type = TypeHandleToVarType(typeHnd, nullptr); + ClassLayout* layout = nullptr; + var_types type = TypeHandleToVarType(typeHnd, &layout); GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; - return gtNewAssignNode(gtNewIndir(type, op2, flags), op1); + return gtNewAssignNode(gtNewLoadValueNode(type, layout, op2, flags), op1); } default: From db54e9765bae7c886167a38baf138a5d008b7be3 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 16:50:14 +0200 Subject: [PATCH 07/50] Don't loose indir flags --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/gentree.cpp | 9 ++-- src/coreclr/jit/importer.cpp | 55 +++++++++++++++-------- src/coreclr/jit/importercalls.cpp | 7 +-- src/coreclr/jit/importervectorization.cpp | 4 +- 5 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 6dfac35fd377e..53e8e9fced59e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4051,7 +4051,7 @@ class Compiler const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); - GenTree* impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, bool willDeref); + GenTree* impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, GenTreeFlags* derefFlags); var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoType* simdBaseJitType = nullptr); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 95191fd58df3e..326df5417fc14 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15937,7 +15937,9 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, // helper needs pointer to struct, not struct itself if (pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT) { - assg = impGetNodeAddr(assg, structType, CHECK_SPILL_ALL, true); + // TODO: deal with flags + GenTreeFlags flags = GTF_EMPTY; + assg = impGetNodeAddr(assg, structType, CHECK_SPILL_ALL, &flags); } else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT) { @@ -16013,8 +16015,9 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, if (!varTypeIsStruct(lclTyp)) { // get the result as primitive type - result = impGetNodeAddr(result, structType, CHECK_SPILL_ALL, true); - result = gtNewIndir(lclTyp, result); + GenTreeFlags flags = GTF_EMPTY; + result = impGetNodeAddr(result, structType, CHECK_SPILL_ALL, &flags); + result = gtNewIndir(lclTyp, result, flags); } } else if (varTypeIsIntegral(lclTyp) && genTypeSize(lclTyp) < genTypeSize(TYP_INT)) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index cbe55115c7c44..8a10f2259f80c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -957,7 +957,9 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, WellKnownArg wellKnownArgType = srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; - GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, /* willDeref */ true); + // TODO: deal with flags + GenTreeFlags flags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, &flags); NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); #if !defined(TARGET_ARM) @@ -1059,7 +1061,9 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, if (call->ShouldHaveRetBufArg()) { // insert the return value buffer into the argument list as first byref parameter after 'this' - GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, /* willDeref */ true); + // TODO: deal with flags + GenTreeFlags flags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, &flags); call->gtArgs.InsertAfterThisOrFirst(this, NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer)); @@ -1076,7 +1080,8 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // Since we are assigning the result of a GT_MKREFANY, "destAddr" must point to a refany. // TODO-CQ: we can do this without address-exposing the local on the LHS. - GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, /* willDeref */ true); + GenTreeFlags flags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); GenTree* destAddrClone; destAddr = impCloneExpr(destAddr, &destAddrClone, NO_CLASS_HANDLE, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); @@ -1084,11 +1089,11 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, assert(OFFSETOF__CORINFO_TypedReference__dataPtr == 0); assert(destAddr->gtType == TYP_I_IMPL || destAddr->gtType == TYP_BYREF); - GenTree* ptrSlot = gtNewIndir(TYP_I_IMPL, destAddr); + GenTree* ptrSlot = gtNewIndir(TYP_I_IMPL, destAddr, flags); GenTreeIntCon* typeFieldOffset = gtNewIconNode(OFFSETOF__CORINFO_TypedReference__type, TYP_I_IMPL); GenTree* typeSlot = - gtNewIndir(TYP_I_IMPL, gtNewOperNode(GT_ADD, destAddr->gtType, destAddrClone, typeFieldOffset)); + gtNewIndir(TYP_I_IMPL, gtNewOperNode(GT_ADD, destAddr->gtType, destAddrClone, typeFieldOffset), flags); // append the assign of the pointer value GenTree* asg = gtNewAssignNode(ptrSlot, src->AsOp()->gtOp1); @@ -1185,24 +1190,26 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // impGetNodeAddr: Get the address of a value. // // Arguments: -// val - The value in question -// typeHnd - The type handle for "val" -// curLevel - Stack level for spilling -// willDeref - Whether the caller will dereference the address +// val - The value in question +// typeHnd - The type handle for "val" +// curLevel - Stack level for spilling +// derefFlags - Flags to be used on dereference, nullptr when +// the address won't be dereferenced // // Return Value: // In case "val" can represent locations (is an indirection/local), // will return its address. Otherwise, address of a temporary assigned // the value of "val" will be returned. // -GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, bool willDeref) +GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, GenTreeFlags* derefFlags) { switch (val->OperGet()) { case GT_BLK: case GT_IND: - if (willDeref) + if (derefFlags != nullptr) { + flags |= val->gtFlags; return val->AsIndir()->Addr(); } break; @@ -1215,6 +1222,10 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, un case GT_FIELD: { + if (derefFlags != nullptr) + { + flags |= (val->gtFlags & GTF_IND_FLAGS); + } GenTreeField* fieldNode = val->AsField(); GenTreeField* fieldAddr = new (this, GT_FIELD_ADDR) GenTreeField(GT_FIELD_ADDR, TYP_BYREF, fieldNode->GetFldObj(), @@ -1228,7 +1239,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, un case GT_COMMA: impAppendTree(val->AsOp()->gtGetOp1(), curLevel, impCurStmtDI); - return impGetNodeAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, willDeref); + return impGetNodeAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, derefFlags); default: break; @@ -3268,7 +3279,9 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, GenTree* objToBox = impPopStack().val; // Spill struct to get its address (to access hasValue field) - objToBox = impGetNodeAddr(objToBox, nullableCls, CHECK_SPILL_ALL, true); + // TODO: deal with flags + GenTreeFlags flags = GTF_EMPTY; + objToBox = impGetNodeAddr(objToBox, nullableCls, CHECK_SPILL_ALL, &flags); impPushOnStack(gtNewFieldRef(TYP_BOOL, hasValueFldHnd, objToBox, 0), typeInfo(TI_INT)); @@ -3621,7 +3634,9 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) return; } - op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, operCls, CHECK_SPILL_ALL, true)); + // TODO: deal with flags + GenTreeFlags flags = GTF_EMPTY; + op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, operCls, CHECK_SPILL_ALL, &flags)); } /* Push the result back on the stack, */ @@ -8289,7 +8304,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) } else { - op1 = impGetNodeAddr(op1, clsHnd, CHECK_SPILL_ALL, false); + op1 = impGetNodeAddr(op1, clsHnd, CHECK_SPILL_ALL, nullptr); } JITDUMP("\n ... optimized to ...\n"); @@ -9252,7 +9267,10 @@ void Compiler::impImportBlockCode(BasicBlock* block) { BADCODE("top of stack must be a value type"); } - obj = impGetNodeAddr(obj, objType, CHECK_SPILL_ALL, true); + + // TODO: deal with flags + GenTreeFlags flags = GTF_EMPTY; + obj = impGetNodeAddr(obj, objType, CHECK_SPILL_ALL, &flags); } if (isLoadAddress) @@ -10008,12 +10026,13 @@ void Compiler::impImportBlockCode(BasicBlock* block) else { // Get the address of the refany - op1 = impGetNodeAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, /* willDeref */ true); + GenTreeFlags flags = GTF_EMPTY; + op1 = impGetNodeAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); // Fetch the type from the correct slot op1 = gtNewOperNode(GT_ADD, TYP_BYREF, op1, gtNewIconNode(OFFSETOF__CORINFO_TypedReference__type, TYP_I_IMPL)); - op1 = gtNewIndir(TYP_BYREF, op1); + op1 = gtNewIndir(TYP_BYREF, op1, flags); } // Convert native TypeHandle to RuntimeTypeHandle. diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 11eb61ea15385..f0e045cc4b60b 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4052,7 +4052,8 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if (varTypeIsStruct(fromType) || varTypeIsStruct(toType)) { GenTree* addr; - GenTree* val = impPopStack().val; + GenTreeFlags flags = GTF_EMPTY; + GenTree* val = impPopStack().val; if (val->OperIsIndir() && (fromSize != val->AsIndir()->Size())) { unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); @@ -4061,9 +4062,9 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, } else { - addr = impGetNodeAddr(val, fromTypeHnd, CHECK_SPILL_ALL, true); + addr = impGetNodeAddr(val, fromTypeHnd, CHECK_SPILL_ALL, &flags); } - return gtNewLoadValueNode(toType, toLayout, addr); + return gtNewLoadValueNode(toType, toLayout, addr, flags); } if (varTypeIsFloating(fromType)) diff --git a/src/coreclr/jit/importervectorization.cpp b/src/coreclr/jit/importervectorization.cpp index 0d37868dc3e7e..5dcb39c2251c8 100644 --- a/src/coreclr/jit/importervectorization.cpp +++ b/src/coreclr/jit/importervectorization.cpp @@ -795,7 +795,9 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* if (unrolled != nullptr) { // We succeeded, fill the placeholders: - impAssignTempGen(spanObjRef, impGetNodeAddr(spanObj, spanCls, CHECK_SPILL_NONE, true)); + // TODO: deal with flags + GenTreeFlags flags = GTF_EMPTY; + impAssignTempGen(spanObjRef, impGetNodeAddr(spanObj, spanCls, CHECK_SPILL_NONE, &flags)); impAssignTempGen(spanDataTmp, spanData); if (unrolled->OperIs(GT_QMARK)) { From 9c2ee9e44be5424838338305ba95543b5932a15c Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 17:11:41 +0200 Subject: [PATCH 08/50] Fix build --- src/coreclr/jit/gentree.cpp | 6 +++--- src/coreclr/jit/importer.cpp | 29 ++++++++++++++++------------- src/coreclr/jit/importercalls.cpp | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 326df5417fc14..04b76635307c5 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15939,7 +15939,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, { // TODO: deal with flags GenTreeFlags flags = GTF_EMPTY; - assg = impGetNodeAddr(assg, structType, CHECK_SPILL_ALL, &flags); + assg = impGetNodeAddr(assg, structType, CHECK_SPILL_ALL, &flags); } else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT) { @@ -16016,8 +16016,8 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, { // get the result as primitive type GenTreeFlags flags = GTF_EMPTY; - result = impGetNodeAddr(result, structType, CHECK_SPILL_ALL, &flags); - result = gtNewIndir(lclTyp, result, flags); + result = impGetNodeAddr(result, structType, CHECK_SPILL_ALL, &flags); + result = gtNewIndir(lclTyp, result, flags); } } else if (varTypeIsIntegral(lclTyp) && genTypeSize(lclTyp) < genTypeSize(TYP_INT)) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 8a10f2259f80c..79ae6bd3c6993 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -958,9 +958,9 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, &flags); - NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); + GenTreeFlags flags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, &flags); + NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); #if !defined(TARGET_ARM) // Unmanaged instance methods on Windows or Unix X86 need the retbuf arg after the first (this) parameter @@ -1062,8 +1062,8 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // insert the return value buffer into the argument list as first byref parameter after 'this' // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, &flags); + GenTreeFlags flags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, &flags); call->gtArgs.InsertAfterThisOrFirst(this, NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer)); @@ -1080,9 +1080,9 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // Since we are assigning the result of a GT_MKREFANY, "destAddr" must point to a refany. // TODO-CQ: we can do this without address-exposing the local on the LHS. - GenTreeFlags flags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); - GenTree* destAddrClone; + GenTreeFlags flags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); + GenTree* destAddrClone; destAddr = impCloneExpr(destAddr, &destAddrClone, NO_CLASS_HANDLE, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); @@ -1201,7 +1201,10 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // will return its address. Otherwise, address of a temporary assigned // the value of "val" will be returned. // -GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, GenTreeFlags* derefFlags) +GenTree* Compiler::impGetNodeAddr(GenTree* val, + CORINFO_CLASS_HANDLE typeHnd, + unsigned curLevel, + GenTreeFlags* derefFlags) { switch (val->OperGet()) { @@ -1209,7 +1212,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, un case GT_IND: if (derefFlags != nullptr) { - flags |= val->gtFlags; + derefFlags |= val->gtFlags; return val->AsIndir()->Addr(); } break; @@ -1224,7 +1227,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, un { if (derefFlags != nullptr) { - flags |= (val->gtFlags & GTF_IND_FLAGS); + derefFlags |= (val->gtFlags & GTF_IND_FLAGS); } GenTreeField* fieldNode = val->AsField(); GenTreeField* fieldAddr = @@ -9270,7 +9273,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) // TODO: deal with flags GenTreeFlags flags = GTF_EMPTY; - obj = impGetNodeAddr(obj, objType, CHECK_SPILL_ALL, &flags); + obj = impGetNodeAddr(obj, objType, CHECK_SPILL_ALL, &flags); } if (isLoadAddress) @@ -10027,7 +10030,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) { // Get the address of the refany GenTreeFlags flags = GTF_EMPTY; - op1 = impGetNodeAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); + op1 = impGetNodeAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); // Fetch the type from the correct slot op1 = gtNewOperNode(GT_ADD, TYP_BYREF, op1, diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index f0e045cc4b60b..e1b6fa0ba7ad0 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4051,7 +4051,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if (varTypeIsStruct(fromType) || varTypeIsStruct(toType)) { - GenTree* addr; + GenTree* addr; GenTreeFlags flags = GTF_EMPTY; GenTree* val = impPopStack().val; if (val->OperIsIndir() && (fromSize != val->AsIndir()->Size())) From 3e5003ff46ab7726eb922f21bfa6c7b64f627660 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 17:27:44 +0200 Subject: [PATCH 09/50] Fix missed deref --- src/coreclr/jit/importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 79ae6bd3c6993..34f2998d1bd94 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1212,7 +1212,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, case GT_IND: if (derefFlags != nullptr) { - derefFlags |= val->gtFlags; + *derefFlags |= val->gtFlags; return val->AsIndir()->Addr(); } break; @@ -1227,7 +1227,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, { if (derefFlags != nullptr) { - derefFlags |= (val->gtFlags & GTF_IND_FLAGS); + *derefFlags |= (val->gtFlags & GTF_IND_FLAGS); } GenTreeField* fieldNode = val->AsField(); GenTreeField* fieldAddr = From b3e7e970ae230d036c8feb1a7225d57b7a6db7aa Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 18:41:22 +0200 Subject: [PATCH 10/50] Also mask indir flags --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 34f2998d1bd94..78bed9daaae58 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1212,7 +1212,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, case GT_IND: if (derefFlags != nullptr) { - *derefFlags |= val->gtFlags; + *derefFlags |= (val->gtFlags & GTF_IND_FLAGS); return val->AsIndir()->Addr(); } break; From 66f111bd0e5362b5d2d47e1f60e8fad084d6f3fa Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 21:18:28 +0200 Subject: [PATCH 11/50] Rename variables, fix structs --- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/gentree.cpp | 10 ++--- src/coreclr/jit/importer.cpp | 54 +++++++++++------------ src/coreclr/jit/importercalls.cpp | 18 +++++--- src/coreclr/jit/importervectorization.cpp | 4 +- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 53e8e9fced59e..2423640ec1d25 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4051,7 +4051,7 @@ class Compiler const DebugInfo& di = DebugInfo(), BasicBlock* block = nullptr); - GenTree* impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, GenTreeFlags* derefFlags); + GenTree* impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, GenTreeFlags* pDerefFlags); var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd, CorInfoType* simdBaseJitType = nullptr); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 04b76635307c5..de953c50d4950 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -15938,8 +15938,8 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, if (pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT) { // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - assg = impGetNodeAddr(assg, structType, CHECK_SPILL_ALL, &flags); + GenTreeFlags indirFlags = GTF_EMPTY; + assg = impGetNodeAddr(assg, structType, CHECK_SPILL_ALL, &indirFlags); } else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT) { @@ -16015,9 +16015,9 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, if (!varTypeIsStruct(lclTyp)) { // get the result as primitive type - GenTreeFlags flags = GTF_EMPTY; - result = impGetNodeAddr(result, structType, CHECK_SPILL_ALL, &flags); - result = gtNewIndir(lclTyp, result, flags); + GenTreeFlags indirFlags = GTF_EMPTY; + result = impGetNodeAddr(result, structType, CHECK_SPILL_ALL, &indirFlags); + result = gtNewIndir(lclTyp, result, indirFlags); } } else if (varTypeIsIntegral(lclTyp) && genTypeSize(lclTyp) < genTypeSize(TYP_INT)) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 78bed9daaae58..dbd6feab8d0d7 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -958,9 +958,9 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, &flags); - NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); + GenTreeFlags indirFlags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, &indirFlags); + NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); #if !defined(TARGET_ARM) // Unmanaged instance methods on Windows or Unix X86 need the retbuf arg after the first (this) parameter @@ -1062,8 +1062,8 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // insert the return value buffer into the argument list as first byref parameter after 'this' // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, &flags); + GenTreeFlags indirFlags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, &indirFlags); call->gtArgs.InsertAfterThisOrFirst(this, NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer)); @@ -1080,8 +1080,8 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // Since we are assigning the result of a GT_MKREFANY, "destAddr" must point to a refany. // TODO-CQ: we can do this without address-exposing the local on the LHS. - GenTreeFlags flags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); + GenTreeFlags indirFlags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &indirFlags); GenTree* destAddrClone; destAddr = impCloneExpr(destAddr, &destAddrClone, NO_CLASS_HANDLE, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); @@ -1089,11 +1089,11 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, assert(OFFSETOF__CORINFO_TypedReference__dataPtr == 0); assert(destAddr->gtType == TYP_I_IMPL || destAddr->gtType == TYP_BYREF); - GenTree* ptrSlot = gtNewIndir(TYP_I_IMPL, destAddr, flags); + GenTree* ptrSlot = gtNewIndir(TYP_I_IMPL, destAddr, indirFlags); GenTreeIntCon* typeFieldOffset = gtNewIconNode(OFFSETOF__CORINFO_TypedReference__type, TYP_I_IMPL); GenTree* typeSlot = - gtNewIndir(TYP_I_IMPL, gtNewOperNode(GT_ADD, destAddr->gtType, destAddrClone, typeFieldOffset), flags); + gtNewIndir(TYP_I_IMPL, gtNewOperNode(GT_ADD, destAddr->gtType, destAddrClone, typeFieldOffset), indirFlags); // append the assign of the pointer value GenTree* asg = gtNewAssignNode(ptrSlot, src->AsOp()->gtOp1); @@ -1190,10 +1190,10 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, // impGetNodeAddr: Get the address of a value. // // Arguments: -// val - The value in question -// typeHnd - The type handle for "val" -// curLevel - Stack level for spilling -// derefFlags - Flags to be used on dereference, nullptr when +// val - The value in question +// typeHnd - The type handle for "val" +// curLevel - Stack level for spilling +// pDerefFlags - Flags to be used on dereference, nullptr when // the address won't be dereferenced // // Return Value: @@ -1204,15 +1204,15 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTree* Compiler::impGetNodeAddr(GenTree* val, CORINFO_CLASS_HANDLE typeHnd, unsigned curLevel, - GenTreeFlags* derefFlags) + GenTreeFlags* pDerefFlags) { switch (val->OperGet()) { case GT_BLK: case GT_IND: - if (derefFlags != nullptr) + if (pDerefFlags != nullptr) { - *derefFlags |= (val->gtFlags & GTF_IND_FLAGS); + *pDerefFlags |= (val->gtFlags & GTF_IND_FLAGS); return val->AsIndir()->Addr(); } break; @@ -1225,9 +1225,9 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, case GT_FIELD: { - if (derefFlags != nullptr) + if (pDerefFlags != nullptr) { - *derefFlags |= (val->gtFlags & GTF_IND_FLAGS); + *pDerefFlags |= (val->gtFlags & GTF_IND_FLAGS); } GenTreeField* fieldNode = val->AsField(); GenTreeField* fieldAddr = @@ -1242,7 +1242,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, case GT_COMMA: impAppendTree(val->AsOp()->gtGetOp1(), curLevel, impCurStmtDI); - return impGetNodeAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, derefFlags); + return impGetNodeAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, pDerefFlags); default: break; @@ -3283,8 +3283,8 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, // Spill struct to get its address (to access hasValue field) // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - objToBox = impGetNodeAddr(objToBox, nullableCls, CHECK_SPILL_ALL, &flags); + GenTreeFlags indirFlags = GTF_EMPTY; + objToBox = impGetNodeAddr(objToBox, nullableCls, CHECK_SPILL_ALL, &indirFlags); impPushOnStack(gtNewFieldRef(TYP_BOOL, hasValueFldHnd, objToBox, 0), typeInfo(TI_INT)); @@ -3638,8 +3638,8 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) } // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, operCls, CHECK_SPILL_ALL, &flags)); + GenTreeFlags indirFlags = GTF_EMPTY; + op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, operCls, CHECK_SPILL_ALL, &indirFlags)); } /* Push the result back on the stack, */ @@ -9272,8 +9272,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) } // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - obj = impGetNodeAddr(obj, objType, CHECK_SPILL_ALL, &flags); + GenTreeFlags indirFlags = GTF_EMPTY; + obj = impGetNodeAddr(obj, objType, CHECK_SPILL_ALL, &indirFlags); } if (isLoadAddress) @@ -10029,8 +10029,8 @@ void Compiler::impImportBlockCode(BasicBlock* block) else { // Get the address of the refany - GenTreeFlags flags = GTF_EMPTY; - op1 = impGetNodeAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, &flags); + GenTreeFlags indirFlags = GTF_EMPTY; + op1 = impGetNodeAddr(op1, impGetRefAnyClass(), CHECK_SPILL_ALL, &indirFlags); // Fetch the type from the correct slot op1 = gtNewOperNode(GT_ADD, TYP_BYREF, op1, diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index e1b6fa0ba7ad0..ea9994b8896c8 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4052,8 +4052,8 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if (varTypeIsStruct(fromType) || varTypeIsStruct(toType)) { GenTree* addr; - GenTreeFlags flags = GTF_EMPTY; - GenTree* val = impPopStack().val; + GenTreeFlags indirFlags = GTF_EMPTY; + GenTree* val = impPopStack().val; if (val->OperIsIndir() && (fromSize != val->AsIndir()->Size())) { unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); @@ -4062,9 +4062,9 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, } else { - addr = impGetNodeAddr(val, fromTypeHnd, CHECK_SPILL_ALL, &flags); + addr = impGetNodeAddr(val, fromTypeHnd, CHECK_SPILL_ALL, &indirFlags); } - return gtNewLoadValueNode(toType, toLayout, addr, flags); + return gtNewLoadValueNode(toType, toLayout, addr, indirFlags); } if (varTypeIsFloating(fromType)) @@ -4416,8 +4416,16 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, ClassLayout* layout = nullptr; var_types type = TypeHandleToVarType(typeHnd, &layout); GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; + GenTree* indir = gtNewLoadValueNode(type, layout, op2, flags); - return gtNewAssignNode(gtNewLoadValueNode(type, layout, op2, flags), op1); + if (varTypeIsStruct(type)) + { + return impAssignStruct(indir, op1, CHECK_SPILL_ALL); + } + else + { + return gtNewAssignNode(indir, op1); + } } default: diff --git a/src/coreclr/jit/importervectorization.cpp b/src/coreclr/jit/importervectorization.cpp index 5dcb39c2251c8..c6210bd0a5889 100644 --- a/src/coreclr/jit/importervectorization.cpp +++ b/src/coreclr/jit/importervectorization.cpp @@ -796,8 +796,8 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* { // We succeeded, fill the placeholders: // TODO: deal with flags - GenTreeFlags flags = GTF_EMPTY; - impAssignTempGen(spanObjRef, impGetNodeAddr(spanObj, spanCls, CHECK_SPILL_NONE, &flags)); + GenTreeFlags indirFlags = GTF_EMPTY; + impAssignTempGen(spanObjRef, impGetNodeAddr(spanObj, spanCls, CHECK_SPILL_NONE, &indirFlags)); impAssignTempGen(spanDataTmp, spanData); if (unrolled->OperIs(GT_QMARK)) { From a5bb60b3b9db6fb1bc2de77e98bbca8590fdbdb4 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 30 Apr 2023 21:38:56 +0200 Subject: [PATCH 12/50] Fix build --- src/coreclr/jit/importer.cpp | 19 ++++++++++--------- src/coreclr/jit/importercalls.cpp | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index dbd6feab8d0d7..bddd90f12c8a8 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -958,9 +958,9 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; // TODO: deal with flags - GenTreeFlags indirFlags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, &indirFlags); - NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); + GenTreeFlags indirFlags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, srcCall->gtRetClsHnd, CHECK_SPILL_ALL, &indirFlags); + NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); #if !defined(TARGET_ARM) // Unmanaged instance methods on Windows or Unix X86 need the retbuf arg after the first (this) parameter @@ -1062,8 +1062,8 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // insert the return value buffer into the argument list as first byref parameter after 'this' // TODO: deal with flags - GenTreeFlags indirFlags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, &indirFlags); + GenTreeFlags indirFlags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, call->gtRetClsHnd, CHECK_SPILL_ALL, &indirFlags); call->gtArgs.InsertAfterThisOrFirst(this, NewCallArg::Primitive(destAddr).WellKnown(WellKnownArg::RetBuffer)); @@ -1080,8 +1080,8 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, { // Since we are assigning the result of a GT_MKREFANY, "destAddr" must point to a refany. // TODO-CQ: we can do this without address-exposing the local on the LHS. - GenTreeFlags indirFlags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &indirFlags); + GenTreeFlags indirFlags = GTF_EMPTY; + GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &indirFlags); GenTree* destAddrClone; destAddr = impCloneExpr(destAddr, &destAddrClone, NO_CLASS_HANDLE, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); @@ -3639,7 +3639,8 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) // TODO: deal with flags GenTreeFlags indirFlags = GTF_EMPTY; - op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, operCls, CHECK_SPILL_ALL, &indirFlags)); + op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, + impGetNodeAddr(exprToBox, operCls, CHECK_SPILL_ALL, &indirFlags)); } /* Push the result back on the stack, */ @@ -10035,7 +10036,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) // Fetch the type from the correct slot op1 = gtNewOperNode(GT_ADD, TYP_BYREF, op1, gtNewIconNode(OFFSETOF__CORINFO_TypedReference__type, TYP_I_IMPL)); - op1 = gtNewIndir(TYP_BYREF, op1, flags); + op1 = gtNewIndir(TYP_BYREF, op1, indirFlags); } // Convert native TypeHandle to RuntimeTypeHandle. diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index ea9994b8896c8..acb7812961be5 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4416,7 +4416,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, ClassLayout* layout = nullptr; var_types type = TypeHandleToVarType(typeHnd, &layout); GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; - GenTree* indir = gtNewLoadValueNode(type, layout, op2, flags); + GenTree* indir = gtNewLoadValueNode(type, layout, op2, flags); if (varTypeIsStruct(type)) { From 36ab91d8a683f8a59d7b3c69cf8b6019acc2f82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Wed, 3 May 2023 22:07:01 +0200 Subject: [PATCH 13/50] Update importer.cpp --- src/coreclr/jit/importer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 5c92a55eb92ea..c1e23fade0134 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1229,6 +1229,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, case GT_LCL_FLD: return gtNewLclAddrNode(val->AsLclFld()->GetLclNum(), val->AsLclFld()->GetLclOffs(), TYP_BYREF); + case GT_COMMA: impAppendTree(val->AsOp()->gtGetOp1(), curLevel, impCurStmtDI); return impGetNodeAddr(val->AsOp()->gtGetOp2(), typeHnd, curLevel, pDerefFlags); From b9119846aa5520838a7e55d0f7e16298e938b893 Mon Sep 17 00:00:00 2001 From: petris Date: Sat, 6 May 2023 23:23:18 +0200 Subject: [PATCH 14/50] Fix 32bit bug, simplify code --- src/coreclr/inc/targetosarch.h | 5 ++ src/coreclr/jit/importercalls.cpp | 87 ++++++++++--------------------- 2 files changed, 32 insertions(+), 60 deletions(-) diff --git a/src/coreclr/inc/targetosarch.h b/src/coreclr/inc/targetosarch.h index fbe7806cf7d1c..c4742b08c8e14 100644 --- a/src/coreclr/inc/targetosarch.h +++ b/src/coreclr/inc/targetosarch.h @@ -35,6 +35,11 @@ class TargetOS class TargetArchitecture { public: +#ifdef TARGET_64BIT + static const bool Is64Bit = true; +#else + static const bool Is64Bit = false; +#endif #ifdef TARGET_ARM static const bool IsX86 = false; static const bool IsX64 = false; diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 56a3ac80e1de4..2bc2958682838 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4024,12 +4024,6 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, CORINFO_CLASS_HANDLE fromTypeHnd = sig->sigInst.methInst[0]; CORINFO_CLASS_HANDLE toTypeHnd = sig->sigInst.methInst[1]; - if (fromTypeHnd == toTypeHnd) - { - // Handle the easy case of matching type handles, such as `int` to `int` - return impPopStack().val; - } - unsigned fromSize = info.compCompHnd->getClassSize(fromTypeHnd); unsigned toSize = info.compCompHnd->getClassSize(toTypeHnd); @@ -4048,45 +4042,20 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, ClassLayout* toLayout = nullptr; var_types toType = TypeHandleToVarType(toTypeHnd, &toLayout); - if (fromLayout != nullptr && toLayout != nullptr && ClassLayout::AreCompatible(fromLayout, toLayout)) - { - // Handle compatible struct layouts where we can simply return op1 - return impPopStack().val; - } + assert((fromType != TYP_REF) && (toType != TYP_REF)); - if (varTypeIsStruct(fromType) || varTypeIsStruct(toType)) + GenTree* op1 = impPopStack().val; + + if ((fromTypeHnd == toTypeHnd) || (varTypeIsIntegral(fromType) && varTypeIsIntegral(toType)) || + (fromLayout != nullptr && toLayout != nullptr && ClassLayout::AreCompatible(fromLayout, toLayout))) { - GenTree* addr; - GenTreeFlags indirFlags = GTF_EMPTY; - GenTree* val = impPopStack().val; - if (val->OperIsIndir() && (fromSize != val->AsIndir()->Size())) - { - unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); - impAssignTempGen(lclNum, val, fromTypeHnd, CHECK_SPILL_ALL); - addr = gtNewLclVarAddrNode(lclNum, TYP_BYREF); - } - else - { - addr = impGetNodeAddr(val, fromTypeHnd, CHECK_SPILL_ALL, &indirFlags); - } - return gtNewLoadValueNode(toType, toLayout, addr, indirFlags); + // Handle matching handles, integrals or compatible struct layouts where we can simply return op1 + return op1; } - if (varTypeIsFloating(fromType)) + // Handle bitcasting between floating and same sized integral, such as `float` to `int` + if (varTypeIsFloating(fromType) && varTypeIsIntegral(toType)) { - // Handle bitcasting from floating to same sized integral, such as `float` to `int` - assert(varTypeIsIntegral(toType)); - -#if !TARGET_64BIT - if ((fromType == TYP_DOUBLE) && !impStackTop().val->IsCnsFltOrDbl()) - { - // TODO-Cleanup: We should support this on 32-bit but it requires decomposition work - return nullptr; - } -#endif // !TARGET_64BIT - - GenTree* op1 = impPopStack().val; - if (op1->IsCnsFltOrDbl()) { if (fromType == TYP_DOUBLE) @@ -4101,30 +4070,16 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, return gtNewIconNode(static_cast(BitOperations::SingleToUInt32Bits(f32Cns))); } } - else + // TODO-CQ: We should support this on 32-bit via decomposition + else if (TargetArchitecture::Is64Bit || (fromType == TYP_FLOAT)) { toType = varTypeToSigned(toType); op1 = impImplicitR4orR8Cast(op1, fromType); return gtNewBitCastNode(toType, op1); } - break; } - - if (varTypeIsFloating(toType)) + else if (varTypeIsIntegral(fromType) && varTypeIsFloating(toType)) { - // Handle bitcasting from integral to same sized floating, such as `int` to `float` - assert(varTypeIsIntegral(fromType)); - -#if !TARGET_64BIT - if ((toType == TYP_DOUBLE) && !impStackTop().val->IsIntegralConst()) - { - // TODO-Cleanup: We should support this on 32-bit but it requires decomposition work - return nullptr; - } -#endif // !TARGET_64BIT - - GenTree* op1 = impPopStack().val; - if (op1->IsIntegralConst()) { if (toType == TYP_DOUBLE) @@ -4140,14 +4095,26 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, return gtNewDconNode(BitOperations::UInt32BitsToSingle(u32Cns), TYP_FLOAT); } } - else + // TODO-CQ: We should support this on 32-bit via decomposition + else if (TargetArchitecture::Is64Bit || (toType == TYP_FLOAT)) { return gtNewBitCastNode(toType, op1); } } - // Handle bitcasting for same sized integrals, such as `int` to `uint` - return impPopStack().val; + GenTree* addr; + GenTreeFlags indirFlags = GTF_EMPTY; + if (op1->OperIsIndir() && (fromSize != op1->AsIndir()->Size())) + { + unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); + impAssignTempGen(lclNum, op1, fromTypeHnd, CHECK_SPILL_ALL); + addr = gtNewLclVarAddrNode(lclNum, TYP_BYREF); + } + else + { + addr = impGetNodeAddr(op1, fromTypeHnd, CHECK_SPILL_ALL, &indirFlags); + } + return gtNewLoadValueNode(toType, toLayout, addr, indirFlags); } case NI_SRCS_UNSAFE_ByteOffset: From 2982fd4e95403a00711c7f498816e12328c65d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 6 May 2023 23:28:52 +0200 Subject: [PATCH 15/50] Fix merge --- src/coreclr/jit/importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index c319abad7465f..1acc4cbdee297 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1005,7 +1005,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, // Since we are assigning the result of a GT_MKREFANY, "destAddr" must point to a refany. // TODO-CQ: we can do this without address-exposing the local on the LHS. GenTreeFlags indirFlags = GTF_EMPTY; - GenTree* destAddr = impGetNodeAddr(dest, impGetRefAnyClass(), CHECK_SPILL_ALL, &indirFlags); + GenTree* destAddr = impGetNodeAddr(dest, CHECK_SPILL_ALL, &indirFlags); GenTree* destAddrClone; destAddr = impCloneExpr(destAddr, NO_CLASS_HANDLE, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); @@ -1113,7 +1113,7 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTree* src, unsigned // will return its address. Otherwise, address of a temporary assigned // the value of "val" will be returned. // -GenTree* Compiler::impGetNodeAddr(GenTree* val, unsigned curLevel, GenTreeFlags* pDerefFlags) +GenTree* Compiler::impGetNodeAddr(GenTree* val, unsigned curLevel, GenTreeFlags* pDerefFlags) { switch (val->OperGet()) { From feb0accd17b2144786ed1968c649befee0415bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 6 May 2023 23:30:00 +0200 Subject: [PATCH 16/50] Simplify code --- src/coreclr/jit/importercalls.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 2bc2958682838..e0e4ffd1aab3b 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4390,14 +4390,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; GenTree* indir = gtNewLoadValueNode(type, layout, op2, flags); - if (varTypeIsStruct(type)) - { - return impAssignStruct(indir, op1, CHECK_SPILL_ALL); - } - else - { - return gtNewAssignNode(indir, op1); - } + return varTypeIsStruct(type) ? impAssignStruct(indir, op1, CHECK_SPILL_ALL) : gtNewAssignNode(indir, op1); } default: From 08e9ece3dda71c898466ead8a215af1ad0ebe91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 6 May 2023 23:41:02 +0200 Subject: [PATCH 17/50] Update importercalls.cpp --- src/coreclr/jit/importercalls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index e0e4ffd1aab3b..739feda92f728 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4107,12 +4107,12 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if (op1->OperIsIndir() && (fromSize != op1->AsIndir()->Size())) { unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); - impAssignTempGen(lclNum, op1, fromTypeHnd, CHECK_SPILL_ALL); + impAssignTempGen(lclNum, op1, CHECK_SPILL_ALL); addr = gtNewLclVarAddrNode(lclNum, TYP_BYREF); } else { - addr = impGetNodeAddr(op1, fromTypeHnd, CHECK_SPILL_ALL, &indirFlags); + addr = impGetNodeAddr(op1, CHECK_SPILL_ALL, &indirFlags); } return gtNewLoadValueNode(toType, toLayout, addr, indirFlags); } From 9204f803c448caa0d7bcbf1ac0b68ede2d8ddc87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 6 May 2023 23:42:32 +0200 Subject: [PATCH 18/50] Update importer.cpp --- src/coreclr/jit/importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 1acc4cbdee297..d1f2cf183ccff 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1007,7 +1007,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, GenTreeFlags indirFlags = GTF_EMPTY; GenTree* destAddr = impGetNodeAddr(dest, CHECK_SPILL_ALL, &indirFlags); GenTree* destAddrClone; - destAddr = impCloneExpr(destAddr, NO_CLASS_HANDLE, curLevel, + destAddr = impCloneExpr(destAddr, &destAddrClone, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); assert(OFFSETOF__CORINFO_TypedReference__dataPtr == 0); From 36eecf965ebc95a0a500e96ea49d9d03ea3c9621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 6 May 2023 23:52:06 +0200 Subject: [PATCH 19/50] Remove redundant nullchecks --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 739feda92f728..591a1bc47d5ed 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4047,7 +4047,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTree* op1 = impPopStack().val; if ((fromTypeHnd == toTypeHnd) || (varTypeIsIntegral(fromType) && varTypeIsIntegral(toType)) || - (fromLayout != nullptr && toLayout != nullptr && ClassLayout::AreCompatible(fromLayout, toLayout))) + ClassLayout::AreCompatible(fromLayout, toLayout)) { // Handle matching handles, integrals or compatible struct layouts where we can simply return op1 return op1; From 3ed32adb9540282105fe42026b27423bbaf37320 Mon Sep 17 00:00:00 2001 From: petris Date: Sun, 7 May 2023 00:10:10 +0200 Subject: [PATCH 20/50] Format code --- src/coreclr/jit/importer.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index d1f2cf183ccff..e0fac38dc609a 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1007,8 +1007,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, GenTreeFlags indirFlags = GTF_EMPTY; GenTree* destAddr = impGetNodeAddr(dest, CHECK_SPILL_ALL, &indirFlags); GenTree* destAddrClone; - destAddr = impCloneExpr(destAddr, &destAddrClone, curLevel, - pAfterStmt DEBUGARG("MKREFANY assignment")); + destAddr = impCloneExpr(destAddr, &destAddrClone, curLevel, pAfterStmt DEBUGARG("MKREFANY assignment")); assert(OFFSETOF__CORINFO_TypedReference__dataPtr == 0); assert(destAddr->gtType == TYP_I_IMPL || destAddr->gtType == TYP_BYREF); @@ -3528,8 +3527,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) // TODO: deal with flags GenTreeFlags indirFlags = GTF_EMPTY; - op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, - impGetNodeAddr(exprToBox, CHECK_SPILL_ALL, &indirFlags)); + op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, CHECK_SPILL_ALL, &indirFlags)); } /* Push the result back on the stack, */ From 6cbaa45c1e694863b25d8b3ce65379ebfe0d9fb9 Mon Sep 17 00:00:00 2001 From: petris Date: Mon, 8 May 2023 22:07:22 +0200 Subject: [PATCH 21/50] Mark the local as having new uses --- src/coreclr/jit/compiler.cpp | 4 ++-- src/coreclr/jit/fginline.cpp | 2 +- src/coreclr/jit/gentree.cpp | 10 +++++----- src/coreclr/jit/gentree.h | 2 +- src/coreclr/jit/importer.cpp | 2 ++ 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index bc1b7b0d0dbb4..e437e0b267676 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -9673,9 +9673,9 @@ void cTreeFlags(Compiler* comp, GenTree* tree) { chars += printf("[VAR_ITERATOR]"); } - if (tree->gtFlags & GTF_VAR_CLONED) + if (tree->gtFlags & GTF_VAR_MOREUSES) { - chars += printf("[VAR_CLONED]"); + chars += printf("[VAR_MOREUSES]"); } if (!comp->lvaGetDesc(tree->AsLclVarCommon())->lvPromoted) { diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 856432f00ea83..98ba021f74eb9 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -1638,7 +1638,7 @@ Statement* Compiler::fgInlinePrependStatements(InlineInfo* inlineInfo) GenTree* argSingleUseNode = argInfo.argBashTmpNode; - if ((argSingleUseNode != nullptr) && !(argSingleUseNode->gtFlags & GTF_VAR_CLONED) && argIsSingleDef) + if ((argSingleUseNode != nullptr) && !(argSingleUseNode->gtFlags & GTF_VAR_MOREUSES) && argIsSingleDef) { // Change the temp in-place to the actual argument. // We currently do not support this for struct arguments, so it must not be a GT_BLK. diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 378bfea581b19..cd158efc2cea7 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -8800,7 +8800,7 @@ GenTree* Compiler::gtClone(GenTree* tree, bool complexOK) FINISH_CLONING_LCL_NODE: // Remember that the local node has been cloned. Below the flag will be set on 'copy' too. - tree->gtFlags |= GTF_VAR_CLONED; + tree->gtFlags |= GTF_VAR_MOREUSES; copy->AsLclVarCommon()->SetSsaNum(tree->AsLclVarCommon()->GetSsaNum()); assert(!copy->AsLclVarCommon()->HasSsaName() || ((copy->gtFlags & GTF_VAR_DEF) == 0)); break; @@ -8970,7 +8970,7 @@ GenTree* Compiler::gtCloneExpr( else { // Remember that the local node has been cloned. The flag will be set on 'copy' as well. - tree->gtFlags |= GTF_VAR_CLONED; + tree->gtFlags |= GTF_VAR_MOREUSES; copy = gtNewLclvNode(tree->AsLclVar()->GetLclNum(), tree->gtType DEBUGARG(tree->AsLclVar()->gtLclILoffs)); copy->AsLclVarCommon()->SetSsaNum(tree->AsLclVarCommon()->GetSsaNum()); @@ -8985,7 +8985,7 @@ GenTree* Compiler::gtCloneExpr( else { // Remember that the local node has been cloned. The flag will be set on 'copy' as well. - tree->gtFlags |= GTF_VAR_CLONED; + tree->gtFlags |= GTF_VAR_MOREUSES; copy = new (this, GT_LCL_FLD) GenTreeLclFld(GT_LCL_FLD, tree->TypeGet(), tree->AsLclFld()->GetLclNum(), tree->AsLclFld()->GetLclOffs(), tree->AsLclFld()->GetLayout()); @@ -9049,14 +9049,14 @@ GenTree* Compiler::gtCloneExpr( { case GT_STORE_LCL_VAR: // Remember that the local node has been cloned. The flag will be set on 'copy' as well. - tree->gtFlags |= GTF_VAR_CLONED; + tree->gtFlags |= GTF_VAR_MOREUSES; copy = new (this, GT_STORE_LCL_VAR) GenTreeLclVar(tree->TypeGet(), tree->AsLclVar()->GetLclNum(), tree->AsLclVar()->Data()); break; case GT_STORE_LCL_FLD: // Remember that the local node has been cloned. The flag will be set on 'copy' as well. - tree->gtFlags |= GTF_VAR_CLONED; + tree->gtFlags |= GTF_VAR_MOREUSES; copy = new (this, GT_STORE_LCL_FLD) GenTreeLclFld(tree->TypeGet(), tree->AsLclFld()->GetLclNum(), tree->AsLclFld()->GetLclOffs(), tree->AsLclFld()->Data(), tree->AsLclFld()->GetLayout()); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index ee6948a36c86e..75dbf6449dc87 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -455,7 +455,7 @@ enum GenTreeFlags : unsigned int GTF_LIVENESS_MASK = GTF_VAR_DEF | GTF_VAR_USEASG | GTF_VAR_DEATH_MASK, GTF_VAR_ITERATOR = 0x01000000, // GT_LCL_VAR -- this is a iterator reference in the loop condition - GTF_VAR_CLONED = 0x00800000, // GT_LCL_VAR -- this node has been cloned or is a clone + GTF_VAR_MOREUSES = 0x00800000, // GT_LCL_VAR -- this node has additonal uses, for example due to cloning GTF_VAR_CONTEXT = 0x00400000, // GT_LCL_VAR -- this node is part of a runtime lookup GTF_VAR_EXPLICIT_INIT = 0x00200000, // GT_LCL_VAR -- this node is an "explicit init" store. Valid until rationalization. diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index e0fac38dc609a..14ba724439603 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1126,9 +1126,11 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, unsigned curLevel, GenTreeFlags* break; case GT_LCL_VAR: + val->gtFlags |= GTF_VAR_MOREUSES; return gtNewLclVarAddrNode(val->AsLclVar()->GetLclNum(), TYP_BYREF); case GT_LCL_FLD: + val->gtFlags |= GTF_VAR_MOREUSES; return gtNewLclAddrNode(val->AsLclFld()->GetLclNum(), val->AsLclFld()->GetLclOffs(), TYP_BYREF); case GT_COMMA: From 6c758ee538aa263b989f04009ce3135846c59b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Tue, 9 May 2023 01:29:55 +0200 Subject: [PATCH 22/50] Update targetosarch.h --- src/coreclr/inc/targetosarch.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/coreclr/inc/targetosarch.h b/src/coreclr/inc/targetosarch.h index c4742b08c8e14..b569afa269882 100644 --- a/src/coreclr/inc/targetosarch.h +++ b/src/coreclr/inc/targetosarch.h @@ -35,11 +35,7 @@ class TargetOS class TargetArchitecture { public: -#ifdef TARGET_64BIT - static const bool Is64Bit = true; -#else - static const bool Is64Bit = false; -#endif + static const bool Is64Bit = TARGET_POINTER_SIZE == 8; #ifdef TARGET_ARM static const bool IsX86 = false; static const bool IsX64 = false; From 13ece70b14c1e8367b86c19243ba1aae76771755 Mon Sep 17 00:00:00 2001 From: petris Date: Tue, 9 May 2023 01:47:04 +0200 Subject: [PATCH 23/50] Reword comments --- src/coreclr/jit/gentree.cpp | 2 +- src/coreclr/jit/importer.cpp | 10 +++++----- src/coreclr/jit/importervectorization.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index cd158efc2cea7..1d2e2c5637b4c 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -16201,7 +16201,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, // helper needs pointer to struct, not struct itself if (pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT) { - // TODO: deal with flags + // TODO: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; assg = impGetNodeAddr(assg, CHECK_SPILL_ALL, &indirFlags); } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 14ba724439603..732aa3eccd3f3 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -881,7 +881,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, WellKnownArg wellKnownArgType = srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; - // TODO: deal with flags + // TODO: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; GenTree* destAddr = impGetNodeAddr(dest, CHECK_SPILL_ALL, &indirFlags); NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); @@ -985,7 +985,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, if (call->ShouldHaveRetBufArg()) { // insert the return value buffer into the argument list as first byref parameter after 'this' - // TODO: deal with flags + // TODO: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; GenTree* destAddr = impGetNodeAddr(dest, CHECK_SPILL_ALL, &indirFlags); call->gtArgs.InsertAfterThisOrFirst(this, @@ -3172,7 +3172,7 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, GenTree* objToBox = impPopStack().val; // Spill struct to get its address (to access hasValue field) - // TODO: deal with flags + // TODO: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; objToBox = impGetNodeAddr(objToBox, CHECK_SPILL_ALL, &indirFlags); @@ -3527,7 +3527,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) return; } - // TODO: deal with flags + // TODO: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, CHECK_SPILL_ALL, &indirFlags)); } @@ -9160,7 +9160,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) BADCODE("top of stack must be a value type"); } - // TODO: deal with flags + // TODO: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; obj = impGetNodeAddr(obj, CHECK_SPILL_ALL, &indirFlags); } diff --git a/src/coreclr/jit/importervectorization.cpp b/src/coreclr/jit/importervectorization.cpp index 0c610f29ed97c..0a0e6dfb0565a 100644 --- a/src/coreclr/jit/importervectorization.cpp +++ b/src/coreclr/jit/importervectorization.cpp @@ -797,7 +797,7 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* if (unrolled != nullptr) { // We succeeded, fill the placeholders: - // TODO: deal with flags + // TODO: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; impAssignTempGen(spanObjRef, impGetNodeAddr(spanObj, CHECK_SPILL_NONE, &indirFlags)); impAssignTempGen(spanDataTmp, spanData); From d68aed9b3cb2c173ad588c6b38cec0758aed5086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Tue, 9 May 2023 01:50:14 +0200 Subject: [PATCH 24/50] Update targetosarch.h --- src/coreclr/inc/targetosarch.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/inc/targetosarch.h b/src/coreclr/inc/targetosarch.h index b569afa269882..c4742b08c8e14 100644 --- a/src/coreclr/inc/targetosarch.h +++ b/src/coreclr/inc/targetosarch.h @@ -35,7 +35,11 @@ class TargetOS class TargetArchitecture { public: - static const bool Is64Bit = TARGET_POINTER_SIZE == 8; +#ifdef TARGET_64BIT + static const bool Is64Bit = true; +#else + static const bool Is64Bit = false; +#endif #ifdef TARGET_ARM static const bool IsX86 = false; static const bool IsX64 = false; From 873a914b445746809abd8975109cab8aef158a80 Mon Sep 17 00:00:00 2001 From: petris Date: Tue, 9 May 2023 18:49:03 +0200 Subject: [PATCH 25/50] Handle small types --- src/coreclr/jit/gentree.h | 4 ++-- src/coreclr/jit/importer.cpp | 19 ++++++++++++------- src/coreclr/jit/importercalls.cpp | 25 ++++++++++++++++++++----- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 75dbf6449dc87..65d15ac70deb4 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -493,8 +493,8 @@ enum GenTreeFlags : unsigned int GTF_IND_NONNULL = 0x00400000, // GT_IND -- the indirection never returns null (zero) GTF_IND_INITCLASS = 0x00200000, // OperIsIndir() -- the indirection requires preceding static cctor - GTF_IND_FLAGS = GTF_IND_VOLATILE | GTF_IND_NONFAULTING | GTF_IND_UNALIGNED | GTF_IND_INVARIANT | - GTF_IND_NONNULL | GTF_IND_TGT_NOT_HEAP | GTF_IND_TGT_HEAP | GTF_IND_INITCLASS, + GTF_IND_COPYABLE_FLAGS = GTF_IND_VOLATILE | GTF_IND_NONFAULTING | GTF_IND_UNALIGNED | GTF_IND_INITCLASS, + GTF_IND_FLAGS = GTF_IND_COPYABLE_FLAGS | GTF_IND_NONNULL | GTF_IND_TGT_NOT_HEAP | GTF_IND_TGT_HEAP | GTF_IND_INVARIANT, GTF_ADDRMODE_NO_CSE = 0x80000000, // GT_ADD/GT_MUL/GT_LSH -- Do not CSE this node only, forms complex // addressing mode diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 732aa3eccd3f3..e822fe0cd63ab 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -881,7 +881,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, WellKnownArg wellKnownArgType = srcCall->ShouldHaveRetBufArg() ? WellKnownArg::RetBuffer : WellKnownArg::None; - // TODO: verify if flags matter here + // TODO-Bug?: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; GenTree* destAddr = impGetNodeAddr(dest, CHECK_SPILL_ALL, &indirFlags); NewCallArg newArg = NewCallArg::Primitive(destAddr).WellKnown(wellKnownArgType); @@ -985,7 +985,7 @@ GenTree* Compiler::impAssignStruct(GenTree* dest, if (call->ShouldHaveRetBufArg()) { // insert the return value buffer into the argument list as first byref parameter after 'this' - // TODO: verify if flags matter here + // TODO-Bug?: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; GenTree* destAddr = impGetNodeAddr(dest, CHECK_SPILL_ALL, &indirFlags); call->gtArgs.InsertAfterThisOrFirst(this, @@ -1105,7 +1105,8 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTree* src, unsigned // val - The value in question // curLevel - Stack level for spilling // pDerefFlags - Flags to be used on dereference, nullptr when -// the address won't be dereferenced +// the address won't be dereferenced. Returned flags +// are included in the GTF_IND_COPYABLE_FLAGS mask. // // Return Value: // In case "val" can represent locations (is an indirection/local), @@ -1114,13 +1115,17 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, GenTree* src, unsigned // GenTree* Compiler::impGetNodeAddr(GenTree* val, unsigned curLevel, GenTreeFlags* pDerefFlags) { + if (pDerefFlags != nullptr) + { + *pDerefFlags = GTF_EMPTY; + } switch (val->OperGet()) { case GT_BLK: case GT_IND: if (pDerefFlags != nullptr) { - *pDerefFlags |= (val->gtFlags & GTF_IND_FLAGS); + *pDerefFlags = val->gtFlags & GTF_IND_COPYABLE_FLAGS; return val->AsIndir()->Addr(); } break; @@ -3172,7 +3177,7 @@ int Compiler::impBoxPatternMatch(CORINFO_RESOLVED_TOKEN* pResolvedToken, GenTree* objToBox = impPopStack().val; // Spill struct to get its address (to access hasValue field) - // TODO: verify if flags matter here + // TODO-Bug?: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; objToBox = impGetNodeAddr(objToBox, CHECK_SPILL_ALL, &indirFlags); @@ -3527,7 +3532,7 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken) return; } - // TODO: verify if flags matter here + // TODO-Bug?: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; op1 = gtNewHelperCallNode(boxHelper, TYP_REF, op2, impGetNodeAddr(exprToBox, CHECK_SPILL_ALL, &indirFlags)); } @@ -9160,7 +9165,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) BADCODE("top of stack must be a value type"); } - // TODO: verify if flags matter here + // TODO-Bug?: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; obj = impGetNodeAddr(obj, CHECK_SPILL_ALL, &indirFlags); } diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 591a1bc47d5ed..6cfe4b652a45a 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4046,10 +4046,18 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTree* op1 = impPopStack().val; - if ((fromTypeHnd == toTypeHnd) || (varTypeIsIntegral(fromType) && varTypeIsIntegral(toType)) || - ClassLayout::AreCompatible(fromLayout, toLayout)) + var_types valType = op1->gtType; + GenTree* effectiveVal = op1->gtEffectiveVal(); + if (effectiveVal->OperIs(GT_LCL_VAR)) { - // Handle matching handles, integrals or compatible struct layouts where we can simply return op1 + valType = lvaGetDesc(effectiveVal->AsLclVar()->GetLclNum())->TypeGet(); + } + + if ((fromTypeHnd == toTypeHnd) || ClassLayout::AreCompatible(fromLayout, toLayout) || + (varTypeIsIntegral(fromType) && varTypeIsIntegral(toType) && + ((genTypeSize(valType) == fromSize) || (varTypeIsSigned(valType) == varTypeIsSigned(toType))))) + { + // Handle matching handles, compatible struct layouts or integrals where we can simply return op1 return op1; } @@ -4104,16 +4112,23 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTree* addr; GenTreeFlags indirFlags = GTF_EMPTY; - if (op1->OperIsIndir() && (fromSize != op1->AsIndir()->Size())) + if (varTypeIsIntegral(valType) && (genTypeSize(valType) < fromSize)) { unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); impAssignTempGen(lclNum, op1, CHECK_SPILL_ALL); - addr = gtNewLclVarAddrNode(lclNum, TYP_BYREF); + addr = gtNewLclVarAddrNode(lclNum, TYP_I_IMPL); } else { addr = impGetNodeAddr(op1, CHECK_SPILL_ALL, &indirFlags); } + + if (info.compCompHnd->getClassAlignmentRequirement(fromTypeHnd) < + info.compCompHnd->getClassAlignmentRequirement(toTypeHnd)) + { + indirFlags |= GTF_IND_UNALIGNED; + } + return gtNewLoadValueNode(toType, toLayout, addr, indirFlags); } From c9b274c7498183a35b05b386e2a63747daa5153c Mon Sep 17 00:00:00 2001 From: petris Date: Tue, 9 May 2023 19:13:31 +0200 Subject: [PATCH 26/50] Reformat code --- src/coreclr/jit/importercalls.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 6cfe4b652a45a..89556af794a92 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4046,8 +4046,8 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTree* op1 = impPopStack().val; - var_types valType = op1->gtType; - GenTree* effectiveVal = op1->gtEffectiveVal(); + var_types valType = op1->gtType; + GenTree* effectiveVal = op1->gtEffectiveVal(); if (effectiveVal->OperIs(GT_LCL_VAR)) { valType = lvaGetDesc(effectiveVal->AsLclVar()->GetLclNum())->TypeGet(); @@ -4055,7 +4055,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if ((fromTypeHnd == toTypeHnd) || ClassLayout::AreCompatible(fromLayout, toLayout) || (varTypeIsIntegral(fromType) && varTypeIsIntegral(toType) && - ((genTypeSize(valType) == fromSize) || (varTypeIsSigned(valType) == varTypeIsSigned(toType))))) + ((genTypeSize(valType) == fromSize) || (varTypeIsSigned(valType) == varTypeIsSigned(toType))))) { // Handle matching handles, compatible struct layouts or integrals where we can simply return op1 return op1; From 290bf27247e8b247a9febc866f19b2e46b851637 Mon Sep 17 00:00:00 2001 From: petris Date: Tue, 9 May 2023 23:24:31 +0200 Subject: [PATCH 27/50] Commit missed changes --- src/coreclr/jit/gentree.cpp | 2 +- src/coreclr/jit/importervectorization.cpp | 2 +- src/coreclr/jit/promotiondecomposition.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 1d2e2c5637b4c..8a7c7359e2b17 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -16201,7 +16201,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, // helper needs pointer to struct, not struct itself if (pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT) { - // TODO: verify if flags matter here + // TODO-Bug?: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; assg = impGetNodeAddr(assg, CHECK_SPILL_ALL, &indirFlags); } diff --git a/src/coreclr/jit/importervectorization.cpp b/src/coreclr/jit/importervectorization.cpp index 0a0e6dfb0565a..b37366e6fc551 100644 --- a/src/coreclr/jit/importervectorization.cpp +++ b/src/coreclr/jit/importervectorization.cpp @@ -797,7 +797,7 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* if (unrolled != nullptr) { // We succeeded, fill the placeholders: - // TODO: verify if flags matter here + // TODO-Bug?: verify if flags matter here GenTreeFlags indirFlags = GTF_EMPTY; impAssignTempGen(spanObjRef, impGetNodeAddr(spanObj, CHECK_SPILL_NONE, &indirFlags)); impAssignTempGen(spanDataTmp, spanData); diff --git a/src/coreclr/jit/promotiondecomposition.cpp b/src/coreclr/jit/promotiondecomposition.cpp index 076e3f720d67a..4ffd25b2c340d 100644 --- a/src/coreclr/jit/promotiondecomposition.cpp +++ b/src/coreclr/jit/promotiondecomposition.cpp @@ -841,13 +841,13 @@ class DecompositionPlan { addr = m_dst->gtGetOp1(); indirFlags = - m_dst->gtFlags & (GTF_IND_VOLATILE | GTF_IND_NONFAULTING | GTF_IND_UNALIGNED | GTF_IND_INITCLASS); + m_dst->gtFlags & GTF_IND_COPYABLE_FLAGS; } else if (m_src->OperIs(GT_BLK)) { addr = m_src->gtGetOp1(); indirFlags = - m_src->gtFlags & (GTF_IND_VOLATILE | GTF_IND_NONFAULTING | GTF_IND_UNALIGNED | GTF_IND_INITCLASS); + m_src->gtFlags & GTF_IND_COPYABLE_FLAGS; } int numAddrUses = 0; From 3d77bb01422fba15531d094c60566c34b45c88f5 Mon Sep 17 00:00:00 2001 From: petris Date: Tue, 9 May 2023 23:34:04 +0200 Subject: [PATCH 28/50] Fix check --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 89556af794a92..11774a340ff07 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4055,7 +4055,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if ((fromTypeHnd == toTypeHnd) || ClassLayout::AreCompatible(fromLayout, toLayout) || (varTypeIsIntegral(fromType) && varTypeIsIntegral(toType) && - ((genTypeSize(valType) == fromSize) || (varTypeIsSigned(valType) == varTypeIsSigned(toType))))) + (!varTypeIsSmall(valType) || (varTypeIsSigned(valType) == varTypeIsSigned(toType))))) { // Handle matching handles, compatible struct layouts or integrals where we can simply return op1 return op1; From 4ab78a08405f2bce9ed7654fb1bef041329d8115 Mon Sep 17 00:00:00 2001 From: petris Date: Wed, 10 May 2023 00:57:33 +0200 Subject: [PATCH 29/50] Format code --- src/coreclr/jit/promotiondecomposition.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/promotiondecomposition.cpp b/src/coreclr/jit/promotiondecomposition.cpp index 9f7af47100aee..c369fe4096045 100644 --- a/src/coreclr/jit/promotiondecomposition.cpp +++ b/src/coreclr/jit/promotiondecomposition.cpp @@ -837,15 +837,13 @@ class DecompositionPlan if (m_dst->OperIs(GT_BLK)) { - addr = m_dst->gtGetOp1(); - indirFlags = - m_dst->gtFlags & GTF_IND_COPYABLE_FLAGS; + addr = m_dst->gtGetOp1(); + indirFlags = m_dst->gtFlags & GTF_IND_COPYABLE_FLAGS; } else if (m_src->OperIs(GT_BLK)) { - addr = m_src->gtGetOp1(); - indirFlags = - m_src->gtFlags & GTF_IND_COPYABLE_FLAGS; + addr = m_src->gtGetOp1(); + indirFlags = m_src->gtFlags & GTF_IND_COPYABLE_FLAGS; } int numAddrUses = 0; From e1911ee21e21aad76f22a082dc4b24ec13ce7d08 Mon Sep 17 00:00:00 2001 From: petris Date: Wed, 10 May 2023 21:43:39 +0200 Subject: [PATCH 30/50] Add tests for small types and misalignment --- .../tests/UnsafeTests.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 8d596482766a4..dd095307379c4 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1133,9 +1133,90 @@ public static unsafe void BitCast() Assert.Throws(() => Unsafe.BitCast(5)); Assert.Throws(() => Unsafe.BitCast(empty1)); + + byte b = 255; + sbyte sb = -1; + + Assert.Equal(255, (long)Unsafe.BitCast(sb)); + Assert.Equal(-1, (long)Unsafe.BitCast(b)); + Assert.Equal(ushort.MaxValue, (long)Unsafe.BitCast(sb)); + Assert.Equal(255, (long)Unsafe.BitCast(b)); + Assert.Equal(uint.MaxValue, (long)Unsafe.BitCast(sb)); + Assert.Equal(255, (long)Unsafe.BitCast(b)); + Assert.Equal(ulong.MaxValue, Unsafe.BitCast(sb)); + Assert.Equal(255, Unsafe.BitCast(b)); + + S2 s2 = BitConverter.IsLittleEndian ? new S2(255, 0) : new S2(0, 255); + S4 s4 = BitConverter.IsLittleEndian ? new S4(255, 0, 0, 0) : new S4(0, 0, 0, 255); + S8 s8 = BitConverter.IsLittleEndian ? new S8(255, 0, 0, 0, 0, 0, 0, 0) : new S8(0, 0, 0, 0, 0, 0, 0, 255); + + Assert.Equal(s2, Unsafe.BitCast(b)); + Assert.Equal(s2, Unsafe.BitCast(b)); + Assert.Equal(new S2(255, 255), Unsafe.BitCast(sb)); + + Assert.Equal(s4, Unsafe.BitCast(b)); + Assert.Equal(s4, Unsafe.BitCast(b)); + Assert.Equal(new S4(255, 255, 255, 255), Unsafe.BitCast(sb)); + + Assert.Equal(s8, Unsafe.BitCast(b)); + Assert.Equal(s8, Unsafe.BitCast(b)); + Assert.Equal(new S8(255, 255, 255, 255, 255, 255, 255, 255), Unsafe.BitCast(sb)); + + Assert.Equal(255, Unsafe.BitCast(s2)); + Assert.Equal(255, Unsafe.BitCast(s2)); + Assert.Equal(255, Unsafe.BitCast(s4)); + Assert.Equal(255, Unsafe.BitCast(s4)); + Assert.Equal(255, Unsafe.BitCast(s8)); + Assert.Equal(255, Unsafe.BitCast(s8)); + + byte* misalignedPtr = (byte*)NativeMemory.AllocAligned(9, 64) + 1; + new Span(misalignedPtr, 8).Clear(); + + *misalignedPtr = 255; + + Assert.Equal(s2, Unsafe.BitCast(*misalignedPtr)); + Assert.Equal(s2, Unsafe.BitCast(*misalignedPtr)); + Assert.Equal(new S2(255, 255), Unsafe.BitCast(*(sbyte*)misalignedPtr)); + + Assert.Equal(s4, Unsafe.BitCast(*misalignedPtr)); + Assert.Equal(s4, Unsafe.BitCast(*misalignedPtr)); + Assert.Equal(new S4(255, 255, 255, 255), Unsafe.BitCast(*(sbyte*)misalignedPtr)); + + Assert.Equal(s8, Unsafe.BitCast(*misalignedPtr)); + Assert.Equal(s8, Unsafe.BitCast(*misalignedPtr)); + Assert.Equal(new S8(255, 255, 255, 255, 255, 255, 255, 255), Unsafe.BitCast(*(sbyte*)misalignedPtr)); + + *(S2*)misalignedPtr = s2; + Assert.Equal(255, Unsafe.BitCast(*(S2*)misalignedPtr)); + Assert.Equal(255, Unsafe.BitCast(*(S2*)misalignedPtr)); + *(S4*)misalignedPtr = s4; + Assert.Equal(255, Unsafe.BitCast(*(S4*)misalignedPtr)); + Assert.Equal(255, Unsafe.BitCast(*(S4*)misalignedPtr)); + *(S8*)misalignedPtr = s8; + Assert.Equal(255, Unsafe.BitCast(*(S8*)misalignedPtr)); + Assert.Equal(255, Unsafe.BitCast(*(S8*)misalignedPtr)); + + Half h = Unsafe.ReadUnaligned(ref Unsafe.As(ref s2)); + float s = Unsafe.ReadUnaligned(ref Unsafe.As(ref s4)); + double d = Unsafe.ReadUnaligned(ref Unsafe.As(ref s8)); + + Assert.Equal(h, Unsafe.BitCast(s2)); + Assert.Equal(s, Unsafe.BitCast(s4)); + Assert.Equal(d, Unsafe.BitCast(s8)); + + *(S2*)misalignedPtr = s2; + Assert.Equal(h, Unsafe.BitCast(*(S2*)misalignedPtr)); + *(S4*)misalignedPtr = s4; + Assert.Equal(s, Unsafe.BitCast(*(S4*)misalignedPtr)); + *(S8*)misalignedPtr = s8; + Assert.Equal(d, Unsafe.BitCast(*(S8*)misalignedPtr)); } } + [StructLayout(LayoutKind.Sequential)] public record struct S2(byte a, byte b); + [StructLayout(LayoutKind.Sequential)] public record struct S4(byte a, byte b, byte c, byte d); + [StructLayout(LayoutKind.Sequential)] public record struct S8(byte a, byte b, byte c, byte d, byte e, byte f, byte f, byte h); + [StructLayout(LayoutKind.Explicit)] public struct Byte4 { From 38f10fc68dda151f097878bdf9ebb78c073e1b51 Mon Sep 17 00:00:00 2001 From: petris Date: Wed, 10 May 2023 22:09:47 +0200 Subject: [PATCH 31/50] Fix a typo, add more tests --- .../tests/UnsafeTests.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index dd095307379c4..676bc23ebbdd1 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1139,10 +1139,16 @@ public static unsafe void BitCast() Assert.Equal(255, (long)Unsafe.BitCast(sb)); Assert.Equal(-1, (long)Unsafe.BitCast(b)); + + Assert.Equal(255, (long)Unsafe.BitCast(b)); Assert.Equal(ushort.MaxValue, (long)Unsafe.BitCast(sb)); Assert.Equal(255, (long)Unsafe.BitCast(b)); + + Assert.Equal(255, (long)Unsafe.BitCast(b)); Assert.Equal(uint.MaxValue, (long)Unsafe.BitCast(sb)); Assert.Equal(255, (long)Unsafe.BitCast(b)); + + Assert.Equal(255, Unsafe.BitCast(b)); Assert.Equal(ulong.MaxValue, Unsafe.BitCast(sb)); Assert.Equal(255, Unsafe.BitCast(b)); @@ -1215,7 +1221,7 @@ public static unsafe void BitCast() [StructLayout(LayoutKind.Sequential)] public record struct S2(byte a, byte b); [StructLayout(LayoutKind.Sequential)] public record struct S4(byte a, byte b, byte c, byte d); - [StructLayout(LayoutKind.Sequential)] public record struct S8(byte a, byte b, byte c, byte d, byte e, byte f, byte f, byte h); + [StructLayout(LayoutKind.Sequential)] public record struct S8(byte a, byte b, byte c, byte d, byte e, byte f, byte g, byte h); [StructLayout(LayoutKind.Explicit)] public struct Byte4 From b69aaa765ccd2aab3c0014c3fdb2ab0a1e0e6de1 Mon Sep 17 00:00:00 2001 From: petris Date: Thu, 11 May 2023 03:04:49 +0200 Subject: [PATCH 32/50] Adjust small type handling --- src/coreclr/jit/importercalls.cpp | 35 ++++++++++------ .../tests/UnsafeTests.cs | 42 +++++++++---------- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 5d45de5ecdd22..1b0334060fc42 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4058,10 +4058,15 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, assert(sig->sigInst.methInstCount == 2); CORINFO_CLASS_HANDLE fromTypeHnd = sig->sigInst.methInst[0]; + ClassLayout* fromLayout = nullptr; + var_types fromType = TypeHandleToVarType(fromTypeHnd, &fromLayout); + CORINFO_CLASS_HANDLE toTypeHnd = sig->sigInst.methInst[1]; + ClassLayout* toLayout = nullptr; + var_types toType = TypeHandleToVarType(toTypeHnd, &toLayout); - unsigned fromSize = info.compCompHnd->getClassSize(fromTypeHnd); - unsigned toSize = info.compCompHnd->getClassSize(toTypeHnd); + unsigned fromSize = fromLayout != nullptr ? fromLayout->GetSize() : info.compCompHnd->getClassSize(fromTypeHnd); + unsigned toSize = toLayout != nullptr ? toLayout->GetSize() : info.compCompHnd->getClassSize(toTypeHnd); // Runtime requires all types to be at least 1-byte assert((fromSize != 0) && (toSize != 0)); @@ -4072,16 +4077,13 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, return nullptr; } - ClassLayout* fromLayout = nullptr; - var_types fromType = TypeHandleToVarType(fromTypeHnd, &fromLayout); - - ClassLayout* toLayout = nullptr; - var_types toType = TypeHandleToVarType(toTypeHnd, &toLayout); - assert((fromType != TYP_REF) && (toType != TYP_REF)); GenTree* op1 = impPopStack().val; + op1 = impImplicitR4orR8Cast(op1, fromType); + op1 = impImplicitIorI4Cast(op1, fromType); + var_types valType = op1->gtType; GenTree* effectiveVal = op1->gtEffectiveVal(); if (effectiveVal->OperIs(GT_LCL_VAR)) @@ -4089,11 +4091,20 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, valType = lvaGetDesc(effectiveVal->AsLclVar()->GetLclNum())->TypeGet(); } - if ((fromTypeHnd == toTypeHnd) || ClassLayout::AreCompatible(fromLayout, toLayout) || - (varTypeIsIntegral(fromType) && varTypeIsIntegral(toType) && - (!varTypeIsSmall(valType) || (varTypeIsSigned(valType) == varTypeIsSigned(toType))))) + // Handle matching handles, compatible struct layouts or integrals where we can simply return op1 + if (varTypeIsSmall(toType) + { + if (genActualTypeIsInt(valType)) + { + if (fgCastNeeded(op1, toType)) + { + op1 = gtNewCastNode(TYP_INT, op1, false, toType); + } + return op1; + } + } + else if (((toType != TYP_STRUCT) && (genActualType(valType) == toType)) || ClassLayout::AreCompatible(fromLayout, toLayout)) { - // Handle matching handles, compatible struct layouts or integrals where we can simply return op1 return op1; } diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 676bc23ebbdd1..261eca03debfd 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1137,20 +1137,20 @@ public static unsafe void BitCast() byte b = 255; sbyte sb = -1; - Assert.Equal(255, (long)Unsafe.BitCast(sb)); - Assert.Equal(-1, (long)Unsafe.BitCast(b)); + Assert.Equal(255l, (long)Unsafe.BitCast(sb)); + Assert.Equal(-1l, (long)Unsafe.BitCast(b)); - Assert.Equal(255, (long)Unsafe.BitCast(b)); + Assert.Equal(255l, (long)Unsafe.BitCast(b)); Assert.Equal(ushort.MaxValue, (long)Unsafe.BitCast(sb)); - Assert.Equal(255, (long)Unsafe.BitCast(b)); + Assert.Equal(255l, (long)Unsafe.BitCast(b)); - Assert.Equal(255, (long)Unsafe.BitCast(b)); + Assert.Equal(255l, (long)Unsafe.BitCast(b)); Assert.Equal(uint.MaxValue, (long)Unsafe.BitCast(sb)); - Assert.Equal(255, (long)Unsafe.BitCast(b)); + Assert.Equal(255l, (long)Unsafe.BitCast(b)); - Assert.Equal(255, Unsafe.BitCast(b)); + Assert.Equal(255ul, Unsafe.BitCast(b)); Assert.Equal(ulong.MaxValue, Unsafe.BitCast(sb)); - Assert.Equal(255, Unsafe.BitCast(b)); + Assert.Equal(255l, Unsafe.BitCast(b)); S2 s2 = BitConverter.IsLittleEndian ? new S2(255, 0) : new S2(0, 255); S4 s4 = BitConverter.IsLittleEndian ? new S4(255, 0, 0, 0) : new S4(0, 0, 0, 255); @@ -1166,16 +1166,16 @@ public static unsafe void BitCast() Assert.Equal(s8, Unsafe.BitCast(b)); Assert.Equal(s8, Unsafe.BitCast(b)); - Assert.Equal(new S8(255, 255, 255, 255, 255, 255, 255, 255), Unsafe.BitCast(sb)); + Assert.Equal(new S8(255, 255, 255, 255, 255, 255, 255, 255), Unsafe.BitCast(sb)); - Assert.Equal(255, Unsafe.BitCast(s2)); - Assert.Equal(255, Unsafe.BitCast(s2)); - Assert.Equal(255, Unsafe.BitCast(s4)); + Assert.Equal((ushort)255, Unsafe.BitCast(s2)); + Assert.Equal((short)255, Unsafe.BitCast(s2)); + Assert.Equal(255u, Unsafe.BitCast(s4)); Assert.Equal(255, Unsafe.BitCast(s4)); - Assert.Equal(255, Unsafe.BitCast(s8)); - Assert.Equal(255, Unsafe.BitCast(s8)); + Assert.Equal(255ul, Unsafe.BitCast(s8)); + Assert.Equal(255l, Unsafe.BitCast(s8)); - byte* misalignedPtr = (byte*)NativeMemory.AllocAligned(9, 64) + 1; + byte* misalignedPtr = (byte*)NativeMemory.AlignedAlloc(9, 64) + 1; new Span(misalignedPtr, 8).Clear(); *misalignedPtr = 255; @@ -1190,17 +1190,17 @@ public static unsafe void BitCast() Assert.Equal(s8, Unsafe.BitCast(*misalignedPtr)); Assert.Equal(s8, Unsafe.BitCast(*misalignedPtr)); - Assert.Equal(new S8(255, 255, 255, 255, 255, 255, 255, 255), Unsafe.BitCast(*(sbyte*)misalignedPtr)); + Assert.Equal(new S8(255, 255, 255, 255, 255, 255, 255, 255), Unsafe.BitCast(*(sbyte*)misalignedPtr)); *(S2*)misalignedPtr = s2; - Assert.Equal(255, Unsafe.BitCast(*(S2*)misalignedPtr)); - Assert.Equal(255, Unsafe.BitCast(*(S2*)misalignedPtr)); + Assert.Equal((ushort)255, Unsafe.BitCast(*(S2*)misalignedPtr)); + Assert.Equal((short)255, Unsafe.BitCast(*(S2*)misalignedPtr)); *(S4*)misalignedPtr = s4; - Assert.Equal(255, Unsafe.BitCast(*(S4*)misalignedPtr)); + Assert.Equal(255u, Unsafe.BitCast(*(S4*)misalignedPtr)); Assert.Equal(255, Unsafe.BitCast(*(S4*)misalignedPtr)); *(S8*)misalignedPtr = s8; - Assert.Equal(255, Unsafe.BitCast(*(S8*)misalignedPtr)); - Assert.Equal(255, Unsafe.BitCast(*(S8*)misalignedPtr)); + Assert.Equal(255ul, Unsafe.BitCast(*(S8*)misalignedPtr)); + Assert.Equal(255l, Unsafe.BitCast(*(S8*)misalignedPtr)); Half h = Unsafe.ReadUnaligned(ref Unsafe.As(ref s2)); float s = Unsafe.ReadUnaligned(ref Unsafe.As(ref s4)); From 46c45cc068a01db6ba3b5ebf13c1c63e0e866bb5 Mon Sep 17 00:00:00 2001 From: petris Date: Thu, 11 May 2023 03:18:20 +0200 Subject: [PATCH 33/50] Fix typo --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 1b0334060fc42..9f7a7cbe68b24 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4092,7 +4092,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, } // Handle matching handles, compatible struct layouts or integrals where we can simply return op1 - if (varTypeIsSmall(toType) + if (varTypeIsSmall(toType)) { if (genActualTypeIsInt(valType)) { From 0c245fb2e4704f13510cdd00785f5bc8b4125abf Mon Sep 17 00:00:00 2001 From: petris Date: Thu, 11 May 2023 03:48:03 +0200 Subject: [PATCH 34/50] Fix warning --- .../tests/UnsafeTests.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 261eca03debfd..6579f7556cb0f 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1137,20 +1137,20 @@ public static unsafe void BitCast() byte b = 255; sbyte sb = -1; - Assert.Equal(255l, (long)Unsafe.BitCast(sb)); - Assert.Equal(-1l, (long)Unsafe.BitCast(b)); + Assert.Equal(255L, (long)Unsafe.BitCast(sb)); + Assert.Equal(-1L, (long)Unsafe.BitCast(b)); - Assert.Equal(255l, (long)Unsafe.BitCast(b)); + Assert.Equal(255L, (long)Unsafe.BitCast(b)); Assert.Equal(ushort.MaxValue, (long)Unsafe.BitCast(sb)); - Assert.Equal(255l, (long)Unsafe.BitCast(b)); + Assert.Equal(255L, (long)Unsafe.BitCast(b)); - Assert.Equal(255l, (long)Unsafe.BitCast(b)); + Assert.Equal(255L, (long)Unsafe.BitCast(b)); Assert.Equal(uint.MaxValue, (long)Unsafe.BitCast(sb)); - Assert.Equal(255l, (long)Unsafe.BitCast(b)); + Assert.Equal(255L, (long)Unsafe.BitCast(b)); - Assert.Equal(255ul, Unsafe.BitCast(b)); + Assert.Equal(255UL, Unsafe.BitCast(b)); Assert.Equal(ulong.MaxValue, Unsafe.BitCast(sb)); - Assert.Equal(255l, Unsafe.BitCast(b)); + Assert.Equal(255L, Unsafe.BitCast(b)); S2 s2 = BitConverter.IsLittleEndian ? new S2(255, 0) : new S2(0, 255); S4 s4 = BitConverter.IsLittleEndian ? new S4(255, 0, 0, 0) : new S4(0, 0, 0, 255); @@ -1170,10 +1170,10 @@ public static unsafe void BitCast() Assert.Equal((ushort)255, Unsafe.BitCast(s2)); Assert.Equal((short)255, Unsafe.BitCast(s2)); - Assert.Equal(255u, Unsafe.BitCast(s4)); + Assert.Equal(255U, Unsafe.BitCast(s4)); Assert.Equal(255, Unsafe.BitCast(s4)); - Assert.Equal(255ul, Unsafe.BitCast(s8)); - Assert.Equal(255l, Unsafe.BitCast(s8)); + Assert.Equal(255UL, Unsafe.BitCast(s8)); + Assert.Equal(255L, Unsafe.BitCast(s8)); byte* misalignedPtr = (byte*)NativeMemory.AlignedAlloc(9, 64) + 1; new Span(misalignedPtr, 8).Clear(); @@ -1196,11 +1196,11 @@ public static unsafe void BitCast() Assert.Equal((ushort)255, Unsafe.BitCast(*(S2*)misalignedPtr)); Assert.Equal((short)255, Unsafe.BitCast(*(S2*)misalignedPtr)); *(S4*)misalignedPtr = s4; - Assert.Equal(255u, Unsafe.BitCast(*(S4*)misalignedPtr)); + Assert.Equal(255U, Unsafe.BitCast(*(S4*)misalignedPtr)); Assert.Equal(255, Unsafe.BitCast(*(S4*)misalignedPtr)); *(S8*)misalignedPtr = s8; - Assert.Equal(255ul, Unsafe.BitCast(*(S8*)misalignedPtr)); - Assert.Equal(255l, Unsafe.BitCast(*(S8*)misalignedPtr)); + Assert.Equal(255UL, Unsafe.BitCast(*(S8*)misalignedPtr)); + Assert.Equal(255L, Unsafe.BitCast(*(S8*)misalignedPtr)); Half h = Unsafe.ReadUnaligned(ref Unsafe.As(ref s2)); float s = Unsafe.ReadUnaligned(ref Unsafe.As(ref s4)); From 6ca55ac381b1cdf8110643dbe33c020df357f4f2 Mon Sep 17 00:00:00 2001 From: petris Date: Thu, 11 May 2023 17:54:50 +0200 Subject: [PATCH 35/50] Format code --- src/coreclr/jit/importercalls.cpp | 18 +++++++++--------- .../tests/UnsafeTests.cs | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 9f7a7cbe68b24..f10998eaf164c 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4058,15 +4058,15 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, assert(sig->sigInst.methInstCount == 2); CORINFO_CLASS_HANDLE fromTypeHnd = sig->sigInst.methInst[0]; - ClassLayout* fromLayout = nullptr; - var_types fromType = TypeHandleToVarType(fromTypeHnd, &fromLayout); + ClassLayout* fromLayout = nullptr; + var_types fromType = TypeHandleToVarType(fromTypeHnd, &fromLayout); - CORINFO_CLASS_HANDLE toTypeHnd = sig->sigInst.methInst[1]; - ClassLayout* toLayout = nullptr; - var_types toType = TypeHandleToVarType(toTypeHnd, &toLayout); + CORINFO_CLASS_HANDLE toTypeHnd = sig->sigInst.methInst[1]; + ClassLayout* toLayout = nullptr; + var_types toType = TypeHandleToVarType(toTypeHnd, &toLayout); - unsigned fromSize = fromLayout != nullptr ? fromLayout->GetSize() : info.compCompHnd->getClassSize(fromTypeHnd); - unsigned toSize = toLayout != nullptr ? toLayout->GetSize() : info.compCompHnd->getClassSize(toTypeHnd); + unsigned fromSize = fromLayout != nullptr ? fromLayout->GetSize() : genTypeSize(fromType); + unsigned toSize = toLayout != nullptr ? toLayout->GetSize() : genTypeSize(toType); // Runtime requires all types to be at least 1-byte assert((fromSize != 0) && (toSize != 0)); @@ -4103,7 +4103,8 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, return op1; } } - else if (((toType != TYP_STRUCT) && (genActualType(valType) == toType)) || ClassLayout::AreCompatible(fromLayout, toLayout)) + else if (((toType != TYP_STRUCT) && (genActualType(valType) == toType)) || + ClassLayout::AreCompatible(fromLayout, toLayout)) { return op1; } @@ -4129,7 +4130,6 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, else if (TargetArchitecture::Is64Bit || (fromType == TYP_FLOAT)) { toType = varTypeToSigned(toType); - op1 = impImplicitR4orR8Cast(op1, fromType); return gtNewBitCastNode(toType, op1); } } diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 6579f7556cb0f..9aaec3d409e6d 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1134,6 +1134,9 @@ public static unsafe void BitCast() Assert.Throws(() => Unsafe.BitCast(5)); Assert.Throws(() => Unsafe.BitCast(empty1)); + Assert.Equal(uint.MaxValue, (long)Unsafe.BitCast(-1)); + Assert.Equal(uint.MaxValue, (ulong)Unsafe.BitCast(-1)); + byte b = 255; sbyte sb = -1; From 3aed08bf8cbc1fca4ae1223007e975d10f2ebb34 Mon Sep 17 00:00:00 2001 From: petris Date: Mon, 5 Jun 2023 19:45:15 +0200 Subject: [PATCH 36/50] Fix merge --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 87ddf44762a8a..12e10dcffeed2 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4193,7 +4193,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, if (varTypeIsIntegral(valType) && (genTypeSize(valType) < fromSize)) { unsigned lclNum = lvaGrabTemp(true DEBUGARG("bitcast small type extension")); - impAssignTempGen(lclNum, op1, CHECK_SPILL_ALL); + impStoreTemp(lclNum, op1, CHECK_SPILL_ALL); addr = gtNewLclVarAddrNode(lclNum, TYP_I_IMPL); } else From f68aec019556c8f20829916b8ae74768d2e19f2d Mon Sep 17 00:00:00 2001 From: petris Date: Mon, 5 Jun 2023 20:15:49 +0200 Subject: [PATCH 37/50] Fix merge --- src/coreclr/jit/importercalls.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 12e10dcffeed2..f9479cd244003 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4481,9 +4481,13 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, ClassLayout* layout = nullptr; var_types type = TypeHandleToVarType(typeHnd, &layout); GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; - GenTree* indir = gtNewLoadValueNode(type, layout, op2, flags); - return varTypeIsStruct(type) ? impAssignStruct(indir, op1, CHECK_SPILL_ALL) : gtNewAssignNode(indir, op1); + GetTree* store = gtNewStoreValueNode(type, layout, op2, op1, flags); + if (varTypeIsStruct(store)) + { + store = impStoreStruct(store, CHECK_SPILL_ALL); + } + return store; } default: From 23e48d29e6c007103dab4693b1edbd4116a4fcfc Mon Sep 17 00:00:00 2001 From: petris Date: Mon, 5 Jun 2023 20:24:18 +0200 Subject: [PATCH 38/50] Rename variables --- src/coreclr/jit/importercalls.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index f9479cd244003..d49fb04a731ea 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4474,15 +4474,15 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, // stobj !!T // ret - GenTree* op1 = impPopStack().val; - GenTree* op2 = impPopStack().val; - CORINFO_CLASS_HANDLE typeHnd = sig->sigInst.methInst[0]; ClassLayout* layout = nullptr; var_types type = TypeHandleToVarType(typeHnd, &layout); GenTreeFlags flags = intrinsic == NI_SRCS_UNSAFE_WriteUnaligned ? GTF_IND_UNALIGNED : GTF_EMPTY; - GetTree* store = gtNewStoreValueNode(type, layout, op2, op1, flags); + GenTree* value = impPopStack().val; + GenTree* addr = impPopStack().val; + + GetTree* store = gtNewStoreValueNode(type, layout, addr, value, flags); if (varTypeIsStruct(store)) { store = impStoreStruct(store, CHECK_SPILL_ALL); From 964ab384e495c8e377c830809cd825d4a5de1ac9 Mon Sep 17 00:00:00 2001 From: petris Date: Mon, 5 Jun 2023 20:40:48 +0200 Subject: [PATCH 39/50] Fix typo --- src/coreclr/jit/importercalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index d49fb04a731ea..d753b9ee371a4 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4482,7 +4482,7 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, GenTree* value = impPopStack().val; GenTree* addr = impPopStack().val; - GetTree* store = gtNewStoreValueNode(type, layout, addr, value, flags); + GenTree* store = gtNewStoreValueNode(type, layout, addr, value, flags); if (varTypeIsStruct(store)) { store = impStoreStruct(store, CHECK_SPILL_ALL); From 7253d9e26dc7918efccfd2a6fbfac2dc8996dcd8 Mon Sep 17 00:00:00 2001 From: petris Date: Tue, 6 Jun 2023 00:06:34 +0200 Subject: [PATCH 40/50] Format code --- src/coreclr/jit/promotiondecomposition.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/promotiondecomposition.cpp b/src/coreclr/jit/promotiondecomposition.cpp index 675cfef42fa7e..f10a879c9fcc7 100644 --- a/src/coreclr/jit/promotiondecomposition.cpp +++ b/src/coreclr/jit/promotiondecomposition.cpp @@ -537,12 +537,12 @@ class DecompositionPlan if (m_store->OperIs(GT_STORE_BLK)) { - addr = m_store->AsIndir()->Addr(); + addr = m_store->AsIndir()->Addr(); indirFlags = m_store->gtFlags & GTF_IND_COPYABLE_FLAGS; } else if (m_src->OperIs(GT_BLK)) { - addr = m_src->AsIndir()->Addr(); + addr = m_src->AsIndir()->Addr(); indirFlags = m_src->gtFlags & GTF_IND_COPYABLE_FLAGS; } From e65334810785a188ef4edb325dbad540c02cf32f Mon Sep 17 00:00:00 2001 From: petris Date: Wed, 7 Jun 2023 15:50:37 +0200 Subject: [PATCH 41/50] Add missing free --- .../System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 9aaec3d409e6d..1885bf28281a6 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -1219,6 +1219,8 @@ public static unsafe void BitCast() Assert.Equal(s, Unsafe.BitCast(*(S4*)misalignedPtr)); *(S8*)misalignedPtr = s8; Assert.Equal(d, Unsafe.BitCast(*(S8*)misalignedPtr)); + + NativeMemory.AlignedFree(misalignedPtr - 1); } } From 5111f6e6e3fd4eeac8f933939564de172bc07faa Mon Sep 17 00:00:00 2001 From: petris Date: Wed, 14 Jun 2023 04:50:31 +0200 Subject: [PATCH 42/50] Add IL tests --- src/tests/JIT/Intrinsics/BitCast.il | 192 ++++++++++++++++++++++++ src/tests/JIT/Intrinsics/BitCast.ilproj | 12 ++ 2 files changed, 204 insertions(+) create mode 100644 src/tests/JIT/Intrinsics/BitCast.il create mode 100644 src/tests/JIT/Intrinsics/BitCast.ilproj diff --git a/src/tests/JIT/Intrinsics/BitCast.il b/src/tests/JIT/Intrinsics/BitCast.il new file mode 100644 index 0000000000000..26b48a99b81a6 --- /dev/null +++ b/src/tests/JIT/Intrinsics/BitCast.il @@ -0,0 +1,192 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +// This test checks implicit floating point and small type casts in IL + +.assembly _ +{ + .permissionset reqmin = ( + 2e 01 80 8a 53 79 73 74 65 6d 2e 53 65 63 75 72 + 69 74 79 2e 50 65 72 6d 69 73 73 69 6f 6e 73 2e + 53 65 63 75 72 69 74 79 50 65 72 6d 69 73 73 69 + 6f 6e 41 74 74 72 69 62 75 74 65 2c 20 53 79 73 + 74 65 6d 2e 52 75 6e 74 69 6d 65 2c 20 56 65 72 + 73 69 6f 6e 3d 37 2e 30 2e 30 2e 30 2c 20 43 75 + 6c 74 75 72 65 3d 6e 65 75 74 72 61 6c 2c 20 50 + 75 62 6c 69 63 4b 65 79 54 6f 6b 65 6e 3d 62 30 + 33 66 35 66 37 66 31 31 64 35 30 61 33 61 15 01 + 54 02 10 53 6b 69 70 56 65 72 69 66 69 63 61 74 + 69 6f 6e 01 + ) + .hash algorithm 0x00008004 // SHA1 + .ver 0:0:0:0 +} + +.class private auto ansi '' +{ +} // end of class + +.class public auto ansi abstract sealed beforefieldinit Program + extends [System.Runtime]System.Object +{ + // Methods + .method public hidebysig static + int32 Main () cil managed + { + // Method begins at RVA 0x2068 + // Code size 263 (0x107) + .maxstack 2 + .locals init ( + [0] int8 sb, + [1] int16 s, + [2] int32 exitCode + ) + + IL_0000: ldc.i4.m1 + IL_0001: stloc.0 + IL_0002: ldc.i4.m1 + IL_0003: stloc.1 + IL_0004: ldc.i4.0 + IL_0005: stloc.2 + IL_0006: ldc.r8 1 + IL_000b: call !!1 Unsafe::BitCast(!!0) + IL_0010: call !!0 Program::NoInline(!!0) + IL_0015: ldc.i4 1065353216 + IL_001a: bne.un.s IL_0021 + + IL_001c: ldloc.2 + IL_001d: ldc.i4.s 10 + IL_001f: add + IL_0020: stloc.2 + + IL_0021: ldc.r4 1 + IL_002a: call !!1 Unsafe::BitCast(!!0) + IL_002f: call !!0 Program::NoInline(!!0) + IL_0034: ldc.i8 4607182418800017408 + IL_003d: bne.un.s IL_0044 + + IL_003f: ldloc.2 + IL_0040: ldc.i4.s 10 + IL_0042: add + IL_0043: stloc.2 + + IL_0044: ldc.r8 1 + IL_0049: call !!0 Program::NoInline(!!0) + IL_004e: call !!1 Unsafe::BitCast(!!0) + IL_0053: call !!0 Program::NoInline(!!0) + IL_0058: ldc.i4 1065353216 + IL_005d: bne.un.s IL_0064 + + IL_005f: ldloc.2 + IL_0060: ldc.i4.s 10 + IL_0062: add + IL_0063: stloc.2 + + IL_0064: ldc.r4 1 + IL_0069: call !!0 Program::NoInline(!!0) + IL_006f: call !!1 Unsafe::BitCast(!!0) + IL_0074: call !!0 Program::NoInline(!!0) + IL_0079: ldc.i8 4607182418800017408 + IL_0082: bne.un.s IL_0089 + + IL_0084: ldloc.2 + IL_0085: ldc.i4.s 10 + IL_0087: add + IL_0088: stloc.2 + + IL_0089: ldloca.s 0 + IL_008b: conv.u + IL_008c: call !!0* Program::NoInline(!!0*) + IL_0091: ldind.i1 + IL_0092: call !!1 Unsafe::BitCast(!!0) + IL_0097: conv.u8 + IL_0098: call !!0 Program::NoInline(!!0) + IL_009d: ldc.i4 255 + IL_00a2: conv.i8 + IL_00a3: bne.un.s IL_00aa + + IL_00a5: ldloc.2 + IL_00a6: ldc.i4.s 15 + IL_00a8: add + IL_00a9: stloc.2 + + IL_00aa: ldloca.s 1 + IL_00ac: conv.u + IL_00ad: call !!0* Program::NoInline(!!0*) + IL_00b2: ldind.i2 + IL_00b3: call !!1 Unsafe::BitCast(!!0) + IL_00b8: conv.u8 + IL_00b9: call !!0 Program::NoInline(!!0) + IL_00be: ldc.i4 65535 + IL_00c3: conv.i8 + IL_00c4: bne.un.s IL_00cb + + IL_00c6: ldloc.2 + IL_00c7: ldc.i4.s 15 + IL_00c9: add + IL_00ca: stloc.2 + + IL_00cb: ldloca.s 0 + IL_00cd: conv.u + IL_00ce: call !!0* Program::NoInline(!!0*) + IL_00d3: ldind.u1 + IL_00d4: call !!1 Unsafe::BitCast(!!0) + IL_00d9: conv.i8 + IL_00da: call !!0 Program::NoInline(!!0) + IL_00df: ldc.i4.m1 + IL_00e0: conv.i8 + IL_00e1: bne.un.s IL_00e8 + + IL_00e3: ldloc.2 + IL_00e4: ldc.i4.s 15 + IL_00e6: add + IL_00e7: stloc.2 + + IL_00e8: ldloca.s 1 + IL_00ea: conv.u + IL_00eb: call !!0* Program::NoInline(!!0*) + IL_00f0: ldind.u2 + IL_00f1: call !!1 Unsafe::BitCast(!!0) + IL_00f6: conv.i8 + IL_00f7: call !!0 Program::NoInline(!!0) + IL_00fc: ldc.i4.m1 + IL_00fd: conv.i8 + IL_00fe: bne.un.s IL_0105 + + IL_0100: ldloc.2 + IL_0101: ldc.i4.s 15 + IL_0103: add + IL_0104: stloc.2 + + IL_0105: ldloc.2 + IL_0106: ret + } // end of method Program::Main + + .method private hidebysig static + !!T NoInline ( + !!T 'value' + ) cil managed noinlining + { + // Method begins at RVA 0x217b + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ret + } // end of method Program::NoInline + + .method private hidebysig static + !!T* NoInline ( + !!T* 'value' + ) cil managed noinlining + { + // Method begins at RVA 0x217b + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: ret + } // end of method Program::NoInline + +} // end of class Program diff --git a/src/tests/JIT/Intrinsics/BitCast.ilproj b/src/tests/JIT/Intrinsics/BitCast.ilproj new file mode 100644 index 0000000000000..8b9c8251b5ca0 --- /dev/null +++ b/src/tests/JIT/Intrinsics/BitCast.ilproj @@ -0,0 +1,12 @@ + + + Exe + + + PdbOnly + True + + + + + From 8174abdc65072f935722d3b31801d5d147cae459 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Wed, 14 Jun 2023 04:55:05 +0200 Subject: [PATCH 43/50] Update BitCast.il --- src/tests/JIT/Intrinsics/BitCast.il | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/JIT/Intrinsics/BitCast.il b/src/tests/JIT/Intrinsics/BitCast.il index 26b48a99b81a6..da873490e65c8 100644 --- a/src/tests/JIT/Intrinsics/BitCast.il +++ b/src/tests/JIT/Intrinsics/BitCast.il @@ -47,7 +47,7 @@ IL_0001: stloc.0 IL_0002: ldc.i4.m1 IL_0003: stloc.1 - IL_0004: ldc.i4.0 + IL_0004: ldc.i4.5 IL_0005: stloc.2 IL_0006: ldc.r8 1 IL_000b: call !!1 Unsafe::BitCast(!!0) @@ -56,7 +56,7 @@ IL_001a: bne.un.s IL_0021 IL_001c: ldloc.2 - IL_001d: ldc.i4.s 10 + IL_001d: ldc.i4.s 5 IL_001f: add IL_0020: stloc.2 From b9160bebfc3a771ba7ec92045ba6a3678a629377 Mon Sep 17 00:00:00 2001 From: petris Date: Wed, 14 Jun 2023 17:54:43 +0200 Subject: [PATCH 44/50] Fix IL test --- src/tests/JIT/Intrinsics/BitCast.il | 418 ++++++++++++++++------------ 1 file changed, 238 insertions(+), 180 deletions(-) diff --git a/src/tests/JIT/Intrinsics/BitCast.il b/src/tests/JIT/Intrinsics/BitCast.il index da873490e65c8..89c45bd644821 100644 --- a/src/tests/JIT/Intrinsics/BitCast.il +++ b/src/tests/JIT/Intrinsics/BitCast.il @@ -4,189 +4,247 @@ // This test checks implicit floating point and small type casts in IL -.assembly _ +.assembly extern System.Runtime { - .permissionset reqmin = ( - 2e 01 80 8a 53 79 73 74 65 6d 2e 53 65 63 75 72 - 69 74 79 2e 50 65 72 6d 69 73 73 69 6f 6e 73 2e - 53 65 63 75 72 69 74 79 50 65 72 6d 69 73 73 69 - 6f 6e 41 74 74 72 69 62 75 74 65 2c 20 53 79 73 - 74 65 6d 2e 52 75 6e 74 69 6d 65 2c 20 56 65 72 - 73 69 6f 6e 3d 37 2e 30 2e 30 2e 30 2c 20 43 75 - 6c 74 75 72 65 3d 6e 65 75 74 72 61 6c 2c 20 50 - 75 62 6c 69 63 4b 65 79 54 6f 6b 65 6e 3d 62 30 - 33 66 35 66 37 66 31 31 64 35 30 61 33 61 15 01 - 54 02 10 53 6b 69 70 56 65 72 69 66 69 63 61 74 - 69 6f 6e 01 - ) - .hash algorithm 0x00008004 // SHA1 - .ver 0:0:0:0 + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 8:0:0:0 } - -.class private auto ansi '' +.assembly extern System.Console { -} // end of class - -.class public auto ansi abstract sealed beforefieldinit Program - extends [System.Runtime]System.Object + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: + .ver 8:0:0:0 +} +.assembly Bitcast { - // Methods - .method public hidebysig static - int32 Main () cil managed - { - // Method begins at RVA 0x2068 - // Code size 263 (0x107) - .maxstack 2 - .locals init ( - [0] int8 sb, - [1] int16 s, - [2] int32 exitCode - ) - - IL_0000: ldc.i4.m1 - IL_0001: stloc.0 - IL_0002: ldc.i4.m1 - IL_0003: stloc.1 - IL_0004: ldc.i4.5 - IL_0005: stloc.2 - IL_0006: ldc.r8 1 - IL_000b: call !!1 Unsafe::BitCast(!!0) - IL_0010: call !!0 Program::NoInline(!!0) - IL_0015: ldc.i4 1065353216 - IL_001a: bne.un.s IL_0021 - - IL_001c: ldloc.2 - IL_001d: ldc.i4.s 5 - IL_001f: add - IL_0020: stloc.2 - - IL_0021: ldc.r4 1 - IL_002a: call !!1 Unsafe::BitCast(!!0) - IL_002f: call !!0 Program::NoInline(!!0) - IL_0034: ldc.i8 4607182418800017408 - IL_003d: bne.un.s IL_0044 - - IL_003f: ldloc.2 - IL_0040: ldc.i4.s 10 - IL_0042: add - IL_0043: stloc.2 - - IL_0044: ldc.r8 1 - IL_0049: call !!0 Program::NoInline(!!0) - IL_004e: call !!1 Unsafe::BitCast(!!0) - IL_0053: call !!0 Program::NoInline(!!0) - IL_0058: ldc.i4 1065353216 - IL_005d: bne.un.s IL_0064 - - IL_005f: ldloc.2 - IL_0060: ldc.i4.s 10 - IL_0062: add - IL_0063: stloc.2 - - IL_0064: ldc.r4 1 - IL_0069: call !!0 Program::NoInline(!!0) - IL_006f: call !!1 Unsafe::BitCast(!!0) - IL_0074: call !!0 Program::NoInline(!!0) - IL_0079: ldc.i8 4607182418800017408 - IL_0082: bne.un.s IL_0089 - - IL_0084: ldloc.2 - IL_0085: ldc.i4.s 10 - IL_0087: add - IL_0088: stloc.2 - - IL_0089: ldloca.s 0 - IL_008b: conv.u - IL_008c: call !!0* Program::NoInline(!!0*) - IL_0091: ldind.i1 - IL_0092: call !!1 Unsafe::BitCast(!!0) - IL_0097: conv.u8 - IL_0098: call !!0 Program::NoInline(!!0) - IL_009d: ldc.i4 255 - IL_00a2: conv.i8 - IL_00a3: bne.un.s IL_00aa - - IL_00a5: ldloc.2 - IL_00a6: ldc.i4.s 15 - IL_00a8: add - IL_00a9: stloc.2 - - IL_00aa: ldloca.s 1 - IL_00ac: conv.u - IL_00ad: call !!0* Program::NoInline(!!0*) - IL_00b2: ldind.i2 - IL_00b3: call !!1 Unsafe::BitCast(!!0) - IL_00b8: conv.u8 - IL_00b9: call !!0 Program::NoInline(!!0) - IL_00be: ldc.i4 65535 - IL_00c3: conv.i8 - IL_00c4: bne.un.s IL_00cb - - IL_00c6: ldloc.2 - IL_00c7: ldc.i4.s 15 - IL_00c9: add - IL_00ca: stloc.2 - - IL_00cb: ldloca.s 0 - IL_00cd: conv.u - IL_00ce: call !!0* Program::NoInline(!!0*) - IL_00d3: ldind.u1 - IL_00d4: call !!1 Unsafe::BitCast(!!0) - IL_00d9: conv.i8 - IL_00da: call !!0 Program::NoInline(!!0) - IL_00df: ldc.i4.m1 - IL_00e0: conv.i8 - IL_00e1: bne.un.s IL_00e8 - - IL_00e3: ldloc.2 - IL_00e4: ldc.i4.s 15 - IL_00e6: add - IL_00e7: stloc.2 - - IL_00e8: ldloca.s 1 - IL_00ea: conv.u - IL_00eb: call !!0* Program::NoInline(!!0*) - IL_00f0: ldind.u2 - IL_00f1: call !!1 Unsafe::BitCast(!!0) - IL_00f6: conv.i8 - IL_00f7: call !!0 Program::NoInline(!!0) - IL_00fc: ldc.i4.m1 - IL_00fd: conv.i8 - IL_00fe: bne.un.s IL_0105 - - IL_0100: ldloc.2 - IL_0101: ldc.i4.s 15 - IL_0103: add - IL_0104: stloc.2 - - IL_0105: ldloc.2 - IL_0106: ret - } // end of method Program::Main - - .method private hidebysig static - !!T NoInline ( - !!T 'value' - ) cil managed noinlining - { - // Method begins at RVA 0x217b - // Code size 2 (0x2) - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: ret - } // end of method Program::NoInline - - .method private hidebysig static - !!T* NoInline ( - !!T* 'value' - ) cil managed noinlining - { - // Method begins at RVA 0x217b - // Code size 2 (0x2) - .maxstack 8 - - IL_0000: ldarg.0 - IL_0001: ret - } // end of method Program::NoInline + .permissionset reqmin + = {[System.Runtime]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} + .hash algorithm 0x00008004 + .ver 1:0:0:0 +} +.module Bitcast.dll +.custom instance void [System.Runtime]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) +.custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) +.imagebase 0x0000000180000000 +.file alignment 0x00001000 +.stackreserve 0x0000000000400000 +.subsystem 0x0003 // WINDOWS_CUI +.corflags 0x00000004 // IL_LIBRARY + +.class public abstract auto ansi sealed beforefieldinit Program + extends [System.Runtime]System.Object +{ + .field private static int32 exitCode + .method public hidebysig static int32 Main() cil managed + { + .entrypoint + // Code size 306 (0x132) + .maxstack 3 + .locals init (uint64 V_0, + int8* V_1, + int16* V_2, + int32* V_3) + IL_0000: ldc.i8 0x5555555555555555 + IL_0009: stloc.0 + IL_000a: ldloca.s V_0 + IL_000c: conv.u + IL_000d: ldc.i4.3 + IL_000e: add + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldc.i4.m1 + IL_0012: stind.i1 + IL_0013: ldc.i4 0xff + IL_0018: conv.i8 + IL_0019: ldloc.1 + IL_001a: call !!0* Program::NoInline(!!0*) + IL_001f: ldind.i1 + IL_0020: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_0025: conv.u8 + IL_0026: ldc.i4.s 18 + IL_0028: call void Program::Verify(int64, + int64, + int32) + IL_002d: ldc.i4.m1 + IL_002e: conv.i8 + IL_002f: ldloc.1 + IL_0030: call !!0* Program::NoInline(!!0*) + IL_0035: ldind.u1 + IL_0036: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_003b: conv.i8 + IL_003c: ldc.i4.s 19 + IL_003e: call void Program::Verify(int64, + int64, + int32) + IL_0043: ldloca.s V_0 + IL_0045: conv.u + IL_0046: ldc.i4.2 + IL_0047: add + IL_0048: stloc.2 + IL_0049: ldloc.2 + IL_004a: ldc.i4.m1 + IL_004b: stind.i2 + IL_004c: ldc.i4 0xffff + IL_0051: conv.i8 + IL_0052: ldloc.2 + IL_0053: call !!0* Program::NoInline(!!0*) + IL_0058: ldind.i2 + IL_0059: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_005e: conv.u8 + IL_005f: ldc.i4.s 23 + IL_0061: call void Program::Verify(int64, + int64, + int32) + IL_0066: ldc.i4.m1 + IL_0067: conv.i8 + IL_0068: ldloc.2 + IL_0069: call !!0* Program::NoInline(!!0*) + IL_006e: ldind.u2 + IL_006f: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_0074: conv.i8 + IL_0075: ldc.i4.s 24 + IL_0077: call void Program::Verify(int64, + int64, + int32) + IL_007c: ldloc.2 + IL_007d: stloc.3 + IL_007e: ldloc.3 + IL_007f: ldc.i4.m1 + IL_0080: unaligned. 2 + IL_0083: stind.i4 + IL_0084: ldc.i4.m1 + IL_0085: conv.u8 + IL_0086: ldloc.3 + IL_0087: call !!0* Program::NoInline(!!0*) + IL_008c: unaligned. 2 + IL_008f: ldind.i4 + IL_0090: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_0095: conv.u8 + IL_0096: ldc.i4.s 28 + IL_0098: call void Program::Verify(int64, + int64, + int32) + IL_009d: ldc.i4.m1 + IL_009e: conv.i8 + IL_009f: ldloc.3 + IL_00a0: call !!0* Program::NoInline(!!0*) + IL_00a5: unaligned. 2 + IL_00a8: ldind.u4 + IL_00a9: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_00ae: conv.i8 + IL_00af: ldc.i4.s 29 + IL_00b1: call void Program::Verify(int64, + int64, + int32) + IL_00b6: ldc.i4 0x3f800000 + IL_00bb: conv.i8 + IL_00bc: ldc.r8 1. + IL_00c5: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_00ca: conv.i8 + IL_00cb: ldc.i4.s 31 + IL_00cd: call void Program::Verify(int64, + int64, + int32) + IL_00d2: ldc.i8 0x3ff0000000000000 + IL_00db: ldc.r4 1. + IL_00e0: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_00e5: ldc.i4.s 32 + IL_00e7: call void Program::Verify(int64, + int64, + int32) + IL_00ec: ldc.i4 0x3f800000 + IL_00f1: conv.i8 + IL_00f2: ldc.r8 1. + IL_00fb: call !!0 Program::NoInline(!!0) + IL_0100: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_0105: conv.i8 + IL_0106: ldc.i4.s 33 + IL_0108: call void Program::Verify(int64, + int64, + int32) + IL_010d: ldc.i8 0x3ff0000000000000 + IL_0116: ldc.r4 1. + IL_011b: call !!0 Program::NoInline(!!0) + IL_0120: call !!1 [System.Runtime]System.Runtime.CompilerServices.Unsafe::BitCast(!!0) + IL_0125: ldc.i4.s 34 + IL_0127: call void Program::Verify(int64, + int64, + int32) + IL_012c: ldsfld int32 Program::exitCode + IL_0131: ret + } // end of method Program::Main + + .method private hidebysig static void Verify(int64 expected, + int64 actual, + [opt] int32 line) cil managed noinlining + { + .param [3] = int32(0x00000000) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CallerLineNumberAttribute::.ctor() = ( 01 00 00 00 ) + // Code size 100 (0x64) + .maxstack 3 + .locals init (valuetype [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_0) + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: bne.un.s IL_0011 + + IL_0004: ldsfld int32 Program::exitCode + IL_0009: ldc.i4.1 + IL_000a: sub + IL_000b: stsfld int32 Program::exitCode + IL_0010: ret + + IL_0011: ldloca.s V_0 + IL_0013: ldc.i4.s 32 + IL_0015: ldc.i4.3 + IL_0016: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::.ctor(int32, + int32) + IL_001b: ldloca.s V_0 + IL_001d: ldstr "Failed at line " + IL_0022: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string) + IL_0027: ldloca.s V_0 + IL_0029: ldarg.2 + IL_002a: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(!!0) + IL_002f: ldloca.s V_0 + IL_0031: ldstr ", expected " + IL_0036: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string) + IL_003b: ldloca.s V_0 + IL_003d: ldarg.0 + IL_003e: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(!!0) + IL_0043: ldloca.s V_0 + IL_0045: ldstr ", got " + IL_004a: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendLiteral(string) + IL_004f: ldloca.s V_0 + IL_0051: ldarg.1 + IL_0052: call instance void [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::AppendFormatted(!!0) + IL_0057: ldloca.s V_0 + IL_0059: call instance string [System.Runtime]System.Runtime.CompilerServices.DefaultInterpolatedStringHandler::ToStringAndClear() + IL_005e: call void [System.Console]System.Console::WriteLine(string) + IL_0063: ret + } // end of method Program::Verify + + .method private hidebysig static !!T NoInline(!!T 'value') cil managed noinlining + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ret + } // end of method Program::NoInline + + .method private hidebysig static !!T* NoInline(!!T* 'value') cil managed noinlining + { + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ret + } // end of method Program::NoInline + + .method private hidebysig specialname rtspecialname static + void .cctor() cil managed + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldc.i4.s 110 + IL_0002: stsfld int32 Program::exitCode + IL_0007: ret + } // end of method Program::.cctor } // end of class Program From d8646889bafd94ad44693ae526e4eb97e7dd66e4 Mon Sep 17 00:00:00 2001 From: petris Date: Wed, 14 Jun 2023 22:34:05 +0200 Subject: [PATCH 45/50] Intrinsify Copy too --- src/coreclr/jit/importercalls.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index abc66773aedbc..d2e91e83c4a5f 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4240,7 +4240,14 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic, // stobj !!T // ret - return nullptr; + CORINFO_CLASS_HANDLE typeHnd = sig->sigInst.methInst[0]; + ClassLayout* layout = nullptr; + var_types type = TypeHandleToVarType(typeHnd, &layout); + + GenTree* source = impPopStack().val; + GenTree* dest = impPopStack().val; + + return gtNewStoreValueNode(type, layout, dest, gtNewLoadValueNode(type, layout, source)); } case NI_SRCS_UNSAFE_CopyBlock: From 588d55ebdc50d72d216919067b4c6438c61b0884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:07:45 +0200 Subject: [PATCH 46/50] Update UnsafeTests.cs --- .../tests/UnsafeTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 1885bf28281a6..74bb5415fae8f 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -740,6 +740,20 @@ public static unsafe void ReadUnaligned_ByRef_Struct() Assert.Equal(3.42, actual.Double); } + [Fact] + public static unsafe void ReadUnaligned_ByRef_StructManaged() + { + Int32Generic s = new() { Int32 = 1, Value = null }; + + Int32Generic actual = Read>(ref Unsafe.As, byte>(ref s)); + + Assert.Equal(1, actual.Int32); + Assert.Equal(null, actual.Value); + + [MethodImpl(MethodImplOptions.NoInlining)] + static T Read(ref byte b) => Unsafe.ReadUnaligned(ref b); + } + [Fact] public static unsafe void ReadUnaligned_Ptr_Int32() { @@ -814,6 +828,20 @@ public static unsafe void WriteUnaligned_ByRef_Struct() Assert.Equal(3.42, actual.Double); } + [Fact] + public static unsafe void ReadUnaligned_ByRef_StructManaged() + { + Int32Generic actual = default; + + Write(ref Unsafe.As, byte>(ref actual), new Int32Generic() { Int32 = 1, Value = null }); + + Assert.Equal(1, actual.Int32); + Assert.Equal(null, actual.Value); + + [MethodImpl(MethodImplOptions.NoInlining)] + static T Write(ref byte b, T value) => Unsafe.WriteUnaligned(ref b, value); + } + [Fact] public static unsafe void WriteUnaligned_Ptr_Int32() { @@ -1303,6 +1331,12 @@ public struct StringInt32 public int Int32; } + public struct Int32Generic + { + public int Int32; + public T Value; + } + public struct Single4 { public float X; From 674d60532a7611a88b826d739bf3bf9af045aa75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:36:37 +0200 Subject: [PATCH 47/50] Update UnsafeTests.cs --- .../tests/UnsafeTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 74bb5415fae8f..9bd57cca90828 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -743,12 +743,12 @@ public static unsafe void ReadUnaligned_ByRef_Struct() [Fact] public static unsafe void ReadUnaligned_ByRef_StructManaged() { - Int32Generic s = new() { Int32 = 1, Value = null }; + Int32Generic s = new() { Int32 = 5, Value = "a" }; Int32Generic actual = Read>(ref Unsafe.As, byte>(ref s)); - Assert.Equal(1, actual.Int32); - Assert.Equal(null, actual.Value); + Assert.Equal(5, actual.Int32); + Assert.Equal("a", actual.Value); [MethodImpl(MethodImplOptions.NoInlining)] static T Read(ref byte b) => Unsafe.ReadUnaligned(ref b); @@ -829,14 +829,14 @@ public static unsafe void WriteUnaligned_ByRef_Struct() } [Fact] - public static unsafe void ReadUnaligned_ByRef_StructManaged() + public static unsafe void WriteUnaligned_ByRef_StructManaged() { Int32Generic actual = default; - Write(ref Unsafe.As, byte>(ref actual), new Int32Generic() { Int32 = 1, Value = null }); + Write(ref Unsafe.As, byte>(ref actual), new Int32Generic() { Int32 = 5, Value = "a" }); - Assert.Equal(1, actual.Int32); - Assert.Equal(null, actual.Value); + Assert.Equal(5, actual.Int32); + Assert.Equal("a", actual.Value); [MethodImpl(MethodImplOptions.NoInlining)] static T Write(ref byte b, T value) => Unsafe.WriteUnaligned(ref b, value); From 43ebe7810cb689bf2028787d59a7cec900249b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:14:05 +0200 Subject: [PATCH 48/50] Update UnsafeTests.cs --- .../System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 9bd57cca90828..7a0d8e045ec1f 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -839,7 +839,7 @@ public static unsafe void WriteUnaligned_ByRef_StructManaged() Assert.Equal("a", actual.Value); [MethodImpl(MethodImplOptions.NoInlining)] - static T Write(ref byte b, T value) => Unsafe.WriteUnaligned(ref b, value); + static void Write(ref byte b, T value) => Unsafe.WriteUnaligned(ref b, value); } [Fact] From 8e52b2fcf5e1d9d20c5248869768b0eac953f6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Tue, 11 Jul 2023 01:00:17 +0200 Subject: [PATCH 49/50] Update src/tests/JIT/Intrinsics/BitCast.il Co-authored-by: Egor Bogatov --- src/tests/JIT/Intrinsics/BitCast.il | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/tests/JIT/Intrinsics/BitCast.il b/src/tests/JIT/Intrinsics/BitCast.il index 89c45bd644821..0cba680ad3eff 100644 --- a/src/tests/JIT/Intrinsics/BitCast.il +++ b/src/tests/JIT/Intrinsics/BitCast.il @@ -4,31 +4,9 @@ // This test checks implicit floating point and small type casts in IL -.assembly extern System.Runtime -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 8:0:0:0 -} -.assembly extern System.Console -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 8:0:0:0 -} -.assembly Bitcast -{ - .permissionset reqmin - = {[System.Runtime]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}} - .hash algorithm 0x00008004 - .ver 1:0:0:0 -} -.module Bitcast.dll -.custom instance void [System.Runtime]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) -.custom instance void [System.Runtime]System.Runtime.CompilerServices.RefSafetyRulesAttribute::.ctor(int32) = ( 01 00 0B 00 00 00 00 00 ) -.imagebase 0x0000000180000000 -.file alignment 0x00001000 -.stackreserve 0x0000000000400000 -.subsystem 0x0003 // WINDOWS_CUI -.corflags 0x00000004 // IL_LIBRARY +.assembly extern System.Runtime {} +.assembly extern System.Console {} +.assembly Bitcast {} .class public abstract auto ansi sealed beforefieldinit Program extends [System.Runtime]System.Object From 300951c308cca804a24464841bbdb3e26ffcf6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Thu, 13 Jul 2023 00:43:08 +0000 Subject: [PATCH 50/50] Add struct copy tests --- .../tests/UnsafeTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs index 7a0d8e045ec1f..b07ef0c72d13e 100644 --- a/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs +++ b/src/libraries/System.Runtime.CompilerServices.Unsafe/tests/UnsafeTests.cs @@ -157,6 +157,30 @@ public static unsafe void CopyToVoidPtr() Assert.Equal(10, value); } + [Fact] + public static unsafe void CopyToRefGenericStruct() + { + Int32Generic destination = default; + Int32Generic value = new() { Int32 = 5, Value = "a" }; + + Unsafe.Copy(ref destination, Unsafe.AsPointer(ref value)); + + Assert.Equal(5, destination.Int32); + Assert.Equal("a", destination.Value); + } + + [Fact] + public static unsafe void CopyToVoidPtrGenericStruct() + { + Int32Generic destination = default; + Int32Generic value = new() { Int32 = 5, Value = "a" }; + + Unsafe.Copy(Unsafe.AsPointer(ref destination), ref value); + + Assert.Equal(5, destination.Int32); + Assert.Equal("a", destination.Value); + } + [Fact] public static unsafe void SizeOf() {