GNU C “Nested Functions” Extension and Trampoline

Required background knowledge: activation record, static scoping & dynamic scoping.

Below is not standard C code. (case1)

foo (double a, double b){
  double square (double z) { return z * z; }    
  return square (a) + square (b);
}

However GNU C supports this ‘nested function’ syntax by extension. You are also allowed to take the address of nested function and pass it to other functions like this: (case2)

hack (int *array, int size){
   void store (int index, int value){ 
        array[index] = value; 
   }
   intermediate (store, size);
}

Notice that nested function ‘store’ use ‘array’ of function ‘hack’.

Now, let us look at the implementation of nested function in a c/c++ compiler(I’ll just call it compiler from now on, but please bear in mind that I’m referring to c/c++ compiler specifically).

Static link in activation record(AR) is used to access data of other ARs that has lexical relation with it.
A C/C++ compiler does not need static link in AR to implement lexical scope, since all functions are at global scope, a function only has access to heap(allocated using malloc), static area, its own AR, but not ARs of any other function.

Thus if we implement nested function by static link, it is definitely not cost effective because you pay the price (one field for each AR) every time, but the service is seldom used(Most of the AR are not for nested function).

However if we could use static link to implement nested function only when necessary, it is win-win situation . The method is like this:

0. Suppose a register SL is used to store static link temporarily.
1. In caller, allocate a small memory on stack. Put in it assembly code equivalent to below:

     004    move caller FP to SL
     008    jmp to nested function

2. In callee, load SL to static link, and use it to access caller data.
3. Now address 0004 become the new address of nested function.

The code in step2 is called trampoline. Original function address together with caller FP is called closure in this case, which gives you a function to execute and a extra data environment to access. For details, you can refer to these references.

Oh, by the way, the following code is legal in C++

void foo() {
   void bar(int);
   bar(1);
}

References:
1. Nested-Functions
2. Simulate Nested Function in C++
3. How to implement Nested Extension by Trampoline
4. What is Closure?
5. Static vs. Dynamic Scoping
6. Implementing Scope

Leave a Comment