delphiassemblyinline-assemblydelphi-12-athenshighperformance

Keep Delphi In-line Assembly Labels in Assembly Blocks


Assembly is always faster than high-level languages. For example, I tested an empty loop in Delphi repeating 1 billion times, and same loop but written in assembly. The Delphi loop took 6 billion (x64 CPU) cycles, but the assembly loop took only 2 billion cycles.

When I need to perform a high-performance action, I’ll code it in assembly whenever possible. But I can’t always code in assembly; sometimes I’ll need to do actions which need a high-level programming language. Now everything is good, but a loop requires labels.

I tried closing the assembly block, doing my actions, then opening final assembly blocks to handle the loop. But it results in errors. I entered this code in Delphi:

program Test;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Sysutils;

var
  f, i: int64;

begin
  asm
    rdtsc
    mov dword ptr f[0], eax
    mov dword ptr f[4], eax
    mov eax, 1000
    @loop:
    sub eax, 1
  end;
  WriteLn('Hello, World!');
  asm;
    jnz @loop
    rdtsc
    mov dword ptr i[0], eax
    mov dword ptr i[4], eax
  end;
  ReadLn; // Allow user to read output
end.

I expected this to print Hello, World! 1,000 times, but instead I got this error:

E2003: Undeclared identifier: '@loop' at line 23 (23:1)

The problem is that Delphi’s in-line assembly won’t allow accessing labels declared in previous assembly blocks.

Can you help me to access labels declared in previous assembly blocks?


Answer Requirements:


Solution

  • Without touching why your reasoning and code is wrong on so many levels, I can get it to compile if I define the loop in Delphi Seattle by defining only the label in pascal. That might trigger extra instructions, (stack related) but probably only before the real label.

    program Project5;
    
    {$APPTYPE CONSOLE}
    
    
    uses
      System.SysUtils;
    
    label loop;
    
    var
      f, i: int64;
    
    begin
      asm
        rdtsc
        mov dword ptr f[0], eax
        mov dword ptr f[4], eax
        mov eax, 1000
      end;
       loop:
      asm
        sub eax, 1
      end;
      WriteLn('Hello, World!');  // will EAX survive this? Otherwise push/pop
      asm // remove spurious ;
        test eax,eax  // zero flag might not survive writeln  
        jnz loop
        rdtsc
        mov dword ptr i[0], eax
        mov dword ptr i[4], eax
      end;
      ReadLn; // Allow user to read output
    end.