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

Rodney Bates rodney.m.bates at acm.org
Mon Nov 30 22:00:12 CET 2015

  Branch: refs/heads/master
  Home:   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.

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

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

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.

More information about the M3commit mailing list