Recent

Author Topic: [SOLVED] Is there a best method for returning a function value?  (Read 22300 times)

GypsyPrince

  • Guest
So far, I have come across 3 different methods of returning a value from a function. There may be more, but I haven't seen them yet:

I.
Code: Pascal  [Select][+][-]
  1. function %name%(param1: type1; param2: type2; ...): type;
  2.   var  
  3.     declarations;
  4.  
  5. begin
  6.    < statements >
  7.  
  8.    %name% := value;
  9. end;


II.
Code: Pascal  [Select][+][-]
  1. function %name%(param1: type1; param2: type2; ...): type;
  2.   var  
  3.     declarations;
  4.  
  5. begin
  6.    < statements >
  7.  
  8.    result := value;
  9. end;


III.
Code: Pascal  [Select][+][-]
  1. function %name%(param1: type1; param2: type2; ...): type;
  2.   var  
  3.     declarations;
  4.  
  5. begin
  6.    < statements >
  7.  
  8.    exit(value);
  9. end;

Right now, I am partial to number III.
Is there any difference between the 3 as far as speed, stack/heap resource de-allocation, etc.?
Or, are they all identical as far as the end effect? Would anyone recommend any one style over the others?
« Last Edit: April 21, 2020, 06:23:49 pm by GypsyPrince »

440bx

  • Hero Member
  • *****
  • Posts: 6528
Re: Is there a best method for returning a function value?
« Reply #1 on: April 13, 2020, 09:34:56 am »
I can't even think of a case where any of the methods you showed would end up generating different code by the compiler.

IF (note the big IF) there is case where it does make a difference,  the difference would be so small that it would not even be worth considering.  I can't think of a case where one method would be different in terms of code size and performance.  Definitely no difference in terms of stack utilization.

What can make a BIG difference is the type returned by the function.  If the function is not returning a type that fits into a register then, regardless of the method you use to return the result, using the function a large number of times can definitely have a perceptible impact on performance (and stack utilization if the function is recursive.)

Conclusion: of the 3 method you showed, pick whichever you like best, there is no difference.  If you are concerned about performance, pay attention to the size of the type the function is returning.

HTH.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Is there a best method for returning a function value?
« Reply #2 on: April 13, 2020, 09:46:01 am »
There probably isn't much difference if those statements are at the end of the function. The result will be saved to the stack or directly to the primary register if optimized. An exit elsewhere in the function body will generate an additional jump.

A clean way at the end of the function is to either assign the result to the function name or (in the case of FPC) to the reserved variable 'result'. Both point to the same address on the stack, usually EBP-4 (32 bits) or RBP-8 (64 bits). The result on the stack is then stored in the primary register (we're talking simple types here for simplicity).

There is no reason for and nothing to be gained from an exit() statement at the end of the function. Possibly the compiler will optimize this anyway (by not generating a jump instruction).
« Last Edit: April 13, 2020, 09:52:11 am by Munair »
It's only logical.

440bx

  • Hero Member
  • *****
  • Posts: 6528
Re: Is there a best method for returning a function value?
« Reply #3 on: April 13, 2020, 10:13:07 am »
Best ask the compiler :)

Test program:
Code: Pascal  [Select][+][-]
  1. program TestFunctionResults;
  2.  
  3. function MethodOne(parameter : integer) : integer;
  4. begin
  5.   MethodOne := parameter * 2;
  6. end;
  7.  
  8. function MethodTwo(parameter : integer) : integer;
  9. begin
  10.   result := parameter * 2;
  11. end;
  12.  
  13. function MethodThree(parameter : integer) : integer;
  14. begin
  15.   exit(parameter * 2);
  16. end;
  17.  
  18. begin
  19.   MethodOne(3);
  20.   MethodTwo(3);
  21.   MethodThree(3);
  22. end.
  23.  


with optimization level 1, the code generated is:
Code: ASM  [Select][+][-]
  1.                                   ; MethodOne
  2.  
  3. TestFunctionResults.lpr:4         begin
  4. 00401420 55                       push   %ebp
  5. 00401421 89e5                     mov    %esp,%ebp
  6. 00401423 8d6424f8                 lea    -0x8(%esp),%esp
  7. 00401427 8945fc                   mov    %eax,-0x4(%ebp)
  8.  
  9. TestFunctionResults.lpr:5         MethodOne := parameter * 2;
  10. 0040142A d1e0                     shl    %eax
  11. TestFunctionResults.lpr:6         end;
  12. 0040142C c9                       leave
  13. 0040142D c3                       ret
  14.  
  15.  
  16.                                   ; MethodTwo
  17.  
  18. TestFunctionResults.lpr:9         begin
  19. 00401430 55                       push   %ebp
  20. 00401431 89e5                     mov    %esp,%ebp
  21. 00401433 8d6424f8                 lea    -0x8(%esp),%esp
  22. 00401437 8945fc                   mov    %eax,-0x4(%ebp)
  23.  
  24. TestFunctionResults.lpr:10        result := parameter * 2;
  25. 0040143A d1e0                     shl    %eax
  26. TestFunctionResults.lpr:11        end;
  27. 0040143C c9                       leave
  28. 0040143D c30000                   ret
  29.  
  30.  
  31.                                   ; MethodThree
  32.  
  33. TestFunctionResults.lpr:14        begin
  34. 00401440 55                       push   %ebp
  35. 00401441 89e5                     mov    %esp,%ebp
  36. 00401443 8d6424f8                 lea    -0x8(%esp),%esp
  37. 00401447 8945fc                   mov    %eax,-0x4(%ebp)
  38.  
  39. TestFunctionResults.lpr:15        exit(parameter * 2);
  40. 0040144A d1e0                     shl    %eax
  41. 0040144C 8945f8                   mov    %eax,-0x8(%ebp)
  42. TestFunctionResults.lpr:16        end;
  43. 0040144F 8b45f8                   mov    -0x8(%ebp),%eax
  44. 00401452 c9                       leave
  45. 00401453 c30000000000000000000000 ret
  46.  


with optimization level 3, the code generated is:
Code: ASM  [Select][+][-]
  1. TestFunctionResults.lpr:4         begin
  2. 00401420 55                       push   %ebp
  3. 00401421 89e5                     mov    %esp,%ebp
  4. 00401423 8d6424f8                 lea    -0x8(%esp),%esp
  5. 00401427 8945fc                   mov    %eax,-0x4(%ebp)
  6.  
  7. TestFunctionResults.lpr:5         ; MethodOne
  8.  
  9. 0040142A d1e0                     shl    %eax
  10. TestFunctionResults.lpr:6         end;
  11. 0040142C c9                       leave
  12. 0040142D c3                       ret
  13.  
  14.  
  15.  
  16. TestFunctionResults.lpr:9         begin
  17. 00401430 55                       push   %ebp
  18. 00401431 89e5                     mov    %esp,%ebp
  19. 00401433 8d6424f8                 lea    -0x8(%esp),%esp
  20. 00401437 8945fc                   mov    %eax,-0x4(%ebp)
  21.  
  22.                                   ; MethodTwo
  23.  
  24. TestFunctionResults.lpr:10        result := parameter * 2;
  25. 0040143A d1e0                     shl    %eax
  26. TestFunctionResults.lpr:11        end;
  27. 0040143C c9                       leave
  28. 0040143D c30000                   ret
  29.  
  30.  
  31. TestFunctionResults.lpr:14        begin
  32. 00401440 55                       push   %ebp
  33. 00401441 89e5                     mov    %esp,%ebp
  34. 00401443 8d6424f8                 lea    -0x8(%esp),%esp
  35. 00401447 8945fc                   mov    %eax,-0x4(%ebp)
  36.  
  37.                                   ; MethodThree
  38.  
  39. TestFunctionResults.lpr:15        exit(parameter * 2);
  40. 0040144A d1e0                     shl    %eax
  41. 0040144C 8945f8                   mov    %eax,-0x8(%ebp)
  42. TestFunctionResults.lpr:16        end;
  43. 0040144F 8b45f8                   mov    -0x8(%ebp),%eax
  44. 00401452 c9                       leave
  45. 00401453 c30000000000000000000000 ret
  46.  


with optimization level 4, the code generated is:
Code: ASM  [Select][+][-]
  1. TestFunctionResults.lpr:5         MethodOne := parameter * 2;
  2. 00401420 d1e0                     shl    %eax
  3. TestFunctionResults.lpr:6         end;
  4. 00401422 c30000000000000000000000 ret
  5.  
  6. TestFunctionResults.lpr:10        result := parameter * 2;
  7. 00401430 d1e0                     shl    %eax
  8. TestFunctionResults.lpr:11        end;
  9. 00401432 c30000000000000000000000 ret
  10.  
  11. TestFunctionResults.lpr:15        exit(parameter * 2);
  12. 00401440 d1e0                     shl    %eax
  13. TestFunctionResults.lpr:16        end;
  14. 00401442 c30000000000000000000000 ret
  15.  

Conclusion, with optimization levels 1 through 3, the "exit(value)" takes one additional instruction.  with optimization level 4, the three methods generate the same code (as they should.)





FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Is there a best method for returning a function value?
« Reply #4 on: April 13, 2020, 10:32:41 am »
I'm not surprised with the optimization levels 1 .. 3 results. The compiler I'm working on also does not (yet) optimize a 'return' statement at the end of a function, i.e. it generates a jump instruction at all times.

A 'return' or 'exit' statement is firstly meant to break out of a function while in a specific control flow. Using it at the end is like using a JMP or GOTO while already there:
Code: [Select]
func ()
  ...
  goto func_exit
  func_exit:
  ...
end func

Doesn't make sense to do that.
It's only logical.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Is there a best method for returning a function value?
« Reply #5 on: April 13, 2020, 10:35:38 am »
However, I am surprised to see this at level 3:
Code: ASM  [Select][+][-]
  1. 0040144C 8945f8                   mov    %eax,-0x8(%ebp)
  2. TestFunctionResults.lpr:16        end;
  3. 0040144F 8b45f8                   mov    -0x8(%ebp),%eax
Tells me I shouldn't worry too much about optimization.  :D
It's only logical.

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4715
  • I like bugs.
Re: Is there a best method for returning a function value?
« Reply #6 on: April 13, 2020, 10:37:38 am »
There is a semantic difference, thus the preferred way depends.
Assigning a value to Result does not exit the function right away. The Result can be used as any variable. It can be used in calculations etc.
In some languages you need a temporary variable for the same purpose.

The first way, assigning value to a function's name, comes from history and is not used much nowadays.
It has obvious problems. If you rename a function, you must rename the implicit variable, too.
Also reading code becomes more difficult. You must all the time remember the function's name to understand its code.
The "Result" variable is unambiguous.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

GypsyPrince

  • Guest
Re: Is there a best method for returning a function value?
« Reply #7 on: April 13, 2020, 10:45:39 am »
Quote
Conclusion, with optimization levels 1 through 3, the "exit(value)" takes one additional instruction.  with optimization level 4, the three methods generate the same code (as they should.)

I thought something like that may be the case. The reason I had preferred it was due to the documentation which said the exit(); statement is synonymous with the return(); statement in C to immediately exit the function while the previous 2 methods still allowed any successive code in the function to be executed, which isn't really that much of an issue for me.

https://wiki.freepascal.org/Function

The thought of exit(value); being synonymous with a 'goto' statement just about makes my skin curl up... LOL

So, while I am still just getting to know fp, it looks like I might go with 'result :='

GypsyPrince

  • Guest
Re: Is there a best method for returning a function value?
« Reply #8 on: April 13, 2020, 10:47:15 am »
I'm not surprised with the optimization levels 1 .. 3 results. The compiler I'm working on also does not (yet) optimize a 'return' statement at the end of a function, i.e. it generates a jump instruction at all times.

A 'return' or 'exit' statement is firstly meant to break out of a function while in a specific control flow. Using it at the end is like using a JMP or GOTO while already there:
Code: [Select]
func ()
  ...
  goto func_exit
  func_exit:
  ...
end func

Doesn't make sense to do that.

Definitely makes no sense at all... unless you're in QBasic or batch files.

440bx

  • Hero Member
  • *****
  • Posts: 6528
Re: Is there a best method for returning a function value?
« Reply #9 on: April 13, 2020, 11:36:18 am »
The "Result" variable is unambiguous.
Definitely true and, in addition to that, by far the most flexible and reliable way of handling function results for all the reasons you mentioned, including clarity and ease of maintenance.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

jwdietrich

  • Hero Member
  • *****
  • Posts: 1278
    • formatio reticularis
Re: Is there a best method for returning a function value?
« Reply #10 on: April 13, 2020, 11:41:57 am »
The "Result" variable is unambiguous.
Definitely true and, in addition to that, by far the most flexible and reliable way of handling function results for all the reasons you mentioned, including clarity and ease of maintenance.

That is the reason why I prefer the second method in my code. If backwards compatibility with old compilers is an issue then only method 1 is an option.
function GetRandomNumber: integer; // xkcd.com
begin
  GetRandomNumber := 4; // chosen by fair dice roll. Guaranteed to be random.
end;

http://www.formatio-reticularis.de

Lazarus 4.2.0 | FPC 3.2.2 | PPC, Intel, ARM | macOS, Windows, Linux

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Is there a best method for returning a function value?
« Reply #11 on: April 13, 2020, 11:51:21 am »
I'm not surprised with the optimization levels 1 .. 3 results. The compiler I'm working on also does not (yet) optimize a 'return' statement at the end of a function, i.e. it generates a jump instruction at all times.

A 'return' or 'exit' statement is firstly meant to break out of a function while in a specific control flow. Using it at the end is like using a JMP or GOTO while already there:
Code: [Select]
func ()
  ...
  goto func_exit
  func_exit:
  ...
end func

Doesn't make sense to do that.

Definitely makes no sense at all... unless you're in QBasic or batch files.

The 'goto' statement received a bad name because many BASIC programmers in the past abused it, which resulted in the notorious 'spaghetti' code. However, FPC's 'exit' is just another name for exactly the same thing: a direct jump to the end of the function, the only non-trivial difference being it limited to the function body.

My previous 'goto' example was just an illustration to demonstrate what happens when you put an 'exit' statement at the end. It generates an unnecessary jump instruction (unless the code is highly optimized).

So while you do not 'see' goto statements much in high level languages these days, the jumps are still happening. Just look at the generated ASM code of any program written in any language and count the number of JMP and RET instructions. They jump (goto) the next / previous address. In fact, in the CPU jumps are happening all the time. We have just chosen to obscure that fact to enforce 'structured' programming.
It's only logical.

440bx

  • Hero Member
  • *****
  • Posts: 6528
Re: Is there a best method for returning a function value?
« Reply #12 on: April 13, 2020, 12:35:08 pm »
However, FPC's 'exit' is just another name for exactly the same thing: a direct jump to the end of the function, the only non-trivial difference being it limited to the function body.
The important difference between a "goto" and their facsimiles such as "exit" and "continue" is that the target address of those facsimiles is known by the context in which they take place instead of by the existence of a label which could be anywhere in the program.

That's the real problem with the "goto" statement, having to search the code for its destination.

Like you, I am a bit surprised that the redundant move for an "exit" statement is not removed except when level 4 optimization is in effect.  It seems to me, the peephole optimizer should have taken care of that redundant move.  That's not a fancy optimization, I think it's a fairly basic one.


FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Zoran

  • Hero Member
  • *****
  • Posts: 1988
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Is there a best method for returning a function value?
« Reply #13 on: April 13, 2020, 12:38:17 pm »
Originally, Pascal didn't have break statement either.
The procedural purists will avoid not only goto, but also break, continue and exit statements.

The idea was that each routine (and each loop as well) should have one entry point and one leaving point. Therefore leave condition should be checked only on starting or ending side of the loop (so, Pascal has while and repeat loops).
Leaving from the middle of a routine (or of a loop) was considered bad procedural code.

I think that a real abuse of goto is jumping backwards. Jumping forward is much less bad (apart from jumping from outside of a loop into loop's body, which is really the worst abuse).
Even now, when we have break, we still cannot break more than one loop level, for that you still need goto.

I don't use goto, as I was taught to avoid it, but I no more think that it is always so bad.
I also think that using continue statement makes code hard to follow, it's in my opinion definitely worse than jumping forward with goto.
I never use continue.
Swan, ZX Spectrum emulator https://github.com/zoran-vucenovic/swan

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Is there a best method for returning a function value?
« Reply #14 on: April 13, 2020, 12:45:20 pm »
The important difference between a "goto" and their facsimiles such as "exit" and "continue" is that the target address of those facsimiles is known by the context in which they take place instead of by the existence of a label which could be anywhere in the program.

That's the real problem with the "goto" statement, having to search the code for its destination.
I believe in Object Pascal goto (if enabled, you can turn it off easily) only jumps within the current scope. You cannot use it to jump out of a procedure, for instance, or to jump between nested routines.

 

TinyPortal © 2005-2018