-
Notifications
You must be signed in to change notification settings - Fork 5.3k
[RyuJIT Wasm] Cast Operations Follow Up #122862
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9288c02
85f2b80
7dbe5ff
124c8dd
eb34147
e3d59bc
71eb61b
3988828
afadd5d
83410d9
ba132f4
f3e9e63
9b7f09b
386f93a
c88d66b
0247f91
d7f643b
11ec0a8
2031845
4e8ec4a
a4ad71a
44c87f4
cc76584
1b1865f
ec11645
b62b9ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2309,6 +2309,7 @@ void CodeGen::genTransferRegGCState(regNumber dst, regNumber src) | |
| gcInfo.gcMarkRegSetNpt(dstMask); | ||
| } | ||
| } | ||
| #endif | ||
|
|
||
| //------------------------------------------------------------------------ | ||
| // genCodeForCast: Generates the code for GT_CAST. | ||
|
|
@@ -2337,12 +2338,12 @@ void CodeGen::genCodeForCast(GenTreeOp* tree) | |
| // Casts int32/uint32/int64/uint64 --> float/double | ||
| genIntToFloatCast(tree); | ||
| } | ||
| #ifndef TARGET_64BIT | ||
| #if !defined(TARGET_64BIT) && !defined(TARGET_WASM) | ||
| else if (varTypeIsLong(tree->gtOp1)) | ||
| { | ||
| genLongToIntCast(tree); | ||
| } | ||
| #endif // !TARGET_64BIT | ||
| #endif // !TARGET_64BIT && !TARGET_WASM | ||
| else | ||
| { | ||
| // Casts int <--> int | ||
|
|
@@ -2366,8 +2367,13 @@ CodeGen::GenIntCastDesc::GenIntCastDesc(GenTreeCast* cast) | |
| const bool castIsLoad = !src->isUsedFromReg(); | ||
|
|
||
| assert(castIsLoad == src->isUsedFromMemory()); | ||
| #ifndef TARGET_WASM | ||
| assert((srcSize == 4) || (srcSize == genTypeSize(TYP_I_IMPL))); | ||
| assert((dstSize == 4) || (dstSize == genTypeSize(TYP_I_IMPL))); | ||
| #else | ||
| assert((srcSize == 4) || (srcSize == 8)); | ||
| assert((dstSize == 4) || (dstSize == 8)); | ||
| #endif | ||
|
|
||
| assert(dstSize == genTypeSize(genActualType(castType))); | ||
|
|
||
|
|
@@ -2395,7 +2401,7 @@ CodeGen::GenIntCastDesc::GenIntCastDesc(GenTreeCast* cast) | |
| m_extendSrcSize = castSize; | ||
| } | ||
| } | ||
| #ifdef TARGET_64BIT | ||
| #if defined(TARGET_64BIT) || defined(TARGET_WASM) | ||
| // castType cannot be (U)LONG on 32 bit targets, such casts should have been decomposed. | ||
| // srcType cannot be a small int type since it's the "actual type" of the cast operand. | ||
| // This means that widening casts do not occur on 32 bit targets. | ||
|
|
@@ -2480,6 +2486,7 @@ CodeGen::GenIntCastDesc::GenIntCastDesc(GenTreeCast* cast) | |
| m_extendSrcSize = srcSize; | ||
| } | ||
|
|
||
| #ifndef TARGET_WASM | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you need this ifdef? I think things will work correctly without it (since we don't actually do any containment for casts yet). |
||
| if (castIsLoad) | ||
| { | ||
| const var_types srcLoadType = src->TypeGet(); | ||
|
|
@@ -2521,8 +2528,10 @@ CodeGen::GenIntCastDesc::GenIntCastDesc(GenTreeCast* cast) | |
| unreached(); | ||
| } | ||
| } | ||
| #endif // !TARGET_WASM | ||
| } | ||
|
|
||
| #ifndef TARGET_WASM | ||
| #if !defined(TARGET_64BIT) | ||
| //------------------------------------------------------------------------ | ||
| // genStoreLongLclVar: Generate code to store a non-enregistered long lclVar | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -348,6 +348,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) | |||||||
| genCodeForConstant(treeNode); | ||||||||
| break; | ||||||||
|
|
||||||||
| case GT_CAST: | ||||||||
| genCodeForCast(treeNode->AsOp()); | ||||||||
| break; | ||||||||
|
|
||||||||
| case GT_NEG: | ||||||||
| case GT_NOT: | ||||||||
| genCodeForNegNot(treeNode->AsOp()); | ||||||||
|
|
@@ -447,22 +451,234 @@ static constexpr uint32_t PackOperAndType(genTreeOps oper, var_types type) | |||||||
| { | ||||||||
| type = TYP_I_IMPL; | ||||||||
| } | ||||||||
| static_assert((ssize_t)GT_COUNT > (ssize_t)TYP_COUNT); | ||||||||
| return ((uint32_t)oper << (ConstLog2<GT_COUNT>::value + 1)) | ((uint32_t)type); | ||||||||
| const int shift1 = ConstLog2<TYP_COUNT>::value + 1; | ||||||||
| return ((uint32_t)oper << shift1) | ((uint32_t)type); | ||||||||
| } | ||||||||
|
|
||||||||
| //------------------------------------------------------------------------ | ||||||||
| // PackOperAndType: Pack a GenTreeOp* into a uint32_t | ||||||||
| // PackOperAndType: Pack a genTreeOps and two var_types into a uint32_t | ||||||||
| // | ||||||||
| // Arguments: | ||||||||
| // oper - a genTreeOps to pack | ||||||||
| // toType - a var_types to pack | ||||||||
| // fromType - a var_types to pack | ||||||||
| // | ||||||||
| // Return Value: | ||||||||
| // oper and the types packed into an integer that can be used as a switch value/case | ||||||||
| // | ||||||||
| static constexpr uint32_t PackOperAndType(genTreeOps oper, var_types toType, var_types fromType) | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This variant looks unused. |
||||||||
| { | ||||||||
| if (fromType == TYP_BYREF) | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also convert TYP_REF |
||||||||
| { | ||||||||
| fromType = TYP_I_IMPL; | ||||||||
| } | ||||||||
| if (toType == TYP_BYREF) | ||||||||
| { | ||||||||
| toType = TYP_I_IMPL; | ||||||||
| } | ||||||||
| const int shift1 = ConstLog2<TYP_COUNT>::value + 1; | ||||||||
| const int shift2 = shift1 + ConstLog2<GT_COUNT>::value + 1; | ||||||||
| return ((uint32_t)oper << shift1) | ((uint32_t)fromType) | ((uint32_t)toType << shift2); | ||||||||
| } | ||||||||
|
|
||||||||
| // ------------------------------------------------------------------------ | ||||||||
| // PackTypes: Pack two var_types together into a uint32_t | ||||||||
|
|
||||||||
| // Arguments: | ||||||||
| // treeNode - a GenTreeOp to extract oper and type from | ||||||||
| // toType - a var_types to pack | ||||||||
| // fromType - a var_types to pack | ||||||||
| // | ||||||||
| // Return Value: | ||||||||
| // the node's oper and type packed into an integer that can be used as a switch value | ||||||||
| // The two types packed together into an integer that can be used as a switch/value, | ||||||||
| // the primary use case being the handling of operations with two-type variants such | ||||||||
| // as casts. | ||||||||
| // | ||||||||
| static constexpr uint32_t PackTypes(var_types toType, var_types fromType) | ||||||||
adamperlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| { | ||||||||
| if (toType == TYP_BYREF) | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here |
||||||||
| { | ||||||||
| toType = TYP_I_IMPL; | ||||||||
| } | ||||||||
| if (fromType == TYP_BYREF) | ||||||||
| { | ||||||||
| fromType = TYP_I_IMPL; | ||||||||
| } | ||||||||
| const int shift1 = ConstLog2<TYP_COUNT>::value + 1; | ||||||||
| return ((uint32_t)toType) | ((uint32_t)fromType << shift1); | ||||||||
| } | ||||||||
|
|
||||||||
| //------------------------------------------------------------------------ | ||||||||
| // genIntToIntCast: Generate code for an integer to integer cast | ||||||||
| // | ||||||||
| // Arguments: | ||||||||
| // cast - The GT_CAST node for the integer cast operation | ||||||||
| // | ||||||||
| // Notes: | ||||||||
| // Handles casts to and from small int, int, and long types | ||||||||
| // including proper sign extension and truncation as needed. | ||||||||
| // | ||||||||
| void CodeGen::genIntToIntCast(GenTreeCast* cast) | ||||||||
| { | ||||||||
adamperlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| GenIntCastDesc desc(cast); | ||||||||
| var_types toType = genActualType(cast->CastToType()); | ||||||||
| var_types fromType = genActualType(cast->CastOp()); | ||||||||
| int extendSize = desc.ExtendSrcSize(); | ||||||||
| instruction ins = INS_none; | ||||||||
| assert(fromType == TYP_INT || fromType == TYP_LONG); | ||||||||
|
|
||||||||
| genConsumeOperands(cast); | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need an NYI for checked ( |
||||||||
|
|
||||||||
| switch (desc.ExtendKind()) | ||||||||
| { | ||||||||
| case GenIntCastDesc::COPY: | ||||||||
| { | ||||||||
| if (toType == TYP_INT && fromType == TYP_LONG) | ||||||||
| { | ||||||||
| ins = INS_i32_wrap_i64; | ||||||||
| } | ||||||||
| else | ||||||||
| { | ||||||||
| assert(toType == fromType); | ||||||||
| ins = INS_none; | ||||||||
| } | ||||||||
| break; | ||||||||
| } | ||||||||
| case GenIntCastDesc::ZERO_EXTEND_SMALL_INT: | ||||||||
| { | ||||||||
| int andAmount = extendSize == 1 ? 255 : 65535; | ||||||||
| if (fromType == TYP_LONG) | ||||||||
| { | ||||||||
| GetEmitter()->emitIns(INS_i32_wrap_i64); | ||||||||
| } | ||||||||
| GetEmitter()->emitIns_I(INS_i32_const, EA_4BYTE, andAmount); | ||||||||
| GetEmitter()->emitIns(INS_i32_and); | ||||||||
| ins = (toType == TYP_LONG) ? INS_i64_extend_u_i32 : INS_none; | ||||||||
|
Comment on lines
+554
to
+555
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This type of cast always produces a |
||||||||
| break; | ||||||||
| } | ||||||||
| case GenIntCastDesc::SIGN_EXTEND_SMALL_INT: | ||||||||
| { | ||||||||
| if (fromType == TYP_LONG) | ||||||||
| { | ||||||||
| GetEmitter()->emitIns(INS_i32_wrap_i64); | ||||||||
| } | ||||||||
| ins = (extendSize == 1) ? INS_i32_extend8_s : INS_i32_extend16_s; | ||||||||
|
|
||||||||
| break; | ||||||||
| } | ||||||||
| case GenIntCastDesc::ExtendKind::ZERO_EXTEND_INT: | ||||||||
| { | ||||||||
| ins = INS_i64_extend_u_i32; | ||||||||
| break; | ||||||||
| } | ||||||||
| case GenIntCastDesc::ExtendKind::SIGN_EXTEND_INT: | ||||||||
| { | ||||||||
| ins = INS_i64_extend_s_i32; | ||||||||
| break; | ||||||||
| } | ||||||||
| default: | ||||||||
| unreached(); | ||||||||
| } | ||||||||
|
|
||||||||
| if (ins != INS_none) | ||||||||
| { | ||||||||
| GetEmitter()->emitIns(ins); | ||||||||
| } | ||||||||
| genProduceReg(cast); | ||||||||
| } | ||||||||
|
|
||||||||
| //------------------------------------------------------------------------ | ||||||||
| // genFloatToIntCast: Generate code for a floating point to integer cast | ||||||||
| // | ||||||||
| // Arguments: | ||||||||
| // tree - The GT_CAST node for the float-to-int cast operation | ||||||||
| // | ||||||||
| // Notes: | ||||||||
| // Handles casts from TYP_FLOAT/TYP_DOUBLE to TYP_INT/TYP_LONG. | ||||||||
| // Uses saturating truncation instructions (trunc_sat) which clamp | ||||||||
| // out-of-range values rather than trapping. | ||||||||
| // | ||||||||
| void CodeGen::genFloatToIntCast(GenTree* tree) | ||||||||
| { | ||||||||
adamperlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| var_types toType = tree->TypeGet(); | ||||||||
| var_types fromType = tree->AsCast()->CastOp()->TypeGet(); | ||||||||
| bool isUnsigned = varTypeIsUnsigned(tree->AsCast()->CastToType()); | ||||||||
| instruction ins = INS_none; | ||||||||
| assert(varTypeIsFloating(fromType) && (toType == TYP_INT || toType == TYP_LONG)); | ||||||||
|
|
||||||||
| genConsumeOperands(tree->AsCast()); | ||||||||
|
|
||||||||
| switch (PackTypes(fromType, toType)) | ||||||||
| { | ||||||||
| case PackTypes(TYP_FLOAT, TYP_INT): | ||||||||
| ins = isUnsigned ? INS_i32_trunc_sat_f32_u : INS_i32_trunc_sat_f32_s; | ||||||||
| break; | ||||||||
| case PackTypes(TYP_DOUBLE, TYP_INT): | ||||||||
| ins = isUnsigned ? INS_i32_trunc_sat_f64_u : INS_i32_trunc_sat_f64_s; | ||||||||
| break; | ||||||||
| case PackTypes(TYP_FLOAT, TYP_LONG): | ||||||||
| ins = isUnsigned ? INS_i64_trunc_sat_f32_u : INS_i64_trunc_sat_f32_s; | ||||||||
| break; | ||||||||
| case PackTypes(TYP_DOUBLE, TYP_LONG): | ||||||||
| ins = isUnsigned ? INS_i64_trunc_sat_f64_u : INS_i64_trunc_sat_f64_s; | ||||||||
| break; | ||||||||
| default: | ||||||||
| unreached(); | ||||||||
| } | ||||||||
|
|
||||||||
| GetEmitter()->emitIns(ins); | ||||||||
| genProduceReg(tree); | ||||||||
| } | ||||||||
|
|
||||||||
| //------------------------------------------------------------------------ | ||||||||
| // genIntToFloatCast: Generate code for an integer to floating point cast | ||||||||
| // | ||||||||
| // Arguments: | ||||||||
| // tree - The GT_CAST node for the int-to-float cast operation | ||||||||
| // | ||||||||
| static uint32_t PackOperAndType(GenTreeOp* treeNode) | ||||||||
| // Notes: | ||||||||
| // Handles casts from TYP_INT/TYP_LONG to TYP_FLOAT/TYP_DOUBLE. | ||||||||
| // Currently not implemented (NYI_WASM). | ||||||||
| // | ||||||||
| void CodeGen::genIntToFloatCast(GenTree* tree) | ||||||||
| { | ||||||||
| return PackOperAndType(treeNode->OperGet(), treeNode->TypeGet()); | ||||||||
| NYI_WASM("genIntToFloatCast"); | ||||||||
| } | ||||||||
|
|
||||||||
| //------------------------------------------------------------------------ | ||||||||
| // genFloatToFloatCast: Generate code for a float to float cast | ||||||||
| // | ||||||||
| // Arguments: | ||||||||
| // tree - The GT_CAST node for the float-to-float cast operation | ||||||||
| // | ||||||||
| void CodeGen::genFloatToFloatCast(GenTree* tree) | ||||||||
| { | ||||||||
adamperlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| var_types toType = tree->TypeGet(); | ||||||||
| var_types fromType = tree->AsCast()->CastOp()->TypeGet(); | ||||||||
| instruction ins = INS_none; | ||||||||
|
|
||||||||
| genConsumeOperands(tree->AsCast()); | ||||||||
|
|
||||||||
| switch (PackTypes(toType, fromType)) | ||||||||
| { | ||||||||
| case PackTypes(TYP_FLOAT, TYP_DOUBLE): | ||||||||
| ins = INS_f32_demote_f64; | ||||||||
| break; | ||||||||
| case PackTypes(TYP_DOUBLE, TYP_FLOAT): | ||||||||
| ins = INS_f64_promote_f32; | ||||||||
| break; | ||||||||
| case PackTypes(TYP_FLOAT, TYP_FLOAT): | ||||||||
adamperlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| case PackTypes(TYP_DOUBLE, TYP_DOUBLE): | ||||||||
| ins = INS_none; | ||||||||
| break; | ||||||||
| default: | ||||||||
| unreached(); | ||||||||
| } | ||||||||
|
|
||||||||
| if (ins != INS_none) | ||||||||
| { | ||||||||
| GetEmitter()->emitIns(ins); | ||||||||
| } | ||||||||
| genProduceReg(tree); | ||||||||
| } | ||||||||
|
|
||||||||
| //------------------------------------------------------------------------ | ||||||||
|
|
@@ -476,7 +692,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) | |||||||
| genConsumeOperands(treeNode); | ||||||||
|
|
||||||||
| instruction ins; | ||||||||
| switch (PackOperAndType(treeNode)) | ||||||||
| switch (PackOperAndType(treeNode->OperGet(), treeNode->TypeGet())) | ||||||||
| { | ||||||||
| case PackOperAndType(GT_ADD, TYP_INT): | ||||||||
| if (treeNode->gtOverflow()) | ||||||||
|
|
@@ -571,7 +787,7 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode) | |||||||
| genConsumeOperands(treeNode); | ||||||||
|
|
||||||||
| instruction ins; | ||||||||
| switch (PackOperAndType(treeNode)) | ||||||||
| switch (PackOperAndType(treeNode->OperGet(), treeNode->TypeGet())) | ||||||||
| { | ||||||||
| case PackOperAndType(GT_DIV, TYP_INT): | ||||||||
| ins = INS_i32_div_s; | ||||||||
|
|
@@ -689,7 +905,7 @@ void CodeGen::genCodeForShift(GenTree* tree) | |||||||
| // for both the shift and shiftee. So the shift may need to be extended (zero-extended) for TYP_LONG. | ||||||||
|
|
||||||||
| instruction ins; | ||||||||
| switch (PackOperAndType(treeNode)) | ||||||||
| switch (PackOperAndType(treeNode->OperGet(), treeNode->TypeGet())) | ||||||||
| { | ||||||||
| case PackOperAndType(GT_LSH, TYP_INT): | ||||||||
| ins = INS_i32_shl; | ||||||||
|
|
@@ -748,7 +964,7 @@ void CodeGen::genCodeForNegNot(GenTreeOp* tree) | |||||||
| genConsumeOperands(tree); | ||||||||
|
|
||||||||
| instruction ins; | ||||||||
| switch (PackOperAndType(tree)) | ||||||||
| switch (PackOperAndType(tree->OperGet(), tree->TypeGet())) | ||||||||
| { | ||||||||
| case PackOperAndType(GT_NOT, TYP_INT): | ||||||||
| GetEmitter()->emitIns_I(INS_i32_const, emitTypeSize(tree), -1); | ||||||||
|
|
@@ -843,6 +1059,13 @@ void CodeGen::genCodeForLclVar(GenTreeLclVar* tree) | |||||||
| assert(genIsValidReg(varDsc->GetRegNum())); | ||||||||
| unsigned wasmLclIndex = WasmRegToIndex(varDsc->GetRegNum()); | ||||||||
| GetEmitter()->emitIns_I(INS_local_get, emitTypeSize(tree), wasmLclIndex); | ||||||||
| // In this case, the resulting tree type may be different from the local var type where the value originates, | ||||||||
| // and so we need an explicit conversion since we can't "load" | ||||||||
| // the value with a different type like we can if the value is on the shadow stack. | ||||||||
| if (tree->TypeIs(TYP_INT) && varDsc->TypeIs(TYP_LONG)) | ||||||||
| { | ||||||||
| GetEmitter()->emitIns(INS_i32_wrap_i64); | ||||||||
adamperlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
|
|
||||||||
Uh oh!
There was an error while loading. Please reload this page.