@@ -481,6 +481,18 @@ static constexpr uint32_t PackOperAndType(genTreeOps oper, var_types toType, var
481481 return ((uint32_t )oper << shift1) | ((uint32_t )fromType) | ((uint32_t )toType << shift2);
482482}
483483
484+ // ------------------------------------------------------------------------
485+ // PackTypes: Pack two var_types together into a uint32_t
486+
487+ // Arguments:
488+ // toType - a var_types to pack
489+ // fromType - a var_types to pack
490+ //
491+ // Return Value:
492+ // The two types packed together into an integer that can be used as a switch/value,
493+ // the primary use case being the handling of operations with two-type variants such
494+ // as casts.
495+ //
484496static constexpr uint32_t PackTypes (var_types toType, var_types fromType)
485497{
486498 if (toType == TYP_BYREF)
@@ -495,16 +507,26 @@ static constexpr uint32_t PackTypes(var_types toType, var_types fromType)
495507 return ((uint32_t )toType) | ((uint32_t )fromType << shift1);
496508}
497509
510+ // ------------------------------------------------------------------------
511+ // genIntToIntCast: Generate code for an integer to integer cast
512+ //
513+ // Arguments:
514+ // cast - The GT_CAST node for the integer cast operation
515+ //
516+ // Notes:
517+ // Handles casts to and from small int, int, and long types
518+ // including proper sign extension and truncation as needed.
519+ //
498520void CodeGen::genIntToIntCast (GenTreeCast* cast)
499521{
500522 GenIntCastDesc desc (cast);
501523 var_types toType = genActualType (cast->CastToType ());
502- var_types fromType = genActualType (cast->CastOp ()-> TypeGet () );
524+ var_types fromType = genActualType (cast->CastOp ());
503525 int extendSize = desc.ExtendSrcSize ();
504526 instruction ins = INS_none;
505527 assert (fromType == TYP_INT || fromType == TYP_LONG);
506528
507- genConsumeRegs (cast-> CastOp () );
529+ genConsumeOperands (cast);
508530
509531 switch (desc.ExtendKind ())
510532 {
@@ -523,53 +545,31 @@ void CodeGen::genIntToIntCast(GenTreeCast* cast)
523545 }
524546 case GenIntCastDesc::ExtendKind::ZERO_EXTEND_SMALL_INT:
525547 {
526- assert (extendSize <= 2 );
527- if (toType == TYP_LONG)
528- {
529- ins = INS_i64_extend_u_i32;
530- }
531- else
532- {
533- // We expect the underlying types to both be int here,
534- // so we don't need to do anything more
535- assert (toType == TYP_INT && fromType == TYP_INT);
536- ins = INS_none;
537- }
548+ int andAmount = extendSize == 1 ? 255 : 65535 ;
549+ GetEmitter ()->emitIns_I (INS_i32_const, EA_4BYTE, andAmount);
550+ GetEmitter ()->emitIns (INS_i32_and);
551+ ins = (toType == TYP_LONG) ? INS_i64_extend_u_i32 : INS_none;
538552 break ;
539553 }
540554 case GenIntCastDesc::ExtendKind::SIGN_EXTEND_SMALL_INT:
541555 {
542- assert (extendSize <= 2 );
543- switch (extendSize)
556+ if (fromType == TYP_LONG)
544557 {
545- case 1 :
546- ins = INS_i32_extend8_s;
547- break ;
548- case 2 :
549- ins = INS_i32_extend16_s;
550- break ;
551- default :
552- unreached ();
558+ // the actual runtime type here could be a long, so first we need to
559+ // truncate it to int in that case
560+ GetEmitter ()->emitIns (INS_i32_wrap_i64);
553561 }
562+ ins = (extendSize == 1 ) ? INS_i32_extend8_s : INS_i32_extend16_s;
554563
555- // A sign-extended cast from small int->long requires two instructions; first sign extended
556- // small int -> i32, then sign extended i32 -> i64
557- if (toType == TYP_LONG)
558- {
559- GetEmitter ()->emitIns (ins);
560- ins = INS_i64_extend_s_i32;
561- }
562564 break ;
563565 }
564566 case GenIntCastDesc::ExtendKind::ZERO_EXTEND_INT:
565567 {
566- assert (toType == TYP_LONG);
567568 ins = INS_i64_extend_u_i32;
568569 break ;
569570 }
570571 case GenIntCastDesc::ExtendKind::SIGN_EXTEND_INT:
571572 {
572- assert (toType == TYP_LONG);
573573 ins = INS_i64_extend_s_i32;
574574 break ;
575575 }
@@ -584,28 +584,39 @@ void CodeGen::genIntToIntCast(GenTreeCast* cast)
584584 genProduceReg (cast);
585585}
586586
587+ // ------------------------------------------------------------------------
588+ // genFloatToIntCast: Generate code for a floating point to integer cast
589+ //
590+ // Arguments:
591+ // tree - The GT_CAST node for the float-to-int cast operation
592+ //
593+ // Notes:
594+ // Handles casts from TYP_FLOAT/TYP_DOUBLE to TYP_INT/TYP_LONG.
595+ // Uses saturating truncation instructions (trunc_sat) which clamp
596+ // out-of-range values rather than trapping.
597+ //
587598void CodeGen::genFloatToIntCast (GenTree* tree)
588599{
589- var_types toType = genActualType ( tree->TypeGet () );
590- var_types fromType = genActualType ( tree->AsCast ()->CastFromType () );
600+ var_types toType = tree->TypeGet ();
601+ var_types fromType = tree->AsCast ()->CastOp ()-> TypeGet ( );
591602 bool isUnsigned = varTypeIsUnsigned (tree->AsCast ()->CastToType ());
592603 instruction ins = INS_none;
593604 assert (varTypeIsFloating (fromType) && (toType == TYP_INT || toType == TYP_LONG));
594605
595- genConsumeRegs (tree->AsCast ()-> CastOp ());
606+ genConsumeOperands (tree->AsCast ());
596607
597- switch (PackTypes (toType, fromType ))
608+ switch (PackTypes (fromType, toType ))
598609 {
599- case PackTypes (TYP_INT, TYP_FLOAT ):
610+ case PackTypes (TYP_FLOAT, TYP_INT ):
600611 ins = isUnsigned ? INS_i32_trunc_sat_f32_u : INS_i32_trunc_sat_f32_s;
601612 break ;
602- case PackTypes (TYP_INT, TYP_DOUBLE ):
613+ case PackTypes (TYP_DOUBLE, TYP_INT ):
603614 ins = isUnsigned ? INS_i32_trunc_sat_f64_u : INS_i32_trunc_sat_f64_s;
604615 break ;
605- case PackTypes (TYP_LONG, TYP_FLOAT ):
616+ case PackTypes (TYP_FLOAT, TYP_LONG ):
606617 ins = isUnsigned ? INS_i64_trunc_sat_f32_u : INS_i64_trunc_sat_f32_s;
607618 break ;
608- case PackTypes (TYP_LONG, TYP_DOUBLE ):
619+ case PackTypes (TYP_DOUBLE, TYP_LONG ):
609620 ins = isUnsigned ? INS_i64_trunc_sat_f64_u : INS_i64_trunc_sat_f64_s;
610621 break ;
611622 default :
@@ -616,18 +627,34 @@ void CodeGen::genFloatToIntCast(GenTree* tree)
616627 genProduceReg (tree);
617628}
618629
630+ // ------------------------------------------------------------------------
631+ // genIntToFloatCast: Generate code for an integer to floating point cast
632+ //
633+ // Arguments:
634+ // tree - The GT_CAST node for the int-to-float cast operation
635+ //
636+ // Notes:
637+ // Handles casts from TYP_INT/TYP_LONG to TYP_FLOAT/TYP_DOUBLE.
638+ // Currently not implemented (NYI_WASM).
639+ //
619640void CodeGen::genIntToFloatCast (GenTree* tree)
620641{
621642 NYI_WASM (" genIntToFloatCast" );
622643}
623644
645+ // ------------------------------------------------------------------------
646+ // genFloatToFloatCast: Generate code for a float to float cast
647+ //
648+ // Arguments:
649+ // tree - The GT_CAST node for the float-to-float cast operation
650+ //
624651void CodeGen::genFloatToFloatCast (GenTree* tree)
625652{
626- var_types toType = genActualType ( tree->TypeGet () );
627- var_types fromType = genActualType ( tree->AsCast ()->CastFromType () );
653+ var_types toType = tree->TypeGet ();
654+ var_types fromType = tree->AsCast ()->CastOp ()-> TypeGet ( );
628655 instruction ins = INS_none;
629656
630- genConsumeRegs (tree->AsCast ()-> CastOp ());
657+ genConsumeOperands (tree->AsCast ());
631658
632659 switch (PackTypes (toType, fromType))
633660 {
0 commit comments