Lazarus
Free Pascal => General => Topic started by: Starchild on July 11, 2018, 09:28:49 pm
-
Hi,
Let's say I have the following minimal example code:
function Decide(rm: Byte): Boolean;
begin
Result := (rm and $07) = ((rm shr 3) and $07);
end;
When I compile it, I get the hint "Mixing signed expressions and longwords gives a 64bit result". I don't get why - Byte is an unsigned type and there is no other variable involved. It seems as soon as there is a right-shift involved, it starts doing crazy things. My target is IA-32, and I'd expect code like "shr al, 3 ; and al, 7" for the expression on the right-hand side. Both, "shr" and "and" are logical/bitwise operations that have nothing to do with signedness.
The machine code generated for that function is insane (FPC 3.0.4, -O3):
.text:00401420 public P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN
.text:00401420 P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN proc near
.text:00401420 ; CODE XREF: _main+7↓p
.text:00401420 push ebx
.text:00401421 movzx edx, al
.text:00401424 shr edx, 3
.text:00401427 and edx, 7
.text:0040142A mov ebx, 0
.text:0040142F and ax, 7
.text:00401433 movsx eax, ax
.text:00401436 movsx eax, ax
.text:00401439 mov ecx, eax
.text:0040143B sar ecx, 1Fh
.text:0040143E cmp ebx, ecx
.text:00401440 jnz short loc_40144A
.text:00401442 cmp edx, eax
.text:00401444 jnz short loc_40144A
.text:00401446 mov al, 1
.text:00401448 jmp short loc_40144C
.text:0040144A ; ---------------------------------------------------------------------------
.text:0040144A
.text:0040144A loc_40144A: ; CODE XREF: P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN+20↑j
.text:0040144A ; P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN+24↑j
.text:0040144A mov al, 0
.text:0040144C
.text:0040144C loc_40144C: ; CODE XREF: P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN+28↑j
.text:0040144C pop ebx
.text:0040144D retn
.text:0040144D P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN endp
When I add a Byte cast around rm shr 3 (totally unintuitive that I'd have to do such a thing), it gets a bit better. But still not exactly what I would expect.
.text:00401420 public P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN
.text:00401420 P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN proc near
.text:00401420 ; CODE XREF: _main+7↓p
.text:00401420 movzx edx, al
.text:00401423 shr edx, 3
.text:00401426 and dx, 7
.text:0040142B and ax, 7
.text:0040142F cmp dx, ax
.text:00401432 setz al
.text:00401435 retn
.text:00401435 P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN endp
It seems the codegen doesn't want to use the SHR r/m8,imm8 instruction and avoids 8-bit registers in general. The "and ax, 7" looks a bit fishy as well since only al is being passed into the function, but I guess it doesn't matter in the case of "and". In Delphi I don't get any hint and the code it generates looks better too. It does a 32-bit shift as well, but the code looks more like the second example here (without requiring a Byte-cast).
Is this a bug? Am I wrong that this behavior is unexpected?
-
Hello Starchild,
This in no way answers your question but, I have noticed that FPC seems to generate 64bit code that is significantly better than the 32bit code. I'm curious to find out if the problem you mentioned also happens in 64bit.
It would be nice if you could compile your code for a 64bit target and report on the code it generated.
Thanks.
-
Even though it looks surprising, this is not a bug, because shr works on integers ('Logical operators require operands that are of an integer type, and produce an integer type result', see https://freepascal.org/docs-html/ref/refsu46.html). I had a similar observation and discussion here: http://forum.lazarus.freepascal.org/index.php/topic,41563.0.html
-
Gammatester was quicker than me...
You can avoid that behaviour by using an arithmetic instead of logical shift sarshortint (rm,3), or simply div 8 instead of shr 3.
-
It would be nice if you could compile your code for a 64bit target and report on the code it generated.
You're right, it is indeed better, but adding the Byte cast still yet makes it better (it drops the movsx and does cmp ax, cx).
.text:0000000100001460 P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN proc near
.text:0000000100001460 ; CODE XREF: main+11↓p
.text:0000000100001460 movzx eax, cl
.text:0000000100001463 shr eax, 3
.text:0000000100001466 and eax, 7
.text:0000000100001469 and cx, 7
.text:000000010000146E movsx rcx, cx
.text:0000000100001472 cmp rax, rcx
.text:0000000100001475 setz al
.text:0000000100001478 and eax, 0FFh
.text:000000010000147D retn
.text:000000010000147D P$PROJECT1_$$_DECIDE$BYTE$$BOOLEAN endp
Even though it looks surprising, this is not a bug, because shr works on integers ('Logical operators require operands that are of an integer type, and produce an integer type result', see https://freepascal.org/docs-html/ref/refsu46.html). I had a similar observation and discussion here: http://forum.lazarus.freepascal.org/index.php/topic,41563.0.html
Gammatester was quicker than me...
You can avoid that behaviour by using an arithmetic instead of logical shift sarshortint (rm,3), or simply div 8 instead of shr 3.
That sucks. I guess {$mode delphi} doesn't affect stuff like basic types and their interactions in expressions. I don't think an arithmetic shift is equivalent to a logical shift in all situations where I have code like this. For divisions it also produces crazy code (multiple arithmetic right shifts). I'll go with adding Byte casts...