Bläddra i källkod

Improved handling of signed sequences in OptPass2Movx

J. Gareth "Curious Kit" Moreton 3 år sedan
förälder
incheckning
d255ffba8b
1 ändrade filer med 149 tillägg och 48 borttagningar
  1. 149 48
      compiler/x86/aoptx86.pas

+ 149 - 48
compiler/x86/aoptx86.pas

@@ -7942,7 +7942,7 @@ unit aoptx86;
         movzx_cascade;
       var
         ThisReg: TRegister;
-        MinSize, MaxSize, TrySmaller, TargetSize: TOpSize;
+        MinSize, MaxSize, TryShiftDown, TargetSize: TOpSize;
         TargetSubReg: TSubRegister;
         hp1, hp2: tai;
         RegInUse, RegChanged, p_removed: Boolean;
@@ -7951,13 +7951,17 @@ unit aoptx86;
           GetNextInstructionUsingReg multiple times }
         InstrList: array of taicpu;
         InstrMax, Index: Integer;
-        UpperLimit, TrySmallerLimit: TCgInt;
+        UpperLimit, SignedUpperLimit, SignedLowerLimitBottom,
+        LowerLimit, SignedLowerLimit, SignedLowerLimitBottom,
+        TryShiftDownLimit, TryShiftDownSignedLimit, TryShiftDownSignedLimitLower,
+        WorkingValue: TCgInt;
 
         PreMessage: string;
 
         { Data flow analysis }
-        TestValMin, TestValMax: TCgInt;
-        SmallerOverflow, BitwiseOnly, OrXorUsed: Boolean;
+        TestValMin, TestValMax, TestValSignedMax: TCgInt;
+        BitwiseOnly, OrXorUsed,
+        ShiftDownOverflow, SignedOverflow, UnsignedOverflow, LowerSignedOverflow, UpperSignedOverflow: Boolean;
 
       begin
         Result := False;
@@ -8074,15 +8078,29 @@ unit aoptx86;
 {$endif i386 or i8086}
 
               UpperLimit := $FF;
+              SignedLowerLimit := $7F;
+              SignedLowerLimitBottom := -128;
               MinSize := S_B;
               if taicpu(p).opsize = S_BW then
-                MaxSize := S_W
+                begin
+                  MaxSize := S_W;
+                  SignedUpperLimit := $7F;
+                  SignedLowerLimitBottom := -32768;
+                end
               else
-                MaxSize := S_L;
+                begin
+                  MaxSize := S_L;
+                  SignedUpperLimit := $7FFFFFFF;
+                  SignedUpperLimitBottom := -2147483648;
+                end;
             end;
           S_WL:
             begin
               UpperLimit := $FFFF;
+              SignedUpperLimit := $7FFF;
+              SignedLowerLimitBottom := -32768;
+              SignedUpperLimit := $7FFFFFFF;
+              SignedUpperLimitBottom := -2147483648;
               MinSize := S_W;
               MaxSize := S_L;
             end
@@ -8092,12 +8110,17 @@ unit aoptx86;
 
         TestValMin := 0;
         TestValMax := UpperLimit;
-        TrySmallerLimit := UpperLimit;
-        TrySmaller := S_NO;
-        SmallerOverflow := False;
+        TestValSignedMax := SignedUpperLimit;
+        TryShiftDownLimit := UpperLimit;
+        TryShiftDown := S_NO;
+        ShiftDownOverflow := False;
         RegChanged := False;
         BitwiseOnly := True;
         OrXorUsed := False;
+        SignedOverflow := False;
+        LowerSignedOverflow := False;
+        UnsignedOverflow := False;
+        LowerUnsignedOverflow := False;
 
         hp1 := p;
 
@@ -8122,19 +8145,35 @@ unit aoptx86;
                     begin
                       Inc(TestValMin);
                       Inc(TestValMax);
+                      Inc(TestValSignedMax);
                     end
                   else
                     begin
                       Dec(TestValMin);
                       Dec(TestValMax);
+                      Dec(TestValSignedMax);
                     end;
                 end;
 
-              A_CMP:
+              A_TEST, A_CMP:
                 begin
-                  if (taicpu(hp1).oper[1]^.typ <> top_reg) or
+                  if (
+                      { Too high a risk of non-linear behaviour that breaks DFA
+                        here, unless it's cmp $0,%reg, which is equivalent to
+                        test %reg,%reg }
+                      OrXorUsed and
+                      (taicpu(hp1).opcode = A_CMP) and
+                      not Matchoperand(taicpu(hp1).oper[0]^, 0)
+                    ) or
+                    (taicpu(hp1).oper[1]^.typ <> top_reg) or
                     { Has to be an exact match on the register }
                     (taicpu(hp1).oper[1]^.reg <> ThisReg) or
+                    (
+                      { Permit "test %reg,%reg" }
+                      (taicpu(hp1).opcode = A_TEST) and
+                      (taicpu(hp1).oper[0]^.typ = top_reg) and
+                      (taicpu(hp1).oper[0]^.reg <> ThisReg)
+                    ) or
                     (taicpu(hp1).oper[0]^.typ <> top_const) or
                     { Make sure the comparison value is not smaller than the
                       smallest allowed signed value for the minimum size (e.g.
@@ -8142,17 +8181,45 @@ unit aoptx86;
                     not (
                       ((taicpu(hp1).oper[0]^.val and UpperLimit) = taicpu(hp1).oper[0]^.val) or
                       { Is it in the negative range? }
-                      (((not taicpu(hp1).oper[0]^.val) and (UpperLimit shr 1)) = (not taicpu(hp1).oper[0]^.val))
+                      (taicpu(hp1).oper[0]^.val >= SignedLowerLimitBottom)
                     ) then
                     Break;
 
-                  TestValMin := TestValMin - taicpu(hp1).oper[0]^.val;
-                  TestValMax := TestValMax - taicpu(hp1).oper[0]^.val;
-
-                  if (TestValMin < TrySmallerLimit) or (TestValMax < TrySmallerLimit) or
-                    (TestValMin > UpperLimit) or (TestValMax > UpperLimit) then
-                    { Overflow }
-                    Break;
+                  { ANDing can't increase the value past the limit or decrease
+                    it below 0, so we can skip the checks, plus the test value
+                    won't change afterwards }
+                  if (taicpu(hp1).opcode = A_CMP) and
+                    { cmp $0,$reg is equivalent to test %reg,%reg, plus the
+                      test values aren't being modified anyway }
+                    (taicpu(hp1).oper[0]^.val <> 0) then
+                    begin
+                      WorkingValue := taicpu(hp1).oper[0]^.val;
+
+                      TestValMin := TestValMin - WorkingValue;
+                      TestValMax := TestValMax - WorkingValue;
+                      TestValSignedMax := TestValSignedMax - WorkingValue;
+
+                      if (TestValSignedMax > SignedUpperLimit) then
+                        SignedOverflow := True;
+
+                      if (TestValMin > UpperLimit) or (TestValMax > UpperLimit) then
+                        { Absolute overflow }
+                        Break
+                      else if not ShiftDownOverflow and (TryShiftDown <> S_NO) and
+                        ((TestValMin > TryShiftDownLimit) or (TestValMax > TryShiftDownLimit)) then
+                        ShiftDownOverflow := True
+                      else if (TestValMin < SignedLowerLimitBottom) or (TestValMax < SignedLowerLimitBottom) then
+                        { Absolute overflow }
+                        Break
+                      else if (TestValMin < 0) or (TestValMax < 0) then
+                        UnsignedOverflow := True;
+
+                      { Because the register isn't actually adjusted, we can
+                        restore the test values to what they were previously }
+                      TestValMin := TestValMin + WorkingValue;
+                      TestValMax := TestValMax + WorkingValue;
+                      TestValSignedMax := TestValSignedMax + WorkingValue;
+                    end;
 
                   { Check to see if the active register is used afterwards }
                   TransferUsedRegs(TmpUsedRegs);
@@ -8216,7 +8283,7 @@ unit aoptx86;
                     end;
                 end;
 
-              A_ADD,A_SUB,A_AND,A_OR,A_XOR,A_SHL,A_SHR:
+              A_ADD,A_SUB,A_AND,A_OR,A_XOR,A_SHL,A_SHR,A_SAR:
                 begin
                   if
                     (taicpu(hp1).oper[1]^.typ <> top_reg) or
@@ -8266,11 +8333,13 @@ unit aoptx86;
                           begin
                             TestValMin := TestValMin * 2;
                             TestValMax := TestValMax * 2;
+                            TestValSignedMax := TestValSignedMax * 2;
                           end
                         else
                           begin
                             TestValMin := TestValMin + taicpu(hp1).oper[0]^.val;
                             TestValMax := TestValMax + taicpu(hp1).oper[0]^.val;
+                            TestValSignedMax := TestValSignedMax + taicpu(hp1).oper[0]^.val;
                           end;
                       end;
                     A_SUB:
@@ -8285,11 +8354,13 @@ unit aoptx86;
                           begin
                             TestValMin := 0;
                             TestValMax := 0;
+                            TestValSignedMax := 0;
                           end
                         else
                           begin
                             TestValMin := TestValMin - taicpu(hp1).oper[0]^.val;
                             TestValMax := TestValMax - taicpu(hp1).oper[0]^.val;
+                            TestValSignedMax := TestValSignedMax - taicpu(hp1).oper[0]^.val;
                           end;
                       end;
                     A_AND:
@@ -8304,21 +8375,21 @@ unit aoptx86;
                                 if ((taicpu(hp1).oper[0]^.val and $FF) = taicpu(hp1).oper[0]^.val) or
                                   ((not(taicpu(hp1).oper[0]^.val) and $7F) = (not taicpu(hp1).oper[0]^.val)) then
                                   begin
-                                    TrySmaller := S_B;
-                                    TrySmallerLimit := $FF;
+                                    TryShiftDown := S_B;
+                                    TryShiftDownLimit := $FF;
                                   end;
                               S_L:
                                 if ((taicpu(hp1).oper[0]^.val and $FF) = taicpu(hp1).oper[0]^.val) or
                                   ((not(taicpu(hp1).oper[0]^.val) and $7F) = (not taicpu(hp1).oper[0]^.val)) then
                                   begin
-                                    TrySmaller := S_B;
-                                    TrySmallerLimit := $FF;
+                                    TryShiftDown := S_B;
+                                    TryShiftDownLimit := $FF;
                                   end
                                 else if ((taicpu(hp1).oper[0]^.val and $FFFF) = taicpu(hp1).oper[0]^.val) or
                                   ((not(taicpu(hp1).oper[0]^.val) and $7FFF) = (not taicpu(hp1).oper[0]^.val)) then
                                   begin
-                                    TrySmaller := S_W;
-                                    TrySmallerLimit := $FFFF;
+                                    TryShiftDown := S_W;
+                                    TryShiftDownLimit := $FFFF;
                                   end;
                               else
                                 InternalError(2020112320);
@@ -8326,6 +8397,7 @@ unit aoptx86;
 
                           TestValMin := TestValMin and taicpu(hp1).oper[0]^.val;
                           TestValMax := TestValMax and taicpu(hp1).oper[0]^.val;
+                          TestValSignedMax := TestValSignedMax and taicpu(hp1).oper[0]^.val;
                         end;
                     A_OR:
                       begin
@@ -8336,6 +8408,7 @@ unit aoptx86;
 
                         TestValMin := TestValMin or taicpu(hp1).oper[0]^.val;
                         TestValMax := TestValMax or taicpu(hp1).oper[0]^.val;
+                        TestValSignedMax := TestValSignedMax or taicpu(hp1).oper[0]^.val;
                       end;
                     A_XOR:
                       begin
@@ -8346,13 +8419,17 @@ unit aoptx86;
 
                         TestValMin := TestValMin xor taicpu(hp1).oper[0]^.val;
                         TestValMax := TestValMax xor taicpu(hp1).oper[0]^.val;
+                        TestValSignedMax := TestValSignedMax xor taicpu(hp1).oper[0]^.val;
                       end;
                     A_SHL:
                       begin
                         TestValMin := TestValMin shl taicpu(hp1).oper[0]^.val;
                         TestValMax := TestValMax shl taicpu(hp1).oper[0]^.val;
+                        TestValSignedMax := TestValSignedMax shl taicpu(hp1).oper[0]^.val;
                       end;
-                    A_SHR:
+                    A_SHR,
+                    { The first instruction was MOVZX, so the value won't be negative }
+                    A_SAR:
                       begin
                         { we might be able to go smaller if SHR appears first }
                         if InstrMax = -1 then
@@ -8362,26 +8439,42 @@ unit aoptx86;
                             S_W:
                               if (taicpu(hp1).oper[0]^.val >= 8) then
                                 begin
-                                  TrySmaller := S_B;
-                                  TrySmallerLimit := $FF;
+                                  TryShiftDown := S_B;
+                                  TryShiftDownLimit := $FF;
+                                  TryShiftDownSignedLimit := $7F;
+                                  TrySmallerSignedLowerLimit := -128;
                                 end;
                             S_L:
                               if (taicpu(hp1).oper[0]^.val >= 24) then
                                 begin
-                                  TrySmaller := S_B;
-                                  TrySmallerLimit := $FF;
+                                  TryShiftDown := S_B;
+                                  TryShiftDownLimit := $FF;
+                                  TryShiftDownSignedLimit := $7F;
+                                  TrySmallerSignedLowerLimit := -128;
                                 end
                               else if (taicpu(hp1).oper[0]^.val >= 16) then
                                 begin
-                                  TrySmaller := S_W;
-                                  TrySmallerLimit := $FFFF;
+                                  TryShiftDown := S_W;
+                                  TryShiftDownLimit := $FFFF;
+                                  TryShiftDownSignedLimit := $7FFF;
+                                  TrySmallerSignedLowerLimit := -32768;
                                 end;
                             else
                               InternalError(2020112321);
                           end;
 
-                        TestValMin := TestValMin shr taicpu(hp1).oper[0]^.val;
-                        TestValMax := TestValMax shr taicpu(hp1).oper[0]^.val;
+                        if taicpu(hp1).opcode = A_SAR then
+                          begin
+                            TestValMin := SarInt64(TestValMin, taicpu(hp1).oper[0]^.val);
+                            TestValMax := SarInt64(TestValMax, taicpu(hp1).oper[0]^.val);
+                            TestValSignedMax := SarInt64(TestValSignedMax, taicpu(hp1).oper[0]^.val);
+                          end
+                        else
+                          begin
+                            TestValMin := TestValMin shr taicpu(hp1).oper[0]^.val;
+                            TestValMax := TestValMax shr taicpu(hp1).oper[0]^.val;
+                            TestValSignedMax := TestValSignedMax shr taicpu(hp1).oper[0]^.val;
+                          end;
                       end;
                     else
                       InternalError(2020112303);
@@ -8400,6 +8493,7 @@ unit aoptx86;
 
                       TestValMin := TestValMin * TestValMin;
                       TestValMax := TestValMax * TestValMax;
+                      TestValSignedMax := TestValSignedMax * TestValMax;
                     end;
                   3:
                     begin
@@ -8414,6 +8508,7 @@ unit aoptx86;
 
                       TestValMin := TestValMin * taicpu(hp1).oper[0]^.val;
                       TestValMax := TestValMax * taicpu(hp1).oper[0]^.val;
+                      TestValSignedMax := TestValSignedMax * taicpu(hp1).oper[0]^.val;
                     end;
                   else
                     Break;
@@ -8434,6 +8529,7 @@ unit aoptx86;
 
                       TestValMin := TestValMin div taicpu(hp1).oper[0]^.val;
                       TestValMax := TestValMax div taicpu(hp1).oper[0]^.val;
+                      TestValSignedMax := TestValSignedMax div taicpu(hp1).oper[0]^.val;
                     end;
                   else
                     Break;
@@ -8442,7 +8538,7 @@ unit aoptx86;
               A_MOVSX{$ifdef x86_64}, A_MOVSXD{$endif x86_64}:
                 begin
                   { If there are no instructions in between, then we might be able to make a saving }
-                  if (InstrMax <> -1) or (taicpu(hp1).oper[0]^.typ <> top_reg) or (taicpu(hp1).oper[0]^.reg <> ThisReg) then
+                  if SignedOverflow or (taicpu(hp1).oper[0]^.typ <> top_reg) or (taicpu(hp1).oper[0]^.reg <> ThisReg) then
                     Break;
 
                   { We have something like:
@@ -8481,7 +8577,7 @@ unit aoptx86;
 
               A_MOVZX:
                 begin
-                  if not MatchOpType(taicpu(hp1), top_reg, top_reg) then
+                  if UnsignedOverflow or (taicpu(hp1).oper[0]^.typ <> top_reg) then
                     Break;
 
                   if not SuperRegistersEqual(taicpu(hp1).oper[0]^.reg, ThisReg) then
@@ -8513,10 +8609,10 @@ movzx_cascade:
                           TargetSize := S_L;
                           TargetSubReg := R_SUBD;
                         end
-                      else if ((TrySmaller in [S_B, S_W]) and not SmallerOverflow) then
+                      else if ((TryShiftDown in [S_B, S_W]) and not ShiftDownOverflow) then
                         begin
-                          TargetSize := TrySmaller;
-                          if TrySmaller = S_B then
+                          TargetSize := TryShiftDown;
+                          if TryShiftDown = S_B then
                             TargetSubReg := R_SUBL
                           else
                             TargetSubReg := R_SUBW;
@@ -8530,7 +8626,7 @@ movzx_cascade:
                           TargetSize := S_W;
                           TargetSubReg := R_SUBW;
                         end
-                      else if ((TrySmaller = S_B) and not SmallerOverflow) then
+                      else if ((TryShiftDown = S_B) and not ShiftDownOverflow) then
                         begin
                           TargetSize := S_B;
                           TargetSubReg := R_SUBL;
@@ -8544,7 +8640,7 @@ movzx_cascade:
                           TargetSize := S_L;
                           TargetSubReg := R_SUBD;
                         end
-                      else if ((TrySmaller = S_B) and not SmallerOverflow) then
+                      else if ((TryShiftDown = S_B) and not ShiftDownOverflow) then
                         begin
                           TargetSize := S_B;
                           TargetSubReg := R_SUBL;
@@ -8752,13 +8848,18 @@ movzx_cascade:
                 Break;
             end;
 
-            if (TestValMin < 0) or (TestValMax < 0) or
-              (TestValMin > UpperLimit) or (TestValMax > UpperLimit) then
-              { Overflow }
+            if (TestValSignedMax > SignedLowerLimit) or (TestValSignedMax < SignedLowerLimitBottom) then
+              SignedOverflow := True;
+
+            if (TestValMin > UpperLimit) or (TestValMax > UpperLimit) or (TestValSignedMax > UpperLimit) or
+              (TestValMin < SignedLowerLimitBottom) or (TestValMax < SignedLowerLimitBottom) or (TestValSignedMax > SignedLowerLimitBottom) then
+              { Absolute overflow }
               Break
-            else if not SmallerOverflow and (TrySmaller <> S_NO) and
-              ((TestValMin > TrySmallerLimit) or (TestValMax > TrySmallerLimit)) then
-              SmallerOverflow := True;
+            else if not ShiftDownOverflow and (TryShiftDown <> S_NO) and
+              ((TestValMin > TryShiftDownLimit) or (TestValMax > TryShiftDownLimit)) then
+              ShiftDownOverflow := True
+            else if (TestValMin < 0) or (TestValMax < 0) then
+              UnsignedOverflow := True;
 
             { Contains highest index (so instruction count - 1) }
             Inc(InstrMax);