@@ -747,16 +747,19 @@ static constexpr uint32_t PackTypes(var_types toType, var_types fromType)
747747//
748748void CodeGen::genIntToIntCast (GenTreeCast* cast)
749749{
750- if (cast->gtOverflow ())
750+ GenIntCastDesc desc (cast);
751+
752+ if (desc.CheckKind () != GenIntCastDesc::CHECK_NONE)
751753 {
752- NYI_WASM (" Overflow checks" );
754+ GenTree* castValue = cast->gtGetOp1 ();
755+ regNumber castReg = GetMultiUseOperandReg (castValue);
756+ genIntCastOverflowCheck (cast, desc, castReg);
753757 }
754758
755- GenIntCastDesc desc (cast);
756- var_types toType = genActualType (cast->CastToType ());
757- var_types fromType = genActualType (cast->CastOp ());
758- int extendSize = desc.ExtendSrcSize ();
759- instruction ins = INS_none;
759+ var_types toType = genActualType (cast->CastToType ());
760+ var_types fromType = genActualType (cast->CastOp ());
761+ int extendSize = desc.ExtendSrcSize ();
762+ instruction ins = INS_none;
760763 assert (fromType == TYP_INT || fromType == TYP_LONG);
761764
762765 genConsumeOperands (cast);
@@ -819,6 +822,98 @@ void CodeGen::genIntToIntCast(GenTreeCast* cast)
819822 WasmProduceReg (cast);
820823}
821824
825+ // ------------------------------------------------------------------------
826+ // genIntCastOverflowCheck: Generate overflow checking code for an integer cast.
827+ //
828+ // Arguments:
829+ // cast - The GT_CAST node
830+ // desc - The cast description
831+ // reg - The register containing the value to check
832+ //
833+ void CodeGen::genIntCastOverflowCheck (GenTreeCast* cast, const GenIntCastDesc& desc, regNumber reg)
834+ {
835+ bool const is64BitSrc = (desc.CheckSrcSize () == 8 );
836+ emitAttr const srcSize = is64BitSrc ? EA_8BYTE : EA_4BYTE;
837+
838+ GetEmitter ()->emitIns_I (INS_local_get, srcSize, WasmRegToIndex (reg));
839+
840+ switch (desc.CheckKind ())
841+ {
842+ case GenIntCastDesc::CHECK_POSITIVE:
843+ {
844+ // INT or LONG to ULONG
845+ GetEmitter ()->emitIns_I (is64BitSrc ? INS_i64_const : INS_i32_const, srcSize, 0 );
846+ GetEmitter ()->emitIns (is64BitSrc ? INS_i64_lt_s : INS_i32_lt_s);
847+ genJumpToThrowHlpBlk (SCK_OVERFLOW);
848+ break ;
849+ }
850+
851+ case GenIntCastDesc::CHECK_UINT_RANGE:
852+ {
853+ // (U)LONG to UINT
854+ assert (is64BitSrc);
855+ GetEmitter ()->emitIns_I (INS_i64_const, srcSize, UINT32_MAX);
856+ // We can re-interpret LONG as ULONG
857+ // Then negative values will be larger than UINT32_MAX
858+ GetEmitter ()->emitIns (INS_i64_gt_u);
859+ genJumpToThrowHlpBlk (SCK_OVERFLOW);
860+ break ;
861+ }
862+
863+ case GenIntCastDesc::CHECK_POSITIVE_INT_RANGE:
864+ {
865+ // ULONG to INT
866+ GetEmitter ()->emitIns_I (INS_i64_const, srcSize, INT32_MAX);
867+ GetEmitter ()->emitIns (INS_i64_gt_u);
868+ genJumpToThrowHlpBlk (SCK_OVERFLOW);
869+ break ;
870+ }
871+
872+ case GenIntCastDesc::CHECK_INT_RANGE:
873+ {
874+ // LONG to INT
875+ GetEmitter ()->emitIns (INS_i64_extend32_s);
876+ GetEmitter ()->emitIns_I (INS_local_get, srcSize, WasmRegToIndex (reg));
877+ GetEmitter ()->emitIns (INS_i64_ne);
878+ genJumpToThrowHlpBlk (SCK_OVERFLOW);
879+ break ;
880+ }
881+
882+ case GenIntCastDesc::CHECK_SMALL_INT_RANGE:
883+ {
884+ // (U)(INT|LONG) to Small INT
885+ const int castMaxValue = desc.CheckSmallIntMax ();
886+ const int castMinValue = desc.CheckSmallIntMin ();
887+
888+ if (castMinValue == 0 )
889+ {
890+ // When the minimum is 0, a single unsigned upper-bound check is sufficient.
891+ // For signed sources, negative values become large unsigned values and
892+ // thus also trigger the overflow via the same comparison.
893+ GetEmitter ()->emitIns_I (is64BitSrc ? INS_i64_const : INS_i32_const, srcSize, castMaxValue);
894+ GetEmitter ()->emitIns (is64BitSrc ? INS_i64_gt_u : INS_i32_gt_u);
895+ }
896+ else
897+ {
898+ // We need to check a range around zero, eg [-128, 127] for 8-bit signed.
899+ // Do two compares and combine the results: (src > max) | (src < min).
900+ assert (!cast->IsUnsigned ());
901+ GetEmitter ()->emitIns_I (is64BitSrc ? INS_i64_const : INS_i32_const, srcSize, castMaxValue);
902+ GetEmitter ()->emitIns (is64BitSrc ? INS_i64_gt_s : INS_i32_gt_s);
903+ GetEmitter ()->emitIns_I (INS_local_get, srcSize, WasmRegToIndex (reg));
904+ GetEmitter ()->emitIns_I (is64BitSrc ? INS_i64_const : INS_i32_const, srcSize, castMinValue);
905+ GetEmitter ()->emitIns (is64BitSrc ? INS_i64_lt_s : INS_i32_lt_s);
906+ GetEmitter ()->emitIns (INS_i32_or);
907+ }
908+ genJumpToThrowHlpBlk (SCK_OVERFLOW);
909+ break ;
910+ }
911+
912+ default :
913+ unreached ();
914+ }
915+ }
916+
822917// ------------------------------------------------------------------------
823918// genFloatToIntCast: Generate code for a floating point to integer cast
824919//
@@ -1368,6 +1463,22 @@ void CodeGen::genJumpToThrowHlpBlk(SpecialCodeKind codeKind)
13681463void CodeGen::genCodeForNullCheck (GenTreeIndir* tree)
13691464{
13701465 genConsumeAddress (tree->Addr ());
1466+ genEmitNullCheck (REG_NA);
1467+ }
1468+
1469+ // ---------------------------------------------------------------------
1470+ // genEmitNullCheck - generate code for a null check
1471+ //
1472+ // Arguments:
1473+ // regNum - register to check, or REG_NA if value to check is on the stack
1474+ //
1475+ void CodeGen::genEmitNullCheck (regNumber reg)
1476+ {
1477+ if (reg != REG_NA)
1478+ {
1479+ GetEmitter ()->emitIns_I (INS_local_get, EA_PTRSIZE, WasmRegToIndex (reg));
1480+ }
1481+
13711482 GetEmitter ()->emitIns_I (INS_I_const, EA_PTRSIZE, m_compiler->compMaxUncheckedOffsetForNullObject );
13721483 GetEmitter ()->emitIns (INS_I_le_u);
13731484 genJumpToThrowHlpBlk (SCK_NULL_CHECK);
@@ -1629,15 +1740,20 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
16291740// ------------------------------------------------------------------------
16301741// genCall: Produce code for a GT_CALL node
16311742//
1743+ // Arguments:
1744+ // call - the GT_CALL node
1745+ //
16321746void CodeGen::genCall (GenTreeCall* call)
16331747{
1748+ regNumber thisReg = REG_NA;
1749+
16341750 if (call->NeedsNullCheck ())
16351751 {
1636- NYI_WASM (" Insert nullchecks for calls that need it in lowering" );
1752+ CallArg* thisArg = call->gtArgs .GetThisArg ();
1753+ GenTree* thisNode = thisArg->GetNode ();
1754+ thisReg = GetMultiUseOperandReg (thisNode);
16371755 }
16381756
1639- assert (!call->IsTailCall ());
1640-
16411757 for (CallArg& arg : call->gtArgs .EarlyArgs ())
16421758 {
16431759 genConsumeReg (arg.GetEarlyNode ());
@@ -1648,6 +1764,11 @@ void CodeGen::genCall(GenTreeCall* call)
16481764 genConsumeReg (arg.GetLateNode ());
16491765 }
16501766
1767+ if (call->NeedsNullCheck ())
1768+ {
1769+ genEmitNullCheck (thisReg);
1770+ }
1771+
16511772 genCallInstruction (call);
16521773 WasmProduceReg (call);
16531774}
0 commit comments