Eliminate chained casts to small types (#52561)
authorSingleAccretion <62474226+SingleAccretion@users.noreply.github.com>
Fri, 25 Jun 2021 06:32:54 +0000 (09:32 +0300)
committerGitHub <noreply@github.com>
Fri, 25 Jun 2021 06:32:54 +0000 (23:32 -0700)
We can take advantage of the implicit zero/sign-extension for
small integer types and eliminate some casts.

src/coreclr/jit/morph.cpp

index e53c2d9..946fe11 100644 (file)
@@ -730,14 +730,54 @@ OPTIMIZECAST:
             break;
 
             case GT_CAST:
-                /* Check for two consecutive casts into the same dstType */
+                // Check for two consecutive casts into the same dstType.
+                // Also check for consecutive casts to small types.
                 if (!tree->gtOverflow())
                 {
-                    var_types dstType2 = oper->CastToType();
-                    if (dstType == dstType2)
+                    var_types dstCastToType = dstType;
+                    var_types srcCastToType = oper->CastToType();
+                    if (dstCastToType == srcCastToType)
                     {
                         goto REMOVE_CAST;
                     }
+                    // We can take advantage of the implicit zero/sign-extension for
+                    // small integer types and eliminate some casts.
+                    if (opts.OptimizationEnabled() && !oper->gtOverflow() && !gtIsActiveCSE_Candidate(oper) &&
+                        varTypeIsSmall(dstCastToType) && varTypeIsSmall(srcCastToType))
+                    {
+                        // Gather some facts about our casts.
+                        bool     srcZeroExtends = varTypeIsUnsigned(srcCastToType);
+                        bool     dstZeroExtends = varTypeIsUnsigned(dstCastToType);
+                        unsigned srcCastToSize  = genTypeSize(srcCastToType);
+                        unsigned dstCastToSize  = genTypeSize(dstCastToType);
+
+                        // If the previous cast to a smaller type was zero-extending,
+                        // this cast will also always be zero-extending. Example:
+                        // CAST(ubyte): 000X => CAST(short): Sign-extend(0X) => 000X.
+                        if (srcZeroExtends && (dstCastToSize > srcCastToSize))
+                        {
+                            dstZeroExtends = true;
+                        }
+
+                        // Case #1: cast to a smaller or equal in size type.
+                        // We can discard the intermediate cast. Examples:
+                        // CAST(short): --XX => CAST(ubyte): 000X.
+                        // CAST(ushort): 00XX => CAST(short): --XX.
+                        if (dstCastToSize <= srcCastToSize)
+                        {
+                            tree->AsCast()->CastOp() = oper->AsCast()->CastOp();
+                            DEBUG_DESTROY_NODE(oper);
+                        }
+                        // Case #2: cast to a larger type with the same effect.
+                        // Here we can eliminate the outer cast. Example:
+                        // CAST(byte): ---X => CAST(short): Sign-extend(-X) => ---X.
+                        // Example of a sequence where this does not hold:
+                        // CAST(byte): ---X => CAST(ushort): Zero-extend(-X) => 00-X.
+                        else if (srcZeroExtends == dstZeroExtends)
+                        {
+                            goto REMOVE_CAST;
+                        }
+                    }
                 }
                 break;