Avoiding conditional jumps platform independent lazarus / 62-General ---------------------------------------------------------------------- lagprogramming · 2015-09-30 18:32 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 · 2015-09-30 18:59 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 · 2015-09-30 19:10 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 · 2015-10-19 14:07 @Laksen What assembly code is produced for arm regarding the following two functions? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function function1(var parameter:integer):boolean;beginif parameter<>0 then   begin   result:=true;   parameter:=0;   end else result:=false;end; function function2(var parameter:integer):boolean;beginresult:=parameter<>0;if result then parameter:=0;end; Thank you ---------------------------------------------------------------------- Thaddy · 2015-10-19 14:22 simply compile with the -a option will give you the assembler output as .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. ---------------------------------------------------------------------- Laksen · 2015-10-19 15:38 The second one is one cycle slower on a Cortex A8 according to this: http://pulsar.webshaker.net/ccc/result.php?lng=us ---------------------------------------------------------------------- Thaddy · 2015-10-20 08:44 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function function3(var parameter:integer):boolean;beginresult:=parameter<>0;par ameter:=0;end;  Thx to Laksen: that is a neat link!! ---------------------------------------------------------------------- lagprogramming · 2015-10-25 18:00 Not exactely on topic but neither off-topic. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure aaa(x,c:longint);begin  if c=0 then exit;x:=c;end; A year ago the above function produced the following code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i .globl UNIT1_AAA$LONGINT$LONGINT    .type UNIT1_AAA$LONGINT$LONGINT,@functionUNIT1_AAA$LONGINT$LONGINT:# Temps allocated between rsp+0 and rsp+0# Var x located in register eax# Var c located in register esi# [unit1.pas]# [62] begin.Ll1:# [63] if c=0 then exit;x:=c;    movl %esi,%eax    testl %eax,%eax    je .Lj3    movl %esi,%eax.Lj3:.Ll2:# [64] end;    ret.Lt1:.Le0:    .size UNIT1_AAA$LONGINT$LONGINT, .Le0 - UNIT1_AAA$LONGINT$LONGINT    Now it produces(x86_64 level 3 optimizations): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i .section .text.n_unit1_$$_aaa$longint$longint        .balign 16,0x90.globl  UNIT1_$$_AAA$LONGINT$LONGINT        .type   UNIT1_$$_AAA$LONGINT$LONGI NT,@functionUNIT1_$$_AAA$LONGINT$LONGINT:.Lc1:# Var x located in register eax# Var c located in register esi.Ll1:# [unit1.pas]# [39] begin        movl    %edi,%eax.Ll2:# [40] if c=0 then exit;x:=c;      testl   %esi,%esi        je      .Lj5        jmp     .Lj6.Lj5:      jmp     .Lj3.Lj6:# Var x located in register eax# Var c located in register esi        movl    %esi,%eax.Lj3:.Ll3:# [41] end;        ret.Lc2:.Lt1:.Le0:        .size   UNIT1_$$_AAA$LONGINT$LONGINT, .Le0 - UNIT1_$$_AAA$LONGINT$LONGINT     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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure aaa(x,c:longint);begin  if (c=0)or(x=0) then exit;x:=c;end; Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i .section .text.n_unit1_$$_aaa$longint$longint        .balign 16,0x90.globl  UNIT1_$$_AAA$LONGINT$LONGINT        .type   UNIT1_$$_AAA$LONGINT$LONGI NT,@functionUNIT1_$$_AAA$LONGINT$LONGINT:.Lc1:# Var x located in register eax# Var c located in register esi.Ll1:# [unit1.pas]# [39] begin        movl    %edi,%eax.Ll2:# [40] if (c=0)or(x=0) then exit;x:=c;        testl   %esi,%esi        je      .Lj5        testl %eax,%eax        je      .Lj5        jmp     .Lj7.Lj5:        jmp     .Lj3.Lj7:# Var x located in register eax# Var c located in register esi        movl    %esi,%eax.Lj3:.Ll3:# [41] end;        ret.Lc2:.Lt1:.Le0:        .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 · 2015-10-25 18:13 With trunk 32146 I compiled with fpc -O4 -Mdelphi -al nogeentest.dpr and get this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i # [6] BEGIN        movl    %ecx,%eax# [7] IF C=0 THEN EXIT;X:=C;        testl   %edx,%edx        je      .Lj3# Var X located in register eax# Var C located in register edx        movl    %edx,%eax.Lj3:# [8] END;  Seems OK, doesn't it? I don't see mixed jumps? Maybe I do not understand ARM Raspberry Pi 1 gives: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i # [6] BEGIN# [7] IF C=0 THEN EXIT;X:=C;        cmp     r1,#0# Var X located in register r0# Var C located in register r1        movne   r0,r1# [8] END;  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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i test edx,edxret ---------------------------------------------------------------------- lagprogramming · 2015-10-26 11:17 Quote from: Thaddy on October 25, 2015, 07:13:11 pmWith trunk 32146 I compiled with fpc -O4 -Mdelphi -al nogeentest.dpr and get this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i # [6] BEGIN        movl    %ecx,%eax# [7] IF C=0 THEN EXIT;X:=C;        testl   %edx,%edx        je      .Lj3# Var X located in register eax# Var C located in register edx        movl    %edx,%eax.Lj3:# [8] END;  Seems OK, doesn't it? I don't see mixed jumps? Maybe I do not understand ARM Raspberry Pi 1 gives: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i # [6] BEGIN# [7] IF C=0 THEN EXIT;X:=C;        cmp     r1,#0# Var X located in register r0# Var C located in register r1        movne   r0,r1# [8] END;  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  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 · 2015-10-26 12:13 It's not recent. Did you change coding habits?