How to handle nested procedures?

How would following Pascal code be translated to LLVM?

  procedure outer(a: integer): integer;
    procedure inner(b: integer): integer;
    begin
      return a + b
    end;
  begin
    return inner(a)
  end;

If outer(10) is called, it returns 20.

The problem here is that inner has access to the locals and formal
arguments of outer. I do not see any direct support for this. It could
be handled by creating a struct for outer that contains everything used
by inner procedures and explicitly passing its address to the inner
procedures as an extra parameter.

Am I missing something? If not, are there any plans to directly support
this?

How would following Pascal code be translated to LLVM?

  procedure outer(a: integer): integer;
    procedure inner(b: integer): integer;
    begin
      return a + b
    end;
  begin
    return inner(a)
  end;

If outer(10) is called, it returns 20.

The problem here is that inner has access to the locals and formal
arguments of outer. I do not see any direct support for this. It could
be handled by creating a struct for outer that contains everything used
by inner procedures and explicitly passing its address to the inner
procedures as an extra parameter.

There is no need for support of this, keep in mind that normal Pascal
compilers translate to local assembly code which also does not support inner
functions. The struct idea is probably a good LLVM way to do it. In a
normal compiler you would use displays or access links, which basically
consist of having some way to refer to the stack frame of a parent function.
The struct would be acheiving the same thing.

I was concerned that if the optimizer inlined inner, it would not be
able to eliminate all the overhead introduced by this scheme. So I ran
a test. I simulated the above with the following C code:

struct FRAME { int a; };

static int inner(struct FRAME* frame, int b) {
        return frame->a + b;
}

int outer(int a) {
        struct FRAME frame;
        frame.a = a;
        return inner(&frame, a);
}

This produces:

int %outer(int %a) {
entry:
  %tmp.4.i = shl int %a, ubyte 1 ; <int> [#uses=1]
  ret int %tmp.4.i
}

Function inner isn't even emitted (no doubt a consequence of being
static, as an inner method shouldn't have external visibility). I can't
complain :slight_smile:

> functions. The struct idea is probably a good LLVM way to do it. In a
> normal compiler you would use displays or access links, which basically
> consist of having some way to refer to the stack frame of a parent function.
> The struct would be acheiving the same thing.

Yup Patrick is right, either using lambda lifting, displays, etc to
implement this. We don't plan to have direct support for it in LLVM.

I was concerned that if the optimizer inlined inner, it would not be
able to eliminate all the overhead introduced by this scheme. So I ran
a test. I simulated the above with the following C code:

...

This produces:

int %outer(int %a) {
entry:
  %tmp.4.i = shl int %a, ubyte 1 ; <int> [#uses=1]
  ret int %tmp.4.i
}

Not bad :slight_smile:

Function inner isn't even emitted (no doubt a consequence of being
static, as an inner method shouldn't have external visibility). I can't
complain :slight_smile:

Yup, it got emitted but then later deleted by the optimizer (exactly
because it was marked static/internal. This is certainly the right way to
implement this in an LLVM-targetting compiler.

-Chris