Recent

Author Topic: Dynarray initialization pattern  (Read 3407 times)

Чебурашка

  • Hero Member
  • *****
  • Posts: 579
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: Dynarray initialization pattern
« Reply #15 on: March 25, 2024, 08:08:06 pm »
Until recently, dynarr := nil compiled into exactly the same code as SetLength(dynarr, 0).

Curiosity: did fpc give a hint for dynarr := nil, given that this expression is transformed into SetLength(dynarr, 0), for which it does give the hint?
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

Чебурашка

  • Hero Member
  • *****
  • Posts: 579
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: Dynarray initialization pattern
« Reply #16 on: March 25, 2024, 08:41:42 pm »
Until recently, dynarr := nil compiled into exactly the same code as SetLength(dynarr, 0).

Curiosity: did fpc give a hint for dynarr := nil, given that this expression is transformed into SetLength(dynarr, 0), for which it does give the hint?

Sorry, I correct my self:

"given that this expression was compiled to the same code of SetLength(dynarr, 0)"
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

runewalsh

  • Jr. Member
  • **
  • Posts: 84
Re: Dynarray initialization pattern
« Reply #17 on: March 25, 2024, 09:12:33 pm »
No ofc (maybe it happens at a later stage than where conditions for this warning are checked), that’s what I implied :>.

Martok

  • New Member
  • *
  • Posts: 24
Re: Dynarray initialization pattern
« Reply #18 on: March 25, 2024, 09:28:52 pm »
They do, compiler re-initializes (Finalize + Initialize) managed types passed as out parameters, and this is documented (if allegorically)
The documentation says a lot of things that don't hold in generated code...

However, in this case it looks like you are right and this got fixed at some point. That's great news!

Your example function was almost exactly the scenario I first encountered this kind of errors with. s would be uninitialized but hold a random copy of the Result variable if the compiler decided to not put Result in memory at all and instead use only eax. You'd end up with both pointing to the same variable with, as you say, disastrous consequences. One of the harder ones to find, especially as -gt makes it go away - both point to something initialized then, and get deduplicated on first write.

Interestingly, compiling the following with -gt does not trash s, it still gets initialized as nil, even on -O4.

Code: Pascal  [Select][+][-]
  1. procedure main;
  2. var
  3.         s: string;
  4. begin
  5.         WriteLn('s(caller)=', s);
  6. end;

This makes me think correct initialization is now a strong guarantee. The original hint on SetLength is a bit more pointless then, but a generic wrapper would actually be correct. Still not as good as an intrinsic and without implicit specialization the syntax would be a bit much just to avoid a hint, but:

Code: Pascal  [Select][+][-]
  1. {$ModeSwitch implicitfunctionspecialization}    
  2. type
  3.   generic TArray<T> = array of T;
  4.  
  5. generic procedure ArrayNew<T>(out Arr: specialize TArray<T>; const dim1: integer); inline;
  6. begin
  7.   // Arr is guaranteed nil here
  8.   SetLength(Arr{%H-}, dim1);
  9. end;
  10.  
  11. type TDoubleArray = array of Double;
  12.  
  13. function ss(out s: TDoubleArray): string;
  14. begin
  15.   ArrayNew(s, 42);
  16.   // no hint, and thanks to inline the exact same code as SetLength
  17. end;


PascalDragon

  • Hero Member
  • *****
  • Posts: 5526
  • Compiler Developer
Re: Dynarray initialization pattern
« Reply #19 on: March 26, 2024, 08:46:28 pm »
This Hint is entirely correct

I'm uncomfortable with this: the hint is only correct because SetLength() expects a var parameter.

It is reasonable for SetLength() to expect a var parameter when an array or string is being shortened, but not when it is being lengthened. Since a Pascal compiler is unable to distinguish between these cases, SetLength()'s parameter should be an out which should, in principle at least, eliminate the hint/warning.

As such, this merits a bug report.

No, it does not. SetLength is not an ordinary procedure, it's a compiler intrinsics and as such there is no concept as “var-parameter” for it, instead it works directly on the expression passed in.

If the array was non-empty (never the case for managed out parameters, they always start freshly initialized)
Here's the fun part: they don't.

The "The initial value of the parameter on function entry is discarded, and should not be used" in the docs is doing a lot of heavy lifting there.

Depending on how the compiler feels about the calling convention and register pressure, the out parameter may end up being just an uninitialized register. Just calling SetLength on that tries to resize that garbage pointer and will obviously crash. Clearing it first incidentally doesn't attempt to free the old value, for whatever reason. This has bitten me so many times that I'd even argue the Hint should be a Warning.

Managed types - like in this case dynamic arrays - always have valid values. If not, it's a bug.

Martok

  • New Member
  • *
  • Posts: 24
Re: Dynarray initialization pattern
« Reply #20 on: March 28, 2024, 05:39:03 pm »
Managed types - like in this case dynamic arrays - always have valid values. If not, it's a bug.
No weird corner cases? This is extremely good to know, thanks.

Do you happen to know if DFA knows/counts this "assignment"? Not neccessarily for optimization, but for the initialized logic checks.

Thaddy

  • Hero Member
  • *****
  • Posts: 14625
  • Sensorship about opinions does not belong here.
Re: Dynarray initialization pattern
« Reply #21 on: March 28, 2024, 06:30:12 pm »
the only exception i know is with managed types with a range not starting with zero.
managed types always initialize to all zero's or empty so can cause a range check error.
other than that, it has no corner cases, and besides my remark, the memory is initialized.
bitrate is always calculated like this:sample rate * bitdepth * number of channels.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5526
  • Compiler Developer
Re: Dynarray initialization pattern
« Reply #22 on: March 29, 2024, 02:12:47 pm »
Do you happen to know if DFA knows/counts this "assignment"? Not neccessarily for optimization, but for the initialized logic checks.

This is completely unrelated to the DFA.

the only exception i know is with managed types with a range not starting with zero.
managed types always initialize to all zero's or empty so can cause a range check error.
other than that, it has no corner cases, and besides my remark, the memory is initialized.

The value might not always be valid, especially with managed records, but it will always be deterministic.

 

TinyPortal © 2005-2018