2

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:

  • I don’t want a way of gathering assembly from the high-level language. Instead, please focus on allowing for simply accessing labels declared in previous assembly blocks.
  • I don’t want to deal with assembly instruction addresses. Instead, please help me access previous labels.
  • Language needs to be Pascal/Delphi.
5
  • You claim that Delphi loop took 6 billion cycles. Which loop construct did you use? Delphi has three standard loop concepts Repeat Statements, While Statements and For Statements. Each of them works a bit differently. Commented May 28 at 17:38
  • In 64-bit, you cannot mix Pascal and inline Assembly in the same function, but you can still write Assembly-only functions. In 32-bit, you can mix Pascal and inline Assembly. Commented May 28 at 17:53
  • 32-bit program, but 64-bit Windows. Commented May 28 at 17:57
  • For Statements, integer counter, no code in for. Each loop is 6 cycles. This may differ in your computer, but still slower than the assembly. Commented May 28 at 17:59
  • 1
    About the huge difference in cycles: the rdtsc instruction reports in EDX:EAX. Your code repeatedly stores EAX twice! Better write: rdtsc mov dword ptr f[0], eax mov dword ptr f[4], EDX ... rdtsc mov dword ptr i[0], eax mov dword ptr i[4], EDX. Commented May 28 at 23:42

2 Answers 2

3

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.
Sign up to request clarification or add additional context in comments.

2 Comments

Why don't you put the sub eax,1 in place of the test (just prior to the jnz). And add a push prior to the writeln call and a pop after it? And someone earlier mentioned "register-preserving calls" - where are these documented? (I couldn't find any mention of register preservation in the Intel assembler doco). Trying to learn stuff here! :)
I just did the bare minimum to keep them working. To see which registers are kept (non-volatile) and which ones are scratch (also called volatile) check your architecture/target's ABI documentation. Note that e.g. conventions between Windows and Linux might differ on 32-bit x86, and even betweeen compilers (e.g. Delphi default "register" vs MSVC's cdecl)
3

Of course - @loop is declared in a different scope, so you cannot access it.

Instead of breaking your code into two asm blocks, move your Writeln line into a procedure, combine the two asm blocks into one, and call that new procedure from the assembly code.

7 Comments

But i tried it, but it won't worked? I excepted 1000 "Hello, World!" messages, but i only seen one. Please provide a working example code.
This is not a code-writing service, sorry. You might want to post your non-working code so that people can offer suggestions.
OK, but after moving it to a procedure and calling with CALL, and with or without (Multiple tests) RET, it only outputs Hello, World! once. Also moved assembly loop counter to other registers, and still not worked. Help me.
@JOrE: With the loop counter in a call-preserved register, a simple loop should work. Like Dmitry said, update your question with a minimal reproducible example of the attempt if you want anyone to look at it. Or single-step it in a debugger and see if the function gets called repeatedly but somehow only prints once. Like if it uses a carriage return but not linefeed, if a terminal needs both or something... (Unlikely, I'd assume WriteLn would do what the name implies). And BTW, you could just write the whole loop in Pascal and check the compiler's asm output to see if it makes a tight asm loop around a call
Shouldn't you be saving eax prior to the writeln/call, then after the writeln/call restoring eax and decrementing eax just prior to the jnz?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.