Recent

Author Topic: Relocatable relative assembly jump by value instead of label - SOLVED?  (Read 3613 times)

NickleSi

  • Newbie
  • Posts: 5
Compiler / linker is confused by relative short jumps with an immediate value. Thinks they are a label or something..

dec cl
jnz -15

converting -15 to $f1 hex value doesn't work either. Only manual machine code works, like so:

db $75; db $f1

Assembly coders use relocatable macros using relative negative jumps for relocatable loops. FPC macros don't work in assembly sections, instead an external .inc file has to be written for each macro. It's not ideal but OK.

// rcx = bits count 1..64 .. esi = ink .. bx = jump for way 0  .. dx = jump for way 1 
// r8 = bits indicating forward / diagonal cursor movement for fast circles, arcs, expansions..

asm
{    loop bits      }  // not an actual label, just a comment
{$i   dot            }  // mov [rdi], esi
{$i   goWay0or1 } // mov ax, bx  ;  shr r8, 1  ;  cmovc ax, dx ;  add rdi, eax // if shifted bit=1 : jump bitmap stride+n else jump n
{$i  nextBit        } // dec cl ; db $75; db $f1  // jnz -15 should work as an immediate relative jump by default.
{$i  dot              } // mov [rdi], esi  // last pixel drawn
end;

If anyone knows what I'm doing wrong, if I am, then please explain as I can't find a solution in the forum or documentation.... I've tried adding 'near'... Just finding 'offset' had to be added as a prefix to load immediate global variable addresses into registers was a real chore too..
« Last Edit: May 09, 2021, 10:43:53 am by NickleSi »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
TASM has "short" label type for this purpose, but FPC doesn't seem to support it.

I.e. you can't do something like this:
Code: [Select]
  asm
    @test:
    mov eax, eax
    jnz short @test
  end;

P.S. Delphi doesn't support "short" label type too, but at least it compiles to right opcodes (see attachment). May be it depends on optimization settings?
« Last Edit: May 08, 2021, 09:24:12 pm by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

NickleSi

  • Newbie
  • Posts: 5
Cheers.. I forgot to say I'd tried 'short' as well as 'near' with no luck.. Short is parsed but does nothing, same for 'near'.. Also 'byte', 'smallint', 'shortint' don't work. The compiler is telling the linker it's a synthetic absolute label that needs to be converted into a relative 8 bit jump...
--
This is a BUG to me... Not just a limitation with hacky work-around... Lack of macro support in assembly blocks almost is too - although the {i <EXTERNAL FILE>} solution can be neatened up, but all those external '.inc' files are a pain.. What gets me with almost all high level languages is they do not do 'the basics' properly... Engineers know macros can weaken data type and language integrity but deal with that, no problem..
--
Assembly macros should be available in every assembler, and macro relocation should be automatic, but if not, at least parse numeric values in jump statements as relative jumps. I can see it's a compiler-linker problem. I'd fix it if I knew where to look and have the time.
--
I am not asking for 'inlined assembler procedures', even though many of us want them. I see why this is being avoided - but still no excuse not to implement macros in assembly blocks and relative jumps by numeric value.
« Last Edit: May 09, 2021, 10:21:57 am by NickleSi »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
For me "short" doesn't even compile.

Hmmm. May be I missed something, when I was doing it for the first time, but now exactly the same example compiles to right opcodes (see attachment).

I'm not sure, how compatible with TASM FPC inline assembler is. I mean, original Delphi's inline assembler should have been very compatible with TASM. And FPC's assembler should mimic it. And in TASM adding @@ to label turns it into local label, i.e. scope-unique label.

If you need to use macros so much, then it would be better for you to use external assembler to compile some routines into separate obj files and then link them to your project.

My experience shows, that FPC optimization is great and using assembler to produce CPU-tick-precise code isn't needed in most cases. ASM syndrome, i.e. assumption, that raw ASM code is always faster, is bad thing. Modern optimized code is as fast, as bare assembler, but you don't need to waste your time on routine things, like caring about register allocation, instead of actually writing your code.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

NickleSi

  • Newbie
  • Posts: 5
In my experience 'ASM syndrome' is yet another high level excuse for not doing low-level work when it's easy, as in the case I demonstrated. Easy to program whilst guaranteeing equal or better performance than the compiled version. Easy to roll-out similar methods (ie. small lines and arcs rotated 8 or 4 ways).
--
The FPC 'bitpacked' types produce pretty efficient code, but I can beat it with assembly and it's easier when it comes to nitty-gritty bit manipulation with a bit of practice. Macros reduce procedure calls... These are all tight little loops and tight, rapidly repeated procedures. Inlined code and assembler macros enable higher code engineering standards... and more speed.
--
This tiny engine could run on a 68000 or even Z80 using 16kb of tiny arc, line, circle and interpolation + a few fast math lookup tables.. It also happens to work well on modern systems.  FPC and Lazarus is a fine effort, and Delphi 7 32bit for compiled Win32 apps was a masterpiece for its time. FPC does a very good job at optimisation with plenty of control - but also lack of it at times...
--
Yes, I use external assemblers and assembly tools, but I am an into INTEGRATED development environments and assessing many.

ccrause

  • Hero Member
  • *****
  • Posts: 843
Compiler / linker is confused by relative short jumps with an immediate value. Thinks they are a label or something..

dec cl
jnz -15
<snip>
Any reason you want to hard code the jump distance, rather than using a label so that the linker can calculate the appropriate distance?

TASM has "short" label type for this purpose, but FPC doesn't seem to support it.
<snip>
The exact syntax depends on the selected assembler mode, for Intel syntax the following code:
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$asmmode intel}
  4.  
  5. begin
  6.   asm
  7.     @l1:
  8.     dec cl
  9.     jnz @l1
  10.   end;
  11. end.
compiles to the following assembler (dumped from the executable using objdump on Linux 64 bit):
Code: ASM  [Select][+][-]
  1. 00000000004001d0 <main>:
  2.   4001d0:       55                      push   %rbp
  3.   4001d1:       48 89 e5                mov    %rsp,%rbp
  4.   4001d4:       e8 27 61 01 00          callq  416300 <fpc_initializeunits>
  5.   4001d9:       fe c9                   dec    %cl
  6.   4001db:       75 fc                   jne    4001d9 <main+0x9>
  7.   4001dd:       e8 6e 65 01 00          callq  416750 <fpc_do_exit>
  8.   4001e2:       48 89 ec                mov    %rbp,%rsp
  9.   4001e5:       5d                      pop    %rbp
  10.   4001e6:       c3                      retq  
  11.  
Using AT&T syntax:
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$asmmode att}
  4.  
  5. begin
  6.   asm
  7.     .L1:
  8.     dec %cl
  9.     jnz .L1
  10.   end;
  11. end.
Yielding identical assembler as above.

So while the use of an immediate value is not supported by FPC, it appears that a programmer can generate the desired instructions via local labels. Or do you have a situation where the compiler + linker cannot determine the jump offset?

<snip>
Yes, I use external assemblers and assembly tools, but I am an into INTEGRATED development environments...
I fully agree with this point of view.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
In my experience 'ASM syndrome' is yet another high level excuse for not doing low-level work when it's easy, as in the case I demonstrated. Easy to program whilst guaranteeing equal or better performance than the compiled version. Easy to roll-out similar methods (ie. small lines and arcs rotated 8 or 4 ways).
--
The FPC 'bitpacked' types produce pretty efficient code, but I can beat it with assembly and it's easier when it comes to nitty-gritty bit manipulation with a bit of practice. Macros reduce procedure calls... These are all tight little loops and tight, rapidly repeated procedures. Inlined code and assembler macros enable higher code engineering standards... and more speed.
--
This tiny engine could run on a 68000 or even Z80 using 16kb of tiny arc, line, circle and interpolation + a few fast math lookup tables.. It also happens to work well on modern systems.  FPC and Lazarus is a fine effort, and Delphi 7 32bit for compiled Win32 apps was a masterpiece for its time. FPC does a very good job at optimisation with plenty of control - but also lack of it at times...
--
Yes, I use external assemblers and assembly tools, but I am an into INTEGRATED development environments and assessing many.
Ok, at the end it's your choice. I can't dictate anything to you.

I'm one of those people, who also had ASM syndrome in the past. This syndrome came from simple fact, that I started my programming career on MS QBasic and wondered, how to write games, that could run on 12Mhz computer fast enough. I decided, that it was due to fact, that these games were written on ASM, so writing on ASM = faster code. I have several projects, fully written on ASM. But then I realized, that high level programming language produced equally effective code. Because at some day I said, that I would write faster code on ASM, than code produced by FPC, in one of threads on this forum, but failed, because FPC's code was faster, than mine. And on ASM I had to think too much about "I need to do everything in registers for better performance, but number of registers is limited, so I need to decide, which one to use". High level language simply does this job better.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

mika

  • Full Member
  • ***
  • Posts: 102
Compiler / linker is confused by relative short jumps with an immediate value. Thinks they are a label or something..

Immediate value is deviation of label "zero". I would consider as bug fact that internal assembler and external assembler represents with label "zero" two different entities.

Treat immediate value as is does not make sense.

NickleSi

  • Newbie
  • Posts: 5
I do not have 'asm syndrome'.. Psychobabble is the neo-way of avoiding issues I find... I do have testing the limits syndrome, before committing to a platform for personal projects... Assembly should be used sparingly for worthwhile optimisation and when it makes the job easier.. I tried to use a simple, timeless example that is still relevant for say a 'picoPi' super-microcontroller, and also other embedded / retro platforms. FPC simply cannot put two instances of a tiny macro containing a local jump in the same scope. A better example is for say a line drawing algorithm with a jump.. Here is what's inside the loop. 

add r9, rdx      // 4 or 6 ops per loop
cmp r9, rcx
db $7e; db $06   // jle @onward
  sub r9, rcx
  add rdi, rbx
// @onward:
  add rdi, rax   // st+=ht; if st>=ln then begin nib.nub+=1; st-=ln; end; nib^:=cl; nib.nub+=padW; end;

1 of 16 directions are selected, 8 of them embed the above code as a relocatable macro in the same function (yes, there are other ways, but for the sake of the example, go with it). Also if rolled out 32 times to avoid loop overhead, with an offset jump into the rolled out loop block. The above can be put in a {$i lineLoop} 'macro' and pasted 32 times in a procedure..   If I'd used a label they'd clash and produce an error, obviously... Just saying it's a bit of an ugly solution, but not a big deal...

Here's how I'd implement macros (Intel syntax+)

macro_name:[macro text]
and
macro_name:
[
  macro text
]

These macros can contain any text and are scoped so

macro_example:
[
  source: [rsi]  // renamed registers, here local to macro_example but could be global in an asm block in the interface section
  target: [rdi]
  count:  [rcx]
// ... 
  mov rax, [source]
  mov [target], rax
// ... 
]

In use 'macro_example' embeds the text inline, but 'call macro_example' can also be used.. Remember, FPC won't inline assembly functions, it's macros don't work in assembly, only {$i filename} linking to an external file works.. Messy when you want to make a code 'lego set' for simple ops.

I do not have 'asm syndrome'.. Psychobabble is the neo-way of avoiding issues I find... I do have testing the limits syndrome, before committing to a platform for personal projects... Assembly should be used sparingly for worthwhile optimisation and when it makes the job easier.. I tried to use a simple, timeless example that is still relevant for say a 'picoPi' super-microcontroller, and also other embedded / retro platforms. FPC simply cannot put two instances of a tiny macro containing a local jump in the same scope. A better example is for say a line drawing algorithm with a jump.. Here is what's inside the loop. 

add r9, rdx      // 4 or 6 ops per loop
cmp r9, rcx
db $7e; db $06   // jle @onward
  dec r9
  add rdi, rbx
// @onward:
  add rdi, rax   // st+=h; if st>=l then begin nb+=1; st-=l; end; nb^:=cl; nb+=padW; end;

1 of 16 directions are selected, 8 of them embed the above code as a relocatable macro in the same function (yes, there are other ways, but for the sake of the example, go with it). Also if rolled out 32 times to avoid loop overhead, with an offset jump into the rolled out loop block. The above can be put in a {$i lineLoop} 'macro' and pasted 32 times in a procedure..   If I'd used a label they'd clash and produce an error, obviously... Just saying it's a bit of an ugly solution, but not a big deal...

Here's how I'd implement macros (Intel syntax+)

macro_name:[macro text]
and
macro_name:
[
  macro text
]

These macros can contain any text and are scoped so

macro_example:
[
  source: [rsi]  // renamed registers, here local to macro_example but could be global in an asm block in the interface section
  target: [rdi]
  count:  [rcx]
// ... 
  mov rax, [source]
  mov [target], rax
// ... 
]

In use 'macro_example' embeds the text inline, but 'call macro_example' can also be used.. Remember, FPC won't inline assembly functions, it's macros don't work in assembly, only {$i filename} linking to an external file works.. Messy when you want to make a code 'lego set' for simple ops.

Cross-platform human-machine languages can be also be implemented with macros...

ie... 'move', 'put' and/or 'get'  replacing mov, 'op' or 'do' replacing 'call', 'to' or 'go' for jump, and working on ARM, X86, ++ with appropriate embedded compiler directive IFDEFs... Many possibilities. Good for engine building.
« Last Edit: May 14, 2021, 03:10:33 pm by NickleSi »

 

TinyPortal © 2005-2018