[m-dev.] Bug #309
Mark Brown
mark at mercurylang.org
Wed Jan 22 14:01:30 AEDT 2014
Hi Paul,
On Wed, Jan 22, 2014 at 11:02 AM, Paul Bone <paul at bone.id.au> wrote:
>
> I've been looking at bug #309. The problem is caused when a special value
> like store.store(S) or io.state is existentially typed. I have some
> questions about the correct fix, but first I'll explain the bug. Consider:
>
> :- some[S] pred root_env(environment(S)::out, S::uo) is det => store(S).
>
> :- implementation.
>
> root_env(Environment, Store) :-
> store.init(Store0),
> store.new_mutvar(map.init, Mappings, Store0, Store),
> Environment = root_environment(Mappings).
>
> Recall that:
>
> :- some[S] pred store.init(store(S)::uo) is det.
>
> and that the typeclass store(S) is also defined in store.m.
>
>
> When the user's code is compiled, root_env returns an Environment, and Store
> and the typeclass info variable for store(S). In the HLDS casts are
> introduced to assign each variable to it's existentially typed equivalent so
> that the HLDS is type correct.
>
> Before code generation the compiler allocates stack slots for variables that
> need them, for special variables like io and state(S) dummy stack slots with
> negative numbers are allocated.
>
> Finally during HLDS->LLDS code generation (before LLDS optimizations) the
> epilogue for the procedure is generated, it must copy each output variable
> into the register where the callers expect to find it. This means copying
> the existentially typed version of Store (named ExistQStore) into MR_r2.
>
> /*Start of procedure epilogue*/
> MR_r1 = MR_sv(1);
> /* Placing ExistQTypeClassInfo_for_store */
> MR_r2 = MR_tempr1;
> /* Placing ExistQEnvironment */
> MR_r3 = !!! Error !!!;
> /* Placing ExistQStore */
> MR_decr_sp_and_return(2);
> /* Return from procedure call */
> The compiler crashed before it could write the rval for the 3rd
> assignment, as it would have written an assignment from a negative stack
> slot. I've written the remaining instruction from a compiler that I've
> 'changed'.
>
> ExistQStore was stored in MR_r2 after the exist_cast (Yes that's wrong, I'll
> come back to it) however the second instruction clobbered MR_r2. This eas
> deemed okay because ExistQStore was also stored in the stack, in stack slot
> -2. This assignment crashes the compiler when it tries to write it out.
>
> As mentioned above store(S) (like io) is special and supposed to never
> actually appear in generated code,
It's never *required* to appear, but it still does. If you pass IO to
list.foldl, for example, the implementation will copy around a piece
of garbage for you.
> this is true but it's not true when it's
> existentially typed - it cannot be as the type is abstract. So if there was
> no register conflict when generating the procedure's epilogue then an
> arbitrary garbage value would have been saved into MR_r2, and the calling
> program would pass it around and as long as it was never coerced into the
> wrong type it would have been safe, it's never actually used or
> dereferenced. This appears to be a harmless bug,
It's harmless, but not a bug. There's no information here, and garbage
faithfully represents that.
> it only becomes harmful
> when a register conflict occurs and the compiler crashes.
Trying to generate code that reads something out of a dummy slot is a bug.
>
> The compiler currently tries to avoid conflicts by copying about-to-be
> clobbered information out of the way, it uses the existing stack slot if
> there is one. I modified this behaviour to ignore stack slots with negative
> numbers, in these cases a new register is used and tried it MR_r5, the
> compiler happily generates the code below, which will probably run
> correctly, well equally as correctly as the above bug without a register
> conflict.
>
> /*Start of procedure epilogue*/
> MR_r1 = MR_sv(1);
> /* Placing ExistQTypeClassInfo_for_store */
> MR_r2 = MR_tempr1;
> /* Placing ExistQEnvironment */
> MR_r3 = MR_r5;
> /* Placing ExistQStore */
> MR_decr_sp_and_return(2);
> /* Return from procedure call */
>
> So my question is, how should this _really_ be fixed. Is a hack like the one
> I've used 'good enough'?
Possibly. But why not just leave off the assignment to MR_r3 altogether?
> Or on the other extreme, should creating an
> existential version of store(S) or io be an error?
Definitely not.
Cheers,
Mark.
More information about the developers
mailing list