Difference between revisions of "Asm"

From Lazarus wiki
Jump to navigationJump to search
m (c)
(changed example with Label and optimized it)
Line 35: Line 35:
 
In order to maintain portability between platforms (i.e. your code still compiles for many targets), while optimizing for specific targets, you want to set up [[Conditional compilation|conditional compilation]]:
 
In order to maintain portability between platforms (i.e. your code still compiles for many targets), while optimizing for specific targets, you want to set up [[Conditional compilation|conditional compilation]]:
  
<syntaxhighlight>program sumExample(input, output, stderr);
+
<syntaxhighlight>program sign(input, output, stderr);
  
{ iteratively calculates the sum over first n integers }
+
type
function iterativeSumFirstNIntegers(const n: longword): qword;
+
signumCodomain = -1..1;
{$ifdef CPUX86_64} // ============= optimized implementation
+
 
// assembler modifier appended to routine declaration
+
{ returns the sign of an integer }
 +
function signum(const x: longint): signumCodomain;
 +
{$ifdef CPUx86_64} // ============= optimized implementation
 
assembler;
 
assembler;
// you have to familiarize the compiler with symbols
 
// which are meant to be jump targets
 
{$goto on}
 
label
 
isfni_iterate;
 
 
{$asmMode intel}
 
{$asmMode intel}
 
asm
 
asm
// ecx is used as counter by loop instruction
+
// load constants: cmov cannot handle immediates
mov ecx, n // ecx := n
+
// xor-instruction modifies flags => put it prior test
mov rax, 0 // rax := 0
+
xor  r8, r8 // r8 := 0
isfni_iterate:
+
add rax, qword(ecx) // rax := rax + ecx
+
// comparison pulled up front for pipelining
loop isfni_iterate // dec(ecx)
+
test  x,   x  // x = 0 ?
// if ecx <> 0 then goto isfni_iterate
+
 +
// load constants, since cmov cannot handle immediates
 +
mov  r9, -1  // r9 := -1
 
 
// the @result macro represents the functions return value
+
// determine result
mov @result, rax // result := rax
+
mov   eax, // result := 1
// note, a list of modified registers (here ['rax', 'ecx'])
+
cmovl eax, r9 // if x < 0 then result := -1
//       is ignored for pure assembler routines
+
cmove eax, r8 // if x = 0 then result := 0
 
end;
 
end;
 
{$else} // ========================== default implementation
 
{$else} // ========================== default implementation
var
 
i: longword;
 
x: qword;
 
 
begin
 
begin
x := 0; // mov rax, 0
+
// This is what virtually math.sign does.
for i := n downto 1 do // mov ecx, n
+
// The compiled code requires _two_ cmp instructions, though.
 +
if x > 0 then
 
begin
 
begin
x := x + i; // add rax, ecx
+
signum := 1;
end; // loop isfni_iterate
+
end
iterativeSumFirstNIntegers := x; // mov @result, rax
+
else if x < 0 then
 +
begin
 +
signum := -1;
 +
end
 +
else
 +
begin
 +
signum := 0;
 +
end;
 
end;
 
end;
 
{$endif}
 
{$endif}
Line 78: Line 82:
 
// M A I N =================================================
 
// M A I N =================================================
 
var
 
var
n: longword;
+
x: longint;
 
begin
 
begin
readLn(n);
+
readLn(x);
writeLn(iterativeSumFirstNIntegers(n));
+
writeLn(signum(x));
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
 
As you can see, you can implement whole routines in assembly language, by adding the <code>assembler</code> modifier and writing <code>asm</code> instead of <code>begin</code> for the implementation block.
 
As you can see, you can implement whole routines in assembly language, by adding the <code>assembler</code> modifier and writing <code>asm</code> instead of <code>begin</code> for the implementation block.
Of course in a production program, you would use an algorithm applying the formula <code>sum := (n * (n + 1)) div 2</code> (“Gaussian sum formula”).
 
 
  
  
Line 93: Line 95:
 
* [[The inline assembler parser]]
 
* [[The inline assembler parser]]
 
* [[Lazarus inline assembler]]
 
* [[Lazarus inline assembler]]
 +
* [[Label|<code>label</code>]]
  
 
relevant compiler directives
 
relevant compiler directives

Revision as of 17:05, 8 February 2018

Deutsch (de) English (en) español (es) suomi (fi)

The reserved word asm starts a block of inline assembly code.

program asmDemo(input, output, stderr);

// The $asmMode directive informs the compiler
// which syntax is used in asm-blocks.
// Alternatives are 'att' (AT&T syntax) and 'direct'.
{$asmMode intel}

var
	n, m: longint;
begin
	n := 42;
	m := -7;
	writeLn('n = ', n, '; m = ', m);
	
	// instead of declaring another temporary variable
	// and writing "tmp := n; n := m; m := tmp;":
	asm
		mov eax, n  // eax := n
		// xchg can only operate at most on one memory address
		xchg eax, m // swaps values in eax and at m
		mov n, eax  // n := eax (holding the former m value)
	// an array of strings after the asm-block closing 'end'
	// tells the compiler which registers have changed
	// (you don't wanna mess with the compiler's notion
	// which registers mean what)
	end ['eax'];
	
	writeLn('n = ', n, '; m = ', m);
end.

In order to maintain portability between platforms (i.e. your code still compiles for many targets), while optimizing for specific targets, you want to set up conditional compilation:

program sign(input, output, stderr);

type
	signumCodomain = -1..1;

{ returns the sign of an integer }
function signum(const x: longint): signumCodomain;
{$ifdef CPUx86_64} // ============= optimized implementation
assembler;
{$asmMode intel}
asm
	// load constants: cmov cannot handle immediates
	// xor-instruction modifies flags => put it prior test
	xor   r8,  r8 // r8 := 0
	
	// comparison pulled up front for pipelining
	test  x,   x  // x = 0 ?
	
	// load constants, since cmov cannot handle immediates
	mov   r9, -1  // r9 := -1
	
	// determine result
	mov   eax, 1  // result := 1
	cmovl eax, r9 // if x < 0 then result := -1
	cmove eax, r8 // if x = 0 then result := 0
end;
{$else} // ========================== default implementation
begin
	// This is what virtually math.sign does.
	// The compiled code requires _two_ cmp instructions, though. 
	if x > 0 then
	begin
		signum := 1;
	end
	else if x < 0 then
	begin
		signum := -1;
	end
	else
	begin
		signum := 0;
	end;
end;
{$endif}

// M A I N =================================================
var
	x: longint;
begin
	readLn(x);
	writeLn(signum(x));
end.

As you can see, you can implement whole routines in assembly language, by adding the assembler modifier and writing asm instead of begin for the implementation block.


see also

general

relevant compiler directives

special tasks