Browse Source

* x86: Extension to CanBeCMOV that permits a potentially unsafe reference if it appears in the previous comparison

J. Gareth "Curious Kit" Moreton 3 years ago
parent
commit
4d57dee8d9
1 changed files with 48 additions and 19 deletions
  1. 48 19
      compiler/x86/aoptx86.pas

+ 48 - 19
compiler/x86/aoptx86.pas

@@ -128,7 +128,7 @@ unit aoptx86;
 
 
 
 
         { Returns true if the given MOV instruction can be safely converted to CMOV }
         { Returns true if the given MOV instruction can be safely converted to CMOV }
-        class function CanBeCMOV(p : tai) : boolean; static;
+        class function CanBeCMOV(p, cond_p: tai) : boolean; static;
 
 
         { Like UpdateUsedRegs, but ignores deallocations }
         { Like UpdateUsedRegs, but ignores deallocations }
         class procedure UpdateIntRegsNoDealloc(var AUsedRegs: TAllUsedRegs; p: Tai); static;
         class procedure UpdateIntRegsNoDealloc(var AUsedRegs: TAllUsedRegs; p: Tai); static;
@@ -11274,23 +11274,52 @@ unit aoptx86;
       end;
       end;
 
 
 
 
-    class function TX86AsmOptimizer.CanBeCMOV(p : tai) : boolean;
+    class function TX86AsmOptimizer.CanBeCMOV(p, cond_p: tai) : boolean;
       begin
       begin
-         CanBeCMOV:=assigned(p) and
-           MatchInstruction(p,A_MOV,[S_W,S_L,S_Q]) and
-           (taicpu(p).oper[1]^.typ = top_reg) and
-           (
-             (taicpu(p).oper[0]^.typ = top_reg) or
-             { allow references, but only pure symbols or got rel. addressing with RIP as based,
-               it is not expected that this can cause a seg. violation }
-             (
-               (taicpu(p).oper[0]^.typ = top_ref) and
-               { TODO: Can we detect which references become constants at this
-                 stage so we don't have to do a blanket ban? }
-               (taicpu(p).oper[0]^.ref^.refaddr <> addr_full) and
-               IsRefSafe(taicpu(p).oper[0]^.ref)
-             )
-           );
+        Result := assigned(p) and
+          MatchInstruction(p,A_MOV,[S_W,S_L,S_Q]) and
+          (taicpu(p).oper[1]^.typ = top_reg) and
+          (
+            (taicpu(p).oper[0]^.typ = top_reg) or
+            { allow references, but only pure symbols or got rel. addressing with RIP as based,
+              it is not expected that this can cause a seg. violation }
+            (
+              (taicpu(p).oper[0]^.typ = top_ref) and
+              { TODO: Can we detect which references become constants at this
+                stage so we don't have to do a blanket ban? }
+              (taicpu(p).oper[0]^.ref^.refaddr <> addr_full) and
+              (
+                IsRefSafe(taicpu(p).oper[0]^.ref) or
+                (
+                  { If the reference also appears in the condition, then we know it's safe, otherwise
+                    any kind of access violation would have occurred already }
+                  Assigned(cond_p) and
+                  { Make sure the sizes match too so we're reading and writing the same number of bytes }
+                  (cond_p.typ = ait_instruction) and
+                  (taicpu(cond_p).opsize = taicpu(p).opsize) and
+                  { Just consider 2-operand comparison instructions for now to be safe }
+                  (taicpu(cond_p).ops = 2) and
+                  (
+                    ((taicpu(cond_p).oper[1]^.typ = top_ref) and RefsEqual(taicpu(cond_p).oper[1]^.ref^, taicpu(p).oper[0]^.ref^)) or
+                    (
+                      (taicpu(cond_p).oper[0]^.typ = top_ref) and
+                      { Don't risk identical registers but different offsets, as we may have constructs
+                        such as buffer streams with things like length fields that indicate whether
+                        any more data follows.  And there are probably some contrived examples where
+                        writing to offsets behind the one being read also lead to access violations }
+                      RefsEqual(taicpu(cond_p).oper[0]^.ref^, taicpu(p).oper[0]^.ref^) and
+                      (
+                        { Check that we're not modifying a register that appears in the reference }
+                        (InsProp[taicpu(cond_p).opcode].Ch * [Ch_Mop2, Ch_RWop2, Ch_Wop2] = []) or
+                        (taicpu(cond_p).oper[1]^.typ <> top_reg) or
+                        not RegInRef(taicpu(cond_p).oper[1]^.reg, taicpu(cond_p).oper[0]^.ref^)
+                      )
+                    )
+                  )
+                )
+              )
+            )
+          );
       end;
       end;
 
 
 
 
@@ -11881,7 +11910,7 @@ unit aoptx86;
                            ait_instruction:
                            ait_instruction:
                              if MatchInstruction(hp1, A_MOV, [S_W, S_L{$ifdef x86_64}, S_Q{$endif x86_64}]) then
                              if MatchInstruction(hp1, A_MOV, [S_W, S_L{$ifdef x86_64}, S_Q{$endif x86_64}]) then
                                begin
                                begin
-                                 if CanBeCMOV(hp1) then
+                                 if CanBeCMOV(hp1, hp_prev) then
                                    Inc(l)
                                    Inc(l)
                                  else if not (cs_opt_size in current_settings.optimizerswitches) and
                                  else if not (cs_opt_size in current_settings.optimizerswitches) and
                                    { CMOV with constants grows the code size }
                                    { CMOV with constants grows the code size }
@@ -12085,7 +12114,7 @@ unit aoptx86;
                                 ait_instruction:
                                 ait_instruction:
                                   if MatchInstruction(hp1, A_MOV, [S_W, S_L{$ifdef x86_64}, S_Q{$endif x86_64}]) then
                                   if MatchInstruction(hp1, A_MOV, [S_W, S_L{$ifdef x86_64}, S_Q{$endif x86_64}]) then
                                     begin
                                     begin
-                                      if CanBeCMOV(hp1) then
+                                      if CanBeCMOV(hp1, hp_prev) then
                                         Inc(l)
                                         Inc(l)
                                       else if not (cs_opt_size in current_settings.optimizerswitches)
                                       else if not (cs_opt_size in current_settings.optimizerswitches)
                                         { CMOV with constants grows the code size }
                                         { CMOV with constants grows the code size }