Difference between revisions of "Function"

From Lazarus wiki
Jump to navigationJump to search
(→‎return value: shorter program)
Line 19: Line 19:
 
program functionDemo(input, output, stderr);
 
program functionDemo(input, output, stderr);
  
{$mode objfpc}
+
{$mode objFPC}
  
 
// traditional syntax:
 
// traditional syntax:
Line 53: Line 53:
 
// return type fits into a single register => use accumulator register
 
// return type fits into a single register => use accumulator register
 
function zero(const x: int64): boolean;
 
function zero(const x: int64): boolean;
{$ifdef CPUx86_64}
+
{$ifDef CPUx86_64}
 
assembler; register;
 
assembler; register;
 
{$asmMode intel}
 
{$asmMode intel}
 
asm
 
asm
 
// xor modifies flags => put it in front of test
 
// xor modifies flags => put it in front of test
xor rax, rax    // rax := 0 [false]
+
xor rax, rax    // rax := 0 [remove residue]
+
// examining the assembler output
 
// we can verify x is stored in register rdi [i.e. not rax]
 
 
test x, x        // x = 0 ?
 
test x, x        // x = 0 ?
jnz @zero_done  // if x <> 0 then goto done
+
setz al          // rax := ZF
 
 
inc rax          // rax := 1 [true]
 
@zero_done:
 
 
// When you examine the assembler output
 
// When you examine the assembler output
 
// you will notice the compiler automatically inserts code
 
// you will notice the compiler automatically inserts code
Line 73: Line 69:
 
{$else}
 
{$else}
 
begin
 
begin
// NOTE: with optimization switches enabled
 
//      the compiler produces with the following Pascal statement
 
//      even shorter (and maybe faster) code
 
//      than the assembler implementation above
 
 
result := x = 0;
 
result := x = 0;
 
end;
 
end;
{$endif}
+
{$endIf}
 
</syntaxhighlight>
 
</syntaxhighlight>
 
Otherwise, depending on which [[sAsmmode|<syntaxhighlight lang="pascal" enclose="none">{$asmMode}</syntaxhighlight>]] is active, the <syntaxhighlight lang="asm" enclose="none">@result</syntaxhighlight> (Intel) or <syntaxhighlight lang="asm" enclose="none">__result</syntaxhighlight> (AT&T) macro can be used.
 
Otherwise, depending on which [[sAsmmode|<syntaxhighlight lang="pascal" enclose="none">{$asmMode}</syntaxhighlight>]] is active, the <syntaxhighlight lang="asm" enclose="none">@result</syntaxhighlight> (Intel) or <syntaxhighlight lang="asm" enclose="none">__result</syntaxhighlight> (AT&T) macro can be used.
<syntaxhighlight lang="pascal" line start="56" highlight="20,31,36,40,41">
+
<syntaxhighlight lang="pascal" line start="48" highlight="20,31,36,40,41">
 
type
 
type
 
bodyAttributes = record
 
bodyAttributes = record
Line 92: Line 84:
 
// return type doesn't fit into accumulator => @result macro gives address
 
// return type doesn't fit into accumulator => @result macro gives address
 
function sphere(const radius: real): bodyAttributes;
 
function sphere(const radius: real): bodyAttributes;
{$ifdef CPUx86_64}
+
{$ifDef CPUx86_64}
 
assembler;
 
assembler;
 
{$asmMode intel}
 
{$asmMode intel}
Line 125: Line 117:
 
sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
 
sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
 
end;
 
end;
{$endif}
+
{$endIf}
 
</syntaxhighlight>
 
</syntaxhighlight>
Originally Pascal expected ''exact one'' assignment to the result variable (whichever is used).
+
Originally Pascal expected ''exact one'' [[Becomes|assignment]] to the result variable (whichever is used).
 
[[FPC]] however does not prohibit multiple assignments.
 
[[FPC]] however does not prohibit multiple assignments.
It will emit a warning, if none of the possible result identifiers were used or the exit routine is not written.
+
It will emit a warning if none of the possible result identifiers were used or the exit routine is not written.
 
<blockquote>
 
<blockquote>
 
; Warning: Function result does not seem to be set
 
; Warning: Function result does not seem to be set
 
: You can get this warning if the compiler thinks that a function return value is not set. This will not be displayed for assembler procedures, or procedures that contain assembler blocks.
 
: You can get this warning if the compiler thinks that a function return value is not set. This will not be displayed for assembler procedures, or procedures that contain assembler blocks.
 
</blockquote>
 
</blockquote>
<syntaxhighlight lang="pascal" line start="100">
+
<syntaxhighlight lang="pascal" line start="92">
 
begin
 
begin
 
writeLn(sphere(2.0).surfaceArea);
 
writeLn(sphere(2.0).surfaceArea);

Revision as of 16:46, 9 February 2019

Deutsch (de) English (en) español (es) suomi (fi) français (fr) русский (ru)

A function is a routine that, in contrast to procedures, returns a value. A call of a function is virtually substituted by its return value. If the {$extendedSyntax} compiler switch state is off, function calls can not appear as non-productive statements, but have to be or be part of an expression.

The word function is a reserved word.

return value

In addition to a normal procedure, a function's formal signature contains a return type: The formal parameter list has to be succeeded by a colon and return type. For instance the following function returns a boolean.

function myFunction(const firstParameter: real): boolean;

When implementing functions there are several ways to define the function's return value.

 1program functionDemo(input, output, stderr);
 2
 3{$mode objFPC}
 4
 5// traditional syntax:
 6// the result is stored in the variable
 7// its name is the same as the function's
 8function myLine(const x: real): real;
 9begin
10	myLine := 0.5 * x + 2;
11end;

If {$modeswitch result+}, which is set by {$mode objFPC} and {$mode Delphi}, inside the implementation block the special identifier result is available, too:

13// using special `result` identifier
14function myParabola(const x: real): real;
15begin
16	result := sqr(x) - 1;
17end;

Additionally, in {$mode objFPC} the routine exit will set the return value, too, and leave the stack frame. In the previous two examples further statements could have appeared, and they would have been executed, whilst after an exit the routine is done. This is the behavior a return statement in C or other programming languages has.

19// using exit routine
20function even(const x: longint): boolean;
21begin
22	exit(not odd(x));
23end;

In assembly language other rules apply. If the return type is an integral value, the accumulator register is used, provided it fits in there:

25// in assembly language:
26// return type fits into a single register => use accumulator register
27function zero(const x: int64): boolean;
28{$ifDef CPUx86_64}
29assembler; register;
30{$asmMode intel}
31asm
32	// xor modifies flags => put it in front of test
33	xor rax, rax     // rax := 0 [remove residue]
34 
35	test x, x        // x = 0 ?
36	setz al          // rax := ZF
37	
38	// When you examine the assembler output
39	// you will notice the compiler automatically inserts code
40	// that moves the contents of rax to the right spot on the stack.
41end;
42{$else}
43begin
44	result := x = 0;
45end;
46{$endIf}

Otherwise, depending on which {$asmMode} is active, the @result (Intel) or __result (AT&T) macro can be used.

48type
49	bodyAttributes = record
50		surfaceArea: real;
51		volume: real;
52	end;
53
54// in assembly language:
55// return type doesn't fit into accumulator => @result macro gives address
56function sphere(const radius: real): bodyAttributes;
57{$ifDef CPUx86_64}
58assembler;
59{$asmMode intel}
60const
61	three: longint = 3;
62	four: longint = 4;
63var
64	r: real;
65asm
66	pextrq r, radius, 0 // r := (@radius+0)^
67	lea rax, @result    // rax := @result
68	fld r               // radius
69	
70	fld st(0)           // radius radius
71	fild four           // 4 radius radius
72	fldpi               // pi 4 radius radius
73	fmul                // 4*pi radius radius
74	fxch                // radius 4*pi radius
75	fld st(0)           // radius radius 4*pi radius
76	fmul                // radius^2 4*pi radius
77	fmul                // 4*pi*radius^2 radius
78	fst [rax].bodyAttributes.surfaceArea
79	
80	fmul                // 4*pi*radius^3
81	fild three          // 3 4*pi*radius^3
82	fdivp               // 4/3*pi*radius^3
83	fst [rax].bodyAttributes.volume
84end;
85{$else}
86begin
87	sphere.surfaceArea := 4 * pi() * sqr(radius);
88	sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
89end;
90{$endIf}

Originally Pascal expected exact one assignment to the result variable (whichever is used). FPC however does not prohibit multiple assignments. It will emit a warning if none of the possible result identifiers were used or the exit routine is not written.

Warning
Function result does not seem to be set
You can get this warning if the compiler thinks that a function return value is not set. This will not be displayed for assembler procedures, or procedures that contain assembler blocks.
92begin
93	writeLn(sphere(2.0).surfaceArea);
94end.

see also