Improved handling of signed sequences in OptPass2Movx

This commit is contained in:
J. Gareth "Curious Kit" Moreton 2021-12-06 12:45:48 +00:00 committed by FPK
parent 01e5f4855a
commit d255ffba8b

View File

@ -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;
{ 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;
if (TestValMin < TrySmallerLimit) or (TestValMax < TrySmallerLimit) or
(TestValMin > UpperLimit) or (TestValMax > UpperLimit) then
{ Overflow }
Break;
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);