<div dir="ltr"><div>I would mean that different calls to |compile-file| within the same invocation of gsc, generates the exact character comparison of generated .c / binary backend output file for a given Scheme file and its included dependencies, maybe presuming some optional |compile-file| argument(s) to force determinism.</div><div><br></div><div><br></div><div>The usecase I had in mind is validation of authenticity of binaries:</div><div><br></div><div>A person or automated build script would be able to validate that a given binary executable (e.g. with hash X) corresponds to an exact given code base (e.g. with hash Y) built on a given exact compiler/build/OS environment (e.g. with hash Z or other unique identifier).</div><div><br></div><div>This could be instrumental for validation before cryptographic signing e.g. multiple participants certify that a binary is authentic, or other code review processes or security audits.</div><div><br></div><div><br></div><div>Thanks for pointing out the various ways in which achieving this is non-trivial. Right so sources of nondeterminism include</div><div> * Any timestamp included by the compiler in the compile output (would need to be removed, set to the file's creation time, or zero, or such),</div><div> * Filename included by the compiler in the compile output (would need to be forced somehow),</div><div> * Gensym,</div><div> * Undeterministic macros in user's code,</div><div> * (and CC/AS nondeterminism).</div><div><br></div><div>If the determinism would be broken or break at some point, builds would simply yield non-matching hashes, and an effort would need to be undertaken by all participants to figure out why, and that could be both obvious stuff in the user code (like the nondeterministic |stamp| macro example you provided) or some unintentional reason which probably not but still could lie in the compiler.</div><div><br></div><div><br></div><div><div>If I read you right, the compiler is expectably mostly deterministic-proofed already, but this has not been a stressed design goal and may need some tweaking both generally and for a given codebase.</div><div><br></div><div>This was mostly what I thought you would say however your confirmation made all difference.</div><div><br></div><div>Thank you for all your clarifications!</div><br class="gmail-Apple-interchange-newline"></div><div>Adam</div><div></div><div><br></div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 15 Mar 2019 at 19:47, Marc Feeley <<a href="mailto:feeley@iro.umontreal.ca">feeley@iro.umontreal.ca</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">But what does it mean for the compiler to be deterministic?<br>
<br>
- deterministic for different invocations of gsc, or for different calls of compile-file within the same invocation of gsc (gensym would only potentially be an issue in the second case)<br>
<br>
- exact character comparison of generated .c file for a given Scheme file and its include dependencies<br>
<br>
- same .c code generated regarless of file name and location in the filesystem (it is already not the case if the file names are different, and with -debug the location in the filesystem is an issue)<br>
<br>
- same .c code for each top-level Scheme definition, but possibly not same ordering of those definitions<br>
<br>
- same ordering of all the lines of .c code, but possibly different names for local labels, local variables, etc<br>
<br>
- different ordering of the instructions in the code (i.e. ordering of labels), but same semantics<br>
<br>
- same generated machine code from gcc for a given Scheme file<br>
<br>
What is your use-case and which definition of compiler determinism are you looking for?<br>
<br>
In my coding style I try to avoid nondeterministic behavior, but I have no proof that there isn’t some nondeterminism left.<br>
<br>
Thread preemption interrupts are particularly nasty because they may cause continuations to be captured in subtly different ways, which can lead to the stack area overflowing sooner or later, which impacts when GCs are triggered, which impacts where objects are allocated, which affects the collisions in eq? hash tables, which affects the ordering of table->list (which is used in a couple places in the compiler, but fortunately it seems the resulting list is then explicitly sorted).  Anyway, you get the idea that it is non-trivial to prove the strictess level of deterministic behavior (exact same generated .c code).<br>
<br>
Marc<br>
<br>
<br>
<br>
> On Mar 15, 2019, at 1:02 AM, Adam <<a href="mailto:adam.mlmb@gmail.com" target="_blank">adam.mlmb@gmail.com</a>> wrote:<br>
> <br>
> Hi Marc,<br>
> <br>
> I meant except for nondeterministic behavior caused by macros, so your example below is excluded from the definition.<br>
> <br>
> The compiler uses gensym a lot, this would be a primary cause of nondeterminism wouldn't it, so for instance if thread-safe determinism would be desired, the Gambit user would need to implement a thread-local gensym, correct?<br>
> <br>
> (This would exclude thread preemption interrupts and GC as sources of nondeterminism - thread preemption and GC would only effect |compile-file| in the way that it could affect the order of gensym calls, right?)<br>
> <br>
> Oh hashtables as a source of |compile-file| nondeterminism, interesting - what does |compile-file| use hashtables for?<br>
> <br>
> Thanks!!<br>
> Adam<br>
> <br>
> On Fri, 15 Mar 2019 at 12:55, Marc Feeley <<a href="mailto:feeley@iro.umontreal.ca" target="_blank">feeley@iro.umontreal.ca</a>> wrote:<br>
> You’ll have to define what it means for the compiler to be deterministic.  Do you expect the compilation of the following program to yield the same executable every time it is compiled?<br>
> <br>
> (define-macro (stamp)<br>
>   (read-line (open-tcp-client "<a href="http://time-a.timefreq.bldrdoc.gov:37" rel="noreferrer" target="_blank">time-a.timefreq.bldrdoc.gov:37</a>")))<br>
> <br>
> (pp (string-append "hello! " (stamp)))<br>
> <br>
> Macros open a big can of worms…<br>
> <br>
> If you avoid macros the compiler is probably mostly deterministic… but I do not have a proof handy!  Hash tables, GC, thread preemption interrupts, etc can introduce nondeterminism that may be observable in some situations.<br>
> <br>
> Marc<br>
>  <br>
> <br>
> On Tue, 26 Feb 2019 at 09:18, Adam <<a href="mailto:adam.mlmb@gmail.com" target="_blank">adam.mlmb@gmail.com</a>> wrote:<br>
> Hi Marc,<br>
> <br>
> Gambit's compiler is deterministic right?<br>
> <br>
> (As in Scheme to GVM, GVM to C or binary. Underlying C compiler's determinism is a separate matter altogether.)<br>
> <br>
> A consideration could be that compilation output is a function of |gensym| and therefore its counter must be managed for deterministic compilation.<br>
> <br>
> Another consideration that if running more instances of the compiler in one Gambit process concurrently, the |gensym| calls could have arbitrary order and therefore determinism break.<br>
> <br>
> Adam<br>
> <br>
>       Virus-free. <a href="http://www.avg.com" rel="noreferrer" target="_blank">www.avg.com</a><br>
<br>
</blockquote></div></div>