Browse Source

* x86: New TEST->BT peephole optimisation

J. Gareth "Curious Kit" Moreton 2 years ago
parent
commit
314f632377
1 changed files with 88 additions and 1 deletions
  1. 88 1
      compiler/x86/aoptx86.pas

+ 88 - 1
compiler/x86/aoptx86.pas

@@ -14023,10 +14023,97 @@ unit aoptx86;
 
 
     function TX86AsmOptimizer.PostPeepholeOptTestOr(var p : tai) : Boolean;
     function TX86AsmOptimizer.PostPeepholeOptTestOr(var p : tai) : Boolean;
       var
       var
-        IsTestConstX : Boolean;
+        IsTestConstX, IsValid : Boolean;
         hp1,hp2 : tai;
         hp1,hp2 : tai;
       begin
       begin
         Result:=false;
         Result:=false;
+        { If x is a power of 2 (popcnt = 1), change:
+            test $x, %reg/ref
+            je / sete / cmove (or jne / setne)
+
+          To:
+            bt   lb(x), %reg/ref
+            jnc / setnc / cmovnc (or jc / setc / cmovnc)
+        }
+        if (taicpu(p).opcode = A_TEST) and
+{$ifdef i8086}
+          (current_settings.optimizecputype >= cpu_386) and
+{$endif i8086}
+          MatchOpType(taicpu(p), top_const, top_reg) and { "btx $x,mem" is unacceptably slow }
+          (PopCnt(QWord(taicpu(p).oper[0]^.val)) = 1) and
+          { For sizes less than S_L, the byte size is equal or larger with BT,
+            so don't bother optimising }
+          (taicpu(p).opsize >= S_L) then
+          begin
+            IsValid := True;
+            { Check the next set of instructions, watching the FLAGS register
+              and the conditions used }
+            TransferUsedRegs(TmpUsedRegs);
+            UpdateUsedRegs(TmpUsedRegs, tai(p.Next));
+            hp1 := p;
+            hp2 := nil;
+
+            while GetNextInstruction(hp1, hp1) do
+              begin
+                if not Assigned(hp2) then
+                  { The first instruction after TEST }
+                  hp2 := hp1;
+
+                if (hp1.typ <> ait_instruction) then
+                  begin
+                    { If the flags are no longer in use, everything is fine }
+                    if RegInUsedRegs(NR_DEFAULTFLAGS, TmpUsedRegs) then
+                      IsValid := False;
+                    Break;
+                  end;
+
+                case taicpu(hp1).condition of
+                  C_None:
+                    begin
+                      if RegInUsedRegs(NR_DEFAULTFLAGS, TmpUsedRegs) then
+                        { Something is not quite normal, so play safe and don't change }
+                        IsValid := False;
+
+                      Break;
+                    end;
+                  C_E, C_Z, C_NE, C_NZ:
+                    { This is fine };
+                  else
+                    begin
+                      { Unsupported condition }
+                      IsValid := False;
+                      Break;
+                    end;
+                end;
+
+                UpdateUsedRegs(TmpUsedRegs, tai(hp1.Next));
+              end;
+
+            if IsValid then
+              begin
+                while hp2 <> hp1 do
+                  begin
+                    case taicpu(hp2).condition of
+                      C_Z, C_E:
+                        taicpu(hp2).condition := C_NC;
+                      C_NZ, C_NE:
+                        taicpu(hp2).condition := C_C;
+                      else
+                        { Should not get this by this point }
+                        InternalError(2022110701);
+                    end;
+
+                    GetNextInstruction(hp2, hp2);
+                  end;
+
+                DebugMsg(SPeepholeOptimization + 'Changed TEST $0x' + hexstr(taicpu(p).oper[0]^.val, 2) + ' to BT ' + debug_tostr(BsrQWord(taicpu(p).oper[0]^.val)) + ' to shrink instruction size (Test2Bt)', p);
+                taicpu(p).opcode := A_BT;
+                taicpu(p).oper[0]^.val := BsrQWord(taicpu(p).oper[0]^.val); { Essentially the base 2 logarithm }
+                Result := True;
+                Exit;
+              end;
+          end;
+
         { removes the line marked with (x) from the sequence
         { removes the line marked with (x) from the sequence
           and/or/xor/add/sub/... $x, %y
           and/or/xor/add/sub/... $x, %y
           test/or %y, %y  | test $-1, %y    (x)
           test/or %y, %y  | test $-1, %y    (x)