@@ -486,6 +486,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
486486 genCodeForConstant (treeNode);
487487 break ;
488488
489+ case GT_CAST:
490+ genCodeForCast (treeNode->AsOp ());
491+ break ;
492+
489493 case GT_NEG:
490494 case GT_NOT:
491495 genCodeForNegNot (treeNode->AsOp ());
@@ -593,22 +597,218 @@ static constexpr uint32_t PackOperAndType(genTreeOps oper, var_types type)
593597 {
594598 type = TYP_I_IMPL;
595599 }
596- static_assert ((ssize_t )GT_COUNT > (ssize_t )TYP_COUNT);
597- return ((uint32_t )oper << (ConstLog2<GT_COUNT>::value + 1 )) | ((uint32_t )type);
600+ const int shift1 = ConstLog2<TYP_COUNT>::value + 1 ;
601+ return ((uint32_t )oper << shift1) | ((uint32_t )type);
602+ }
603+
604+ // ------------------------------------------------------------------------
605+ // PackTypes: Pack two var_types together into a uint32_t
606+
607+ // Arguments:
608+ // toType - a var_types to pack
609+ // fromType - a var_types to pack
610+ //
611+ // Return Value:
612+ // The two types packed together into an integer that can be used as a switch/value,
613+ // the primary use case being the handling of operations with two-type variants such
614+ // as casts.
615+ //
616+ static constexpr uint32_t PackTypes (var_types toType, var_types fromType)
617+ {
618+ if (toType == TYP_BYREF || toType == TYP_REF)
619+ {
620+ toType = TYP_I_IMPL;
621+ }
622+ if (fromType == TYP_BYREF || fromType == TYP_REF)
623+ {
624+ fromType = TYP_I_IMPL;
625+ }
626+ const int shift1 = ConstLog2<TYP_COUNT>::value + 1 ;
627+ return ((uint32_t )toType) | ((uint32_t )fromType << shift1);
598628}
599629
600630// ------------------------------------------------------------------------
601- // PackOperAndType: Pack a GenTreeOp* into a uint32_t
631+ // genIntToIntCast: Generate code for an integer to integer cast
602632//
603633// Arguments:
604- // treeNode - a GenTreeOp to extract oper and type from
634+ // cast - The GT_CAST node for the integer cast operation
605635//
606- // Return Value:
607- // the node's oper and type packed into an integer that can be used as a switch value
636+ // Notes:
637+ // Handles casts to and from small int, int, and long types
638+ // including proper sign extension and truncation as needed.
608639//
609- static uint32_t PackOperAndType (GenTreeOp* treeNode )
640+ void CodeGen::genIntToIntCast (GenTreeCast* cast )
610641{
611- return PackOperAndType (treeNode->OperGet (), treeNode->TypeGet ());
642+ if (cast->gtOverflow ())
643+ {
644+ NYI_WASM (" Overflow checks" );
645+ }
646+
647+ GenIntCastDesc desc (cast);
648+ var_types toType = genActualType (cast->CastToType ());
649+ var_types fromType = genActualType (cast->CastOp ());
650+ int extendSize = desc.ExtendSrcSize ();
651+ instruction ins = INS_none;
652+ assert (fromType == TYP_INT || fromType == TYP_LONG);
653+
654+ genConsumeOperands (cast);
655+
656+ // TODO-WASM: Handle load containment GenIntCastDesc::LOAD_* cases once we mark containment for loads
657+ switch (desc.ExtendKind ())
658+ {
659+ case GenIntCastDesc::COPY:
660+ {
661+ if (toType == TYP_INT && fromType == TYP_LONG)
662+ {
663+ ins = INS_i32_wrap_i64;
664+ }
665+ else
666+ {
667+ assert (toType == fromType);
668+ ins = INS_none;
669+ }
670+ break ;
671+ }
672+ case GenIntCastDesc::ZERO_EXTEND_SMALL_INT:
673+ {
674+ int andAmount = extendSize == 1 ? 255 : 65535 ;
675+ if (fromType == TYP_LONG)
676+ {
677+ GetEmitter ()->emitIns (INS_i32_wrap_i64);
678+ }
679+ GetEmitter ()->emitIns_I (INS_i32_const, EA_4BYTE, andAmount);
680+ ins = INS_i32_and;
681+ break ;
682+ }
683+ case GenIntCastDesc::SIGN_EXTEND_SMALL_INT:
684+ {
685+ if (fromType == TYP_LONG)
686+ {
687+ GetEmitter ()->emitIns (INS_i32_wrap_i64);
688+ }
689+ ins = (extendSize == 1 ) ? INS_i32_extend8_s : INS_i32_extend16_s;
690+
691+ break ;
692+ }
693+ case GenIntCastDesc::ZERO_EXTEND_INT:
694+ {
695+ ins = INS_i64_extend_u_i32;
696+ break ;
697+ }
698+ case GenIntCastDesc::SIGN_EXTEND_INT:
699+ {
700+ ins = INS_i64_extend_s_i32;
701+ break ;
702+ }
703+ default :
704+ unreached ();
705+ }
706+
707+ if (ins != INS_none)
708+ {
709+ GetEmitter ()->emitIns (ins);
710+ }
711+ genProduceReg (cast);
712+ }
713+
714+ // ------------------------------------------------------------------------
715+ // genFloatToIntCast: Generate code for a floating point to integer cast
716+ //
717+ // Arguments:
718+ // tree - The GT_CAST node for the float-to-int cast operation
719+ //
720+ // Notes:
721+ // Handles casts from TYP_FLOAT/TYP_DOUBLE to TYP_INT/TYP_LONG.
722+ // Uses saturating truncation instructions (trunc_sat) which clamp
723+ // out-of-range values rather than trapping.
724+ //
725+ void CodeGen::genFloatToIntCast (GenTree* tree)
726+ {
727+ if (tree->gtOverflow ())
728+ {
729+ NYI_WASM (" Overflow checks" );
730+ }
731+
732+ var_types toType = tree->TypeGet ();
733+ var_types fromType = tree->AsCast ()->CastOp ()->TypeGet ();
734+ bool isUnsigned = varTypeIsUnsigned (tree->AsCast ()->CastToType ());
735+ instruction ins = INS_none;
736+ assert (varTypeIsFloating (fromType) && (toType == TYP_INT || toType == TYP_LONG));
737+
738+ genConsumeOperands (tree->AsCast ());
739+
740+ switch (PackTypes (fromType, toType))
741+ {
742+ case PackTypes (TYP_FLOAT, TYP_INT):
743+ ins = isUnsigned ? INS_i32_trunc_sat_f32_u : INS_i32_trunc_sat_f32_s;
744+ break ;
745+ case PackTypes (TYP_DOUBLE, TYP_INT):
746+ ins = isUnsigned ? INS_i32_trunc_sat_f64_u : INS_i32_trunc_sat_f64_s;
747+ break ;
748+ case PackTypes (TYP_FLOAT, TYP_LONG):
749+ ins = isUnsigned ? INS_i64_trunc_sat_f32_u : INS_i64_trunc_sat_f32_s;
750+ break ;
751+ case PackTypes (TYP_DOUBLE, TYP_LONG):
752+ ins = isUnsigned ? INS_i64_trunc_sat_f64_u : INS_i64_trunc_sat_f64_s;
753+ break ;
754+ default :
755+ unreached ();
756+ }
757+
758+ GetEmitter ()->emitIns (ins);
759+ genProduceReg (tree);
760+ }
761+
762+ // ------------------------------------------------------------------------
763+ // genIntToFloatCast: Generate code for an integer to floating point cast
764+ //
765+ // Arguments:
766+ // tree - The GT_CAST node for the int-to-float cast operation
767+ //
768+ // Notes:
769+ // Handles casts from TYP_INT/TYP_LONG to TYP_FLOAT/TYP_DOUBLE.
770+ // Currently not implemented (NYI_WASM).
771+ //
772+ void CodeGen::genIntToFloatCast (GenTree* tree)
773+ {
774+ NYI_WASM (" genIntToFloatCast" );
775+ }
776+
777+ // ------------------------------------------------------------------------
778+ // genFloatToFloatCast: Generate code for a float to float cast
779+ //
780+ // Arguments:
781+ // tree - The GT_CAST node for the float-to-float cast operation
782+ //
783+ void CodeGen::genFloatToFloatCast (GenTree* tree)
784+ {
785+ var_types toType = tree->TypeGet ();
786+ var_types fromType = tree->AsCast ()->CastOp ()->TypeGet ();
787+ instruction ins = INS_none;
788+
789+ genConsumeOperands (tree->AsCast ());
790+
791+ switch (PackTypes (toType, fromType))
792+ {
793+ case PackTypes (TYP_FLOAT, TYP_DOUBLE):
794+ ins = INS_f32_demote_f64;
795+ break ;
796+ case PackTypes (TYP_DOUBLE, TYP_FLOAT):
797+ ins = INS_f64_promote_f32;
798+ break ;
799+ case PackTypes (TYP_FLOAT, TYP_FLOAT):
800+ case PackTypes (TYP_DOUBLE, TYP_DOUBLE):
801+ ins = INS_none;
802+ break ;
803+ default :
804+ unreached ();
805+ }
806+
807+ if (ins != INS_none)
808+ {
809+ GetEmitter ()->emitIns (ins);
810+ }
811+ genProduceReg (tree);
612812}
613813
614814// ------------------------------------------------------------------------
@@ -622,7 +822,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
622822 genConsumeOperands (treeNode);
623823
624824 instruction ins;
625- switch (PackOperAndType (treeNode))
825+ switch (PackOperAndType (treeNode-> OperGet (), treeNode-> TypeGet () ))
626826 {
627827 case PackOperAndType (GT_ADD, TYP_INT):
628828 if (treeNode->gtOverflow ())
@@ -717,7 +917,7 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
717917 genConsumeOperands (treeNode);
718918
719919 instruction ins;
720- switch (PackOperAndType (treeNode))
920+ switch (PackOperAndType (treeNode-> OperGet (), treeNode-> TypeGet () ))
721921 {
722922 case PackOperAndType (GT_DIV, TYP_INT):
723923 ins = INS_i32_div_s;
@@ -835,7 +1035,7 @@ void CodeGen::genCodeForShift(GenTree* tree)
8351035 // for both the shift and shiftee. So the shift may need to be extended (zero-extended) for TYP_LONG.
8361036
8371037 instruction ins;
838- switch (PackOperAndType (treeNode))
1038+ switch (PackOperAndType (treeNode-> OperGet (), treeNode-> TypeGet () ))
8391039 {
8401040 case PackOperAndType (GT_LSH, TYP_INT):
8411041 ins = INS_i32_shl;
@@ -894,7 +1094,7 @@ void CodeGen::genCodeForNegNot(GenTreeOp* tree)
8941094 genConsumeOperands (tree);
8951095
8961096 instruction ins;
897- switch (PackOperAndType (tree))
1097+ switch (PackOperAndType (tree-> OperGet (), tree-> TypeGet () ))
8981098 {
8991099 case PackOperAndType (GT_NOT, TYP_INT):
9001100 GetEmitter ()->emitIns_I (INS_i32_const, emitTypeSize (tree), -1 );
@@ -995,6 +1195,13 @@ void CodeGen::genCodeForLclVar(GenTreeLclVar* tree)
9951195 assert (genIsValidReg (varDsc->GetRegNum ()));
9961196 unsigned wasmLclIndex = WasmRegToIndex (varDsc->GetRegNum ());
9971197 GetEmitter ()->emitIns_I (INS_local_get, emitTypeSize (tree), wasmLclIndex);
1198+ // In this case, the resulting tree type may be different from the local var type where the value originates,
1199+ // and so we need an explicit conversion since we can't "load"
1200+ // the value with a different type like we can if the value is on the shadow stack.
1201+ if (tree->TypeIs (TYP_INT) && varDsc->TypeIs (TYP_LONG))
1202+ {
1203+ GetEmitter ()->emitIns (INS_i32_wrap_i64);
1204+ }
9981205 }
9991206}
10001207
0 commit comments