Recent

Author Topic: Avoiding conditional jumps platform independent  (Read 4507 times)

lagprogramming

  • Full Member
  • ***
  • Posts: 159
Avoiding conditional jumps platform independent
« on: September 30, 2015, 08:32:36 pm »
   Is "ord(comparison)" returning 0 for false and 1 for true, no matter the target of the build?
As example, function Eof, which can be found in rtl/inc/text.inc, contains:

Code: [Select]
     if TextRec(t).mode=fmOutput then
      InOutRes:=104
     else
      InOutRes:=103;

   Can the above code be safely changed to "InOutRes:=103+ord(TextRec(t).mode=fmOutput);"?
   I'm thinking that who knows, maybe there are target CPUs where "ord(boolean_evaluation)" pascal code might return a value for true different than 1. I'm pretty sure false would return 0. I mention that "boolean_evaluation" is indeed a comparison, not a boolean function result, variable or something like that.

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 7605
Re: Avoiding conditional jumps platform independent
« Reply #1 on: September 30, 2015, 08:59:11 pm »
Probably yes, but  "if TextRec(t).mode=fmOutput" as compare node might generate different code then ord(TextRec(t).mode=fmOutput)  (e.g. using flags instead having values 0 and 1 in registers)

Probably the best way is to do this optimizer level to transform such things in a target dependent manner.

Laksen

  • Hero Member
  • *****
  • Posts: 624
    • J-Software
Re: Avoiding conditional jumps platform independent
« Reply #2 on: September 30, 2015, 09:10:15 pm »
marcov is right. The compiler should know best :)

For example on ARM, this:
Code: [Select]
function test1(a: longint): longint;
begin
   if a = 123 then
      test1:=103
   else
      test1:=104;
end;

function test2(a: longint): longint;
begin
   test2:=a+ord(a=123);
end;

Becomes:
Code: [Select]
SYSTEM_$$_TEST1$LONGINT$$LONGINT:
cmp r0,#123
moveq r0,#103
movne r0,#104
bx r14

SYSTEM_$$_TEST2$LONGINT$$LONGINT:
cmp r0,#123
moveq r1,#1
movne r1,#0
add r0,r0,r1
bx r14

lagprogramming

  • Full Member
  • ***
  • Posts: 159
Re: Avoiding conditional jumps platform independent
« Reply #3 on: October 19, 2015, 04:07:10 pm »
@Laksen
What assembly code is produced for arm regarding the following two functions?

Code: Pascal  [Select]
  1. function function1(var parameter:integer):boolean;
  2. begin
  3. if parameter<>0 then
  4.    begin
  5.    result:=true;
  6.    parameter:=0;
  7.    end else result:=false;
  8. end;
  9.  
  10. function function2(var parameter:integer):boolean;
  11. begin
  12. result:=parameter<>0;
  13. if result then parameter:=0;
  14. end;

Thank you

Thaddy

  • Hero Member
  • *****
  • Posts: 9284
Re: Avoiding conditional jumps platform independent
« Reply #4 on: October 19, 2015, 04:22:37 pm »
simply compile with the -a option will give you the assembler output as <yourmodule>.s

for example: ppcrossarm -Tlinux -Mdelphi -O4  -CpARMV7A -s armasm.dpr (contains your functions)  gives you:

Code: [Select]
# Begin asmlist al_procedures

.section .text.n_p$armasm_$$_function1$longint$$boolean
.balign 4
.globl P$ARMASM_$$_FUNCTION1$LONGINT$$BOOLEAN
.type P$ARMASM_$$_FUNCTION1$LONGINT$$BOOLEAN,#function
P$ARMASM_$$_FUNCTION1$LONGINT$$BOOLEAN:
mov r1,r0
ldr r0,[r1]
cmp r0,#0
movne r0,#1
movne r2,#0
strne r2,[r1]
moveq r0,#0
bx r14
.Le0:
.size P$ARMASM_$$_FUNCTION1$LONGINT$$BOOLEAN, .Le0 - P$ARMASM_$$_FUNCTION1$LONGINT$$BOOLEAN

.section .text.n_p$armasm_$$_function2$longint$$boolean
.balign 4
.globl P$ARMASM_$$_FUNCTION2$LONGINT$$BOOLEAN
.type P$ARMASM_$$_FUNCTION2$LONGINT$$BOOLEAN,#function
P$ARMASM_$$_FUNCTION2$LONGINT$$BOOLEAN:
mov r1,r0
ldr r0,[r1]
cmp r0,#0
movne r0,#1
# Peephole CmpMovMov - Removed redundant moveq
cmp r0,#0
movne r2,#0
strne r2,[r1]
bx r14
.Le1:
.size P$ARMASM_$$_FUNCTION2$LONGINT$$BOOLEAN, .Le1 - P$ARMASM_$$_FUNCTION2$LONGINT$$BOOLEAN

As you can see: no branching at all.


 
« Last Edit: October 19, 2015, 05:24:43 pm by Thaddy »
also related to equus asinus.

Laksen

  • Hero Member
  • *****
  • Posts: 624
    • J-Software
Re: Avoiding conditional jumps platform independent
« Reply #5 on: October 19, 2015, 05:38:29 pm »
The second one is one cycle slower on a Cortex A8 according to this: http://pulsar.webshaker.net/ccc/result.php?lng=us
:)

Thaddy

  • Hero Member
  • *****
  • Posts: 9284
Re: Avoiding conditional jumps platform independent
« Reply #6 on: October 20, 2015, 10:44:19 am »
You can further optimize your code to only 6 cycles by eliminating a comparison and doing a straigjht assignment. In your code you can skip the parameter comparison and simply always do an assignment.
Code: [Select]
mov r1,r0
ldr r0,[r1]
cmp r0,#0
movne r0,#1
mov r2,#0
str r2,[r1]
bx r14

That's because if it isn't zero, you make it zero anyway. The assignment is less costly w/o comparison.

Code: Pascal  [Select]
  1. function function3(var parameter:integer):boolean;
  2. begin
  3. result:=parameter<>0;
  4. parameter:=0;
  5. end;
  6.  

Thx to Laksen: that is a neat link!!
« Last Edit: October 20, 2015, 10:46:22 am by Thaddy »
also related to equus asinus.

lagprogramming

  • Full Member
  • ***
  • Posts: 159
Re: Avoiding conditional jumps platform independent
« Reply #7 on: October 25, 2015, 07:00:43 pm »
   Not exactely on topic but neither off-topic.

Code: Pascal  [Select]
  1. procedure aaa(x,c:longint);
  2. begin
  3.   if c=0 then exit;x:=c;
  4. end;
  5.  
A year ago the above function produced the following code:

Code: Pascal  [Select]
  1. .globl UNIT1_AAA$LONGINT$LONGINT
  2.     .type UNIT1_AAA$LONGINT$LONGINT,@function
  3. UNIT1_AAA$LONGINT$LONGINT:
  4. # Temps allocated between rsp+0 and rsp+0
  5. # Var x located in register eax
  6. # Var c located in register esi
  7. # [unit1.pas]
  8. # [62] begin
  9. .Ll1:
  10. # [63] if c=0 then exit;x:=c;
  11.     movl %esi,%eax
  12.     testl %eax,%eax
  13.     je .Lj3
  14.     movl %esi,%eax
  15. .Lj3:
  16. .Ll2:
  17. # [64] end;
  18.     ret
  19. .Lt1:
  20. .Le0:
  21.     .size UNIT1_AAA$LONGINT$LONGINT, .Le0 - UNIT1_AAA$LONGINT$LONGINT


   Now it produces(x86_64 level 3 optimizations):


Code: Pascal  [Select]
  1. .section .text.n_unit1_$$_aaa$longint$longint
  2.         .balign 16,0x90
  3. .globl  UNIT1_$$_AAA$LONGINT$LONGINT
  4.         .type   UNIT1_$$_AAA$LONGINT$LONGINT,@function
  5. UNIT1_$$_AAA$LONGINT$LONGINT:
  6. .Lc1:
  7. # Var x located in register eax
  8. # Var c located in register esi
  9. .Ll1:
  10. # [unit1.pas]
  11. # [39] begin
  12.         movl    %edi,%eax
  13. .Ll2:
  14. # [40] if c=0 then exit;x:=c;
  15.         testl   %esi,%esi
  16.         je      .Lj5
  17.         jmp     .Lj6
  18. .Lj5:
  19.         jmp     .Lj3
  20. .Lj6:
  21. # Var x located in register eax
  22. # Var c located in register esi
  23.         movl    %esi,%eax
  24. .Lj3:
  25. .Ll3:
  26. # [41] end;
  27.         ret
  28. .Lc2:
  29. .Lt1:
  30. .Le0:
  31.         .size   UNIT1_$$_AAA$LONGINT$LONGINT, .Le0 - UNIT1_$$_AAA$LONGINT$LONGINT
  32.  

   It's not a particular situation. Most of the "if" statements lead to conditional jumps that point to unconditional jumps("jmp"). The only exception I've seen is when you have a chain of consecutive conditions like "if (condition1)or(condition2)... then...". For example:

Code: Pascal  [Select]
  1. procedure aaa(x,c:longint);
  2. begin
  3.   if (c=0)or(x=0) then exit;x:=c;
  4. end;

Code: Pascal  [Select]
  1. .section .text.n_unit1_$$_aaa$longint$longint
  2.         .balign 16,0x90
  3. .globl  UNIT1_$$_AAA$LONGINT$LONGINT
  4.         .type   UNIT1_$$_AAA$LONGINT$LONGINT,@function
  5. UNIT1_$$_AAA$LONGINT$LONGINT:
  6. .Lc1:
  7. # Var x located in register eax
  8. # Var c located in register esi
  9. .Ll1:
  10. # [unit1.pas]
  11. # [39] begin
  12.         movl    %edi,%eax
  13. .Ll2:
  14. # [40] if (c=0)or(x=0) then exit;x:=c;
  15.         testl   %esi,%esi
  16.         je      .Lj5
  17.         testl   %eax,%eax
  18.         je      .Lj5
  19.         jmp     .Lj7
  20. .Lj5:
  21.         jmp     .Lj3
  22. .Lj7:
  23. # Var x located in register eax
  24. # Var c located in register esi
  25.         movl    %esi,%eax
  26. .Lj3:
  27. .Ll3:
  28. # [41] end;
  29.         ret
  30. .Lc2:
  31. .Lt1:
  32. .Le0:
  33.         .size   UNIT1_$$_AAA$LONGINT$LONGINT, .Le0 - UNIT1_$$_AAA$LONGINT$LONGINT

   A FPC trunk downloaded about a month ago was used. Is there a purpose in this new design(pattern) regarding the use of mixed jumps?
   Thank you!

Thaddy

  • Hero Member
  • *****
  • Posts: 9284
Re: Avoiding conditional jumps platform independent
« Reply #8 on: October 25, 2015, 07:13:11 pm »
With trunk 32146 I compiled with fpc -O4 -Mdelphi -al nogeentest.dpr and get this:
Code: Pascal  [Select]
  1. # [6] BEGIN
  2.         movl    %ecx,%eax
  3. # [7] IF C=0 THEN EXIT;X:=C;
  4.         testl   %edx,%edx
  5.         je      .Lj3
  6. # Var X located in register eax
  7. # Var C located in register edx
  8.         movl    %edx,%eax
  9. .Lj3:
  10. # [8] END;
  11.  

Seems OK, doesn't it? I don't see mixed jumps? Maybe I do not understand ;)

ARM Raspberry Pi 1 gives:
Code: Pascal  [Select]
  1. # [6] BEGIN
  2. # [7] IF C=0 THEN EXIT;X:=C;
  3.         cmp     r1,#0
  4. # Var X located in register r0
  5. # Var C located in register r1
  6.         movne   r0,r1
  7. # [8] END;
  8.  

Not too bad.

Btw: your milage may vary. Depending on the optimization level the compiler can always choose a new strategy. I believe these have been there for a very long time, though.

[edit]
Here D7 does an interesting job:
Code: Pascal  [Select]
  1. test edx,edx
  2. ret
  3.  
« Last Edit: October 26, 2015, 07:33:22 am by Thaddy »
also related to equus asinus.

lagprogramming

  • Full Member
  • ***
  • Posts: 159
Re: Avoiding conditional jumps platform independent
« Reply #9 on: October 26, 2015, 12:17:10 pm »
With trunk 32146 I compiled with fpc -O4 -Mdelphi -al nogeentest.dpr and get this:
Code: Pascal  [Select]
  1. # [6] BEGIN
  2.         movl    %ecx,%eax
  3. # [7] IF C=0 THEN EXIT;X:=C;
  4.         testl   %edx,%edx
  5.         je      .Lj3
  6. # Var X located in register eax
  7. # Var C located in register edx
  8.         movl    %edx,%eax
  9. .Lj3:
  10. # [8] END;
  11.  

Seems OK, doesn't it? I don't see mixed jumps? Maybe I do not understand ;)

ARM Raspberry Pi 1 gives:
Code: Pascal  [Select]
  1. # [6] BEGIN
  2. # [7] IF C=0 THEN EXIT;X:=C;
  3.         cmp     r1,#0
  4. # Var X located in register r0
  5. # Var C located in register r1
  6.         movne   r0,r1
  7. # [8] END;
  8.  

Not too bad.

Btw: your milage may vary. Depending on the optimization level the compiler can always choose a new strategy. I believe these have been there for a very long time, though.

I SEE  :D
I'm pretty sure that for months the code produced had those avoidable "jmp"s. Indeed, a recent change in fpc's trunk improved things.
Thank you!

Thaddy

  • Hero Member
  • *****
  • Posts: 9284
Re: Avoiding conditional jumps platform independent
« Reply #10 on: October 26, 2015, 01:13:06 pm »
It's not recent. Did you change coding habits?
also related to equus asinus.