[M3commit] [modula3/cm3] b3c18e: Rework indirect calls to possibly nested procedure...

Rodney M. Bates rodney_bates at lcwb.coop
Wed Dec 2 22:04:26 CET 2015



On 11/30/2015 03:00 PM, Rodney Bates wrote:
>    Branch: refs/heads/master
>    Home:   https://github.com/modula3/cm3
>    Commit: b3c18e6125d3ade015400c8e1fe9febdebc597a2
>        https://github.com/modula3/cm3/commit/b3c18e6125d3ade015400c8e1fe9febdebc597a2
>    Author: Rodney Bates <rodney.m.bates at acm.org>
>    Date:   2015-11-30 (Mon, 30 Nov 2015)
>
>    Changed paths:
>      M m3-sys/llvm3.6.1/src/M3CG_LLVM.m3
>
>    Log Message:
>    -----------
>    Rework indirect calls to possibly nested procedures.
>
> Fixes p045 and p048.  p140 has regressed.

p045 probably was failing before.  This failure appears to be a result
of incompatible machine-level parameter passing between the m3cc and
the m3llvm back ends.  Procedure CallProc, in RTExFrame.m3, in the runtime
makes a callback to a compiler-generated FINALLY procedure, passing a
static link that was originally given to PushEFrame.  The runtime is
compiled by m3cc and the test case by m3llvm.  This won't be fixed
until we are ready to compile the libraries, etc. using m3llvm.

This low-level ABI discrepancy needs to be revisited.  Merely making
the static link be the last parameter instead of the first does not
fix the problem.  Also, CallProc passes the  RT0.RaiseActivation, in
addition to a SL, but the front end compiles the FINALLY procedure
with no corresponding formal.  This seems unnecessarily peculiar,
despite being apparently harmless.

>
> This was very tricky.
>
> There is a big semantic mismatch between CM3 CG's IR and llvm's,
> for indirect calls.  In CM3, parameters are passed by separate
> operators, and the call operator just transfers control.  So it
> is possible to have two different parameter-passing sequences,
> one with and one without a static link, selected at runtime,
> merging to a single call operator.  The selection depends on
> whether the procedure variable is a closure (implies call a
> nested procedure) or not (implies call a top-level procedure).
>
> In llvm, the actual parameter list is part of the call operator,
> which denotes the number and llvm types of all the parameters.
> Thus, two different calls are needed, in two different basic
> blocks.
>
> Moreover, the CM3 IR does not provide the needed information
> where it is needed.  Actual passing of the static link is denoted
> by a pop_static_link operator, but evaluation of the SL value
> is done by general-purpose operators such as load, etc., that
> do not give any indication what they are computing.  By the time
> we see the pop_static_link, these have passed.  But the code address
> to call to, result type, and calling convention (not currently used,
> but needs to be kept available for the future) are not there.  The
> result type could be saved from the start_call_indirect, but the
> calling convention is not there either.
>
> When we see the call_indirect operator, the static link value
> has been computed and we can, at compile-time, find its llvm
> variable, but it is computed in a different basic block and
> is not available at runtime.  We can jump around among basic
> blocks at compile time, but llvm's SSA and dominator rules
> mean we can't use it.
>
> Moreover, CM3's seemingly nice idea of making IR operators
> method calls makes it very awkward to do idiom recognition or
> state-dependent handling of general-purpose operators.
>
> The solution used here involves waiting until we get the
> call_indirect operator, then doing idiom recognition on the
> llvm code already produced by the general-purpose operations
> to compute the static link, backing up from there and finding
> the procedure pointer to recompute the code address.  We put
> this back in the basic block where the pop_static_link is
> located, then generate the nested indirect call there.  The
> other code, in the wrong place for this scheme, is isolated
> into an unreachable basic block, which, presumably, llvm will
> remove.
>
> Moreover, if this is a function being called, the two calls
> produce their function results in two different basic blocks.
> This is handled by manually constructing a phi node in the
> block where the paths merge.  Phi nodes are usually only
> constructed internally by llvm.
>
>

-- 
Rodney Bates
rodney.m.bates at acm.org



More information about the M3commit mailing list