Recent

Author Topic: [SOLVED] Variable addresses in SRAM  (Read 3896 times)

nanobit

  • Full Member
  • ***
  • Posts: 122
Re: [SOLVED] Variable addresses in SRAM
« Reply #60 on: October 08, 2022, 08:20:08 pm »
High(s) is correct if parameter is "const {/var/out} s: OpenString;",
regardless of {$P} directive.

PascalDragon

  • Hero Member
  • *****
  • Posts: 4874
  • Compiler Developer
Re: [SOLVED] Variable addresses in SRAM
« Reply #61 on: October 09, 2022, 05:58:08 pm »
If you have a function or method with a var parameter of type ShortString then it also passes the maximum size of the provided string variable as a hidden parameter. This is only done if the parameter is declared as ShortString, but not for any other String[<N>] even String[255]! Also this is not done for out parameters. Something like this also isn't done if the result is a ShortString.
As @ccrause already mentioned above, it really seems that, in some cases, the extra hidden "max size" parameter to manage the string size correctly is a necessity.

Yes, and these cases are var and out parameters (the later FPC only) with a ShortString parameter if and only if $P / $OpenStrings is enabled. In that case the type of the parameter becomes in fact OpenString (which is an internal type provided by both FPC and Delphi) which in turn leads to a hidden parameter that contains the maximum length of the string variable passed in (if you explicitly declare the parameter as OpenString it will have the additional parameter for any modifier except for ordinary by-value; at least in FPC, in Delphi that's only the case for var parameters of that type).
If $P / $OpenStrings is not enabled - which is the default in FPC (except for mode Delphi since yesterday ;) ) - then there simply will be no such parameter. In that case it will depend upon the setting of the $V / $VarStringChecks directive: if it's enabled (default in modes Delphi and TP) then the compiler won't allow you to pass smaller string variables to such functions. Otherwise (which is the default in the other modes) the compiler will allow you to do that and you as a developer need to make sure that you don't pass anything that could lead to a buffer overflow or something like that.
This behaviour itself is fully compatible to how Delphi handles this, only the defaults are different.

Regarding my feedback to ccrause: it turns out that the System unit contains an explicit $P+ in rtl/inc/systemh.inc which means that any of the helper functions take a var or out parameter of type ShortString already contains the correct High value of the parameter. It was simply a matter of implementing ccrause's change correctly and now FPC will handle the example by dseligo better:

It is better to stick to shortstring instead of ansistring or others. On most if not all platforms this reserves just 256 byte or even less if declared properly.
E.g. if you know before hand what the maximum string length is, you can declare a shortstring type with the appropriate size:
Code: Pascal  [Select][+][-]
  1. type Tmystring = string[16];
will occupy just 17 bytes on embedded targets. (one for size)

Yes, I use shortstring with appropriate size and it occupies size + 1. Problem is when strings are concatenated.

Consider this program:
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$mode ObjFPC}
  4.  
  5. var s1: String[10];
  6.  
  7. begin
  8.   s1 := 'test';
  9.   s1 := s1 + '123';
  10. end.

While dseligo used AVR code I'll show the result in x86_64 code (the result will be the same on all platforms as the optimization is done at a higher level):

Before:

Code: ASM  [Select][+][-]
  1. .globl  main
  2. main:
  3. .globl  PASCALMAIN
  4. PASCALMAIN:
  5. .Lc2:
  6. # Temps allocated between rbp-256 and rbp+0
  7. .seh_proc main
  8. # [tshortstr.pp]
  9. # [5] begin
  10.         pushq   %rbp
  11. .seh_pushreg %rbp
  12. .Lc3:
  13.         movq    %rsp,%rbp
  14. .Lc4:
  15.         leaq    -288(%rsp),%rsp
  16. .seh_stackalloc 288
  17. .seh_endprologue
  18.         call    fpc_initializeunits
  19. # [6] s1 := 'test';
  20.         leaq    _$TSHORTSTR$_Ld1(%rip),%rax
  21.         leaq    U_$P$TSHORTSTR_$$_S1(%rip),%rcx
  22.         movq    $10,%rdx
  23.         movq    %rax,%r8
  24.         call    fpc_shortstr_to_shortstr
  25. # [7] s1 := s1 + '123';
  26.         leaq    _$TSHORTSTR$_Ld2(%rip),%r9
  27.         leaq    U_$P$TSHORTSTR_$$_S1(%rip),%r8
  28.         leaq    -256(%rbp),%rcx
  29.         movq    $255,%rdx
  30.         call    fpc_shortstr_concat
  31.         leaq    -256(%rbp),%r8
  32.         leaq    U_$P$TSHORTSTR_$$_S1(%rip),%rcx
  33.         movq    $10,%rdx
  34.         call    fpc_shortstr_to_shortstr
  35. # [8] end.
  36.         call    fpc_do_exit
  37.         ret
  38. .seh_endproc
  39.  

After:

Code: ASM  [Select][+][-]
  1. .globl  main
  2. main:
  3. .globl  PASCALMAIN
  4. PASCALMAIN:
  5. .Lc2:
  6. .seh_proc main
  7. # [tshortstr.pp]
  8. # [5] begin
  9.         pushq   %rbp
  10. .seh_pushreg %rbp
  11. .Lc3:
  12.         movq    %rsp,%rbp
  13. .Lc4:
  14.         leaq    -32(%rsp),%rsp
  15. .seh_stackalloc 32
  16. .seh_endprologue
  17.         call    fpc_initializeunits
  18. # [6] s1 := 'test';
  19.         leaq    _$TSHORTSTR$_Ld1(%rip),%rax
  20.         leaq    U_$P$TSHORTSTR_$$_S1(%rip),%rcx
  21.         movq    $10,%rdx
  22.         movq    %rax,%r8
  23.         call    fpc_shortstr_to_shortstr
  24. # [7] s1 := s1 + '123';
  25.         leaq    _$TSHORTSTR$_Ld2(%rip),%r9
  26.         leaq    U_$P$TSHORTSTR_$$_S1(%rip),%r8
  27.         leaq    U_$P$TSHORTSTR_$$_S1(%rip),%rcx
  28.         movq    $10,%rdx
  29.         call    fpc_shortstr_concat
  30. # [8] end.
  31.         call    fpc_do_exit
  32.         ret
  33. .seh_endproc
  34. .Lc1:
  35.  

Note the highlighted stack allocations and also the missing “Temps allocated” line in the second example.

High(s) is correct if parameter is "const {/var/out} s: OpenString;",
regardless of {$P} directive.

Please note that this is only true in FPC (also for constref). In Delphi this will be only true for var parameters.

MarkMLl

  • Hero Member
  • *****
  • Posts: 5862
Re: [SOLVED] Variable addresses in SRAM
« Reply #62 on: October 09, 2022, 06:26:50 pm »
... Note the highlighted stack allocations and also the missing “Temps allocated” line in the second example.

Nicely done. Leaving aside all consideration of compiler version and mode for a moment, is there any quick way, e.g. by using a SizeOf(), that something can be put into application code to report that an oversize temporary has been allocated, either at compilation or runtime?

This is much more of a niggle than anything else, but I think that the last few days' discussion has emphasised that it's worth users being moderately familiar with the mechanics of parameter passing and being able to keep an eye on what's going on.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

ccrause

  • Hero Member
  • *****
  • Posts: 688
Re: [SOLVED] Variable addresses in SRAM
« Reply #63 on: October 09, 2022, 08:38:47 pm »
Regarding my feedback to ccrause: it turns out that the System unit contains an explicit $P+ in rtl/inc/systemh.inc which means that any of the helper functions take a var or out parameter of type ShortString already contains the correct High value of the parameter. It was simply a matter of implementing ccrause's change correctly and now FPC will handle the example by dseligo better:
Multi string concatenation still has an issue, as illustrated by the following example:
Code: Pascal  [Select][+][-]
  1. var
  2.   s1: string[4] = 'wxyz';
  3.   s2: string[2] = '??';
  4.  
  5. begin
  6.   s1 := s1 + s2 + s1;
  7. end.
This code generates the following assembly:
Code: [Select]
# [20] s1 := s1 + s2 + s1;
ldi r18,lo8(TC_sPsPROJECT1_ss_S1)
ldi r21,hi8(TC_sPsPROJECT1_ss_S1)
ldi r24,lo8(8)
ldi r25,hi8(8)
add r24,r28
adc r25,r29
ldi r22,-1
mov r23,r1
mov r20,r18
call fpc_shortstr_to_shortstr
ldi r18,lo8(8)
ldi r19,hi8(8)
add r18,r28
adc r19,r29
std Y+2,r18
std Y+3,r19
ldi r20,lo8(TC_sPsPROJECT1_ss_S2)
ldi r21,hi8(TC_sPsPROJECT1_ss_S2)
ldi r24,lo8(264)
ldi r25,hi8(264)
add r24,r28
adc r25,r29
ldi r22,-1
mov r23,r1
call fpc_shortstr_to_shortstr
ldi r18,lo8(264)
ldi r19,hi8(264)
add r18,r28
adc r19,r29
std Y+4,r18
std Y+5,r19
ldi r20,lo8(TC_sPsPROJECT1_ss_S1)
ldi r21,hi8(TC_sPsPROJECT1_ss_S1)
ldi r24,lo8(520)
ldi r25,hi8(520)
add r24,r28
adc r25,r29
ldi r22,-1
mov r23,r1
call fpc_shortstr_to_shortstr
ldi r18,lo8(520)
ldi r19,hi8(520)
add r18,r28
adc r19,r29
std Y+6,r18
std Y+7,r19
ldi r20,lo8(2)
ldi r21,hi8(2)
add r20,r28
adc r21,r29
ldi r24,lo8(TC_sPsPROJECT1_ss_S1)
ldi r25,hi8(TC_sPsPROJECT1_ss_S1)
ldi r18,2
mov r19,r1
ldi r22,4
mov r23,r1
call fpc_shortstr_concat_multi
The strings on the RHS are  copied to temporaries before being passed to fpc_shortstr_concat_multi. A bit more digging required...

PascalDragon

  • Hero Member
  • *****
  • Posts: 4874
  • Compiler Developer
Re: [SOLVED] Variable addresses in SRAM
« Reply #64 on: October 10, 2022, 07:33:57 am »
... Note the highlighted stack allocations and also the missing “Temps allocated” line in the second example.

Nicely done. Leaving aside all consideration of compiler version and mode for a moment, is there any quick way, e.g. by using a SizeOf(), that something can be put into application code to report that an oversize temporary has been allocated, either at compilation or runtime?

No, you need to look at the generated assembly code.

The strings on the RHS are  copied to temporaries before being passed to fpc_shortstr_concat_multi. A bit more digging required...

Agreed...  %)

MarkMLl

  • Hero Member
  • *****
  • Posts: 5862
Re: [SOLVED] Variable addresses in SRAM
« Reply #65 on: October 10, 2022, 08:59:43 am »
No, you need to look at the generated assembly code.

Thanks for that. I need to get back into the habit... the last 30 years have made me soft.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

PascalDragon

  • Hero Member
  • *****
  • Posts: 4874
  • Compiler Developer
Re: [SOLVED] Variable addresses in SRAM
« Reply #66 on: October 11, 2022, 07:43:02 am »
The strings on the RHS are  copied to temporaries before being passed to fpc_shortstr_concat_multi. A bit more digging required...

Should be fixed.

ccrause

  • Hero Member
  • *****
  • Posts: 688
Re: [SOLVED] Variable addresses in SRAM
« Reply #67 on: October 11, 2022, 08:15:10 am »
The strings on the RHS are  copied to temporaries before being passed to fpc_shortstr_concat_multi. A bit more digging required...

Should be fixed.
Great, thank you!  The patch works for the couple of cases I tested against.

ccrause

  • Hero Member
  • *****
  • Posts: 688
Re: [SOLVED] Variable addresses in SRAM
« Reply #68 on: October 13, 2022, 08:47:46 am »
Code: Pascal  [Select][+][-]
  1. var
  2.   s1: string[10] = 'wxyz';
  3.   s2: string[2] = '??';
  4.  
  5. begin
  6.   s1 := s1 + s2 + s1;
  7. end.
While this discussion (and PascalDragon's efforts) have already greatly improved the RAM usage for the above code snippet, there is still a temporary shortstring used in the RTL procedure fpc_shortstr_concat_multi.  Here is an attempt to rewrite fpc_shortstr_concat_multi to eliminate the use of a temporary shortstring: https://gitlab.com/ccrause/fpc-source/-/commit/52a1e6040485d268c97cef1f5011370066eb31ad

This patch leads to a slightly larger code footprint for AVR, so eagle eyed coders are encouraged to critisize and/or improve this patch.

 

TinyPortal © 2005-2018