Continuation-passing in C?

adam at visix.com adam at visix.com
Thu Mar 28 17:06:49 AEST 1991


How can I (efficiently) program in a continuation-passing style in C?

This breaks down into two basic questions:

(1) How do I explicitly jump across functions
(2) How do I keep things in registers across functions

Simple example:

main()
{
    register int a, b, c;

    a=1; b=2; c=3;
    foo( a, b, c );
    baz( a, b, c );
    bar( a, b, c );
}

I want the compiler to emit code that

(1) jumps directly from the end of foo to the start of baz, instead of
returning to main only to jump to baz

(2) leaves a, b, and c in registers instead of mucking around on a stack
(or leaves them on the stack instead of popping and pushing them)

Are most compilers smart enough to do this?  Is it possible to trick
most compilers into doing this (how do I get it to emit the right jump?)
Am I being anal about performance?

More complex example:

main()
{
    register int a, b, c;

    a=1; b=2; c=3;
    foo( a, b, c, baz, bar );
    c++;
}

foo( a, b, c, afn, bfn )
register int a, b, c;
int (*afn)();
int (*bfn)();
{
    if (a > b)
	afn( a, b, c );
    else
	bfn( a, b, c );
}

Same questions: can I force a jump from the end of afn back to main
(tail recursion)?  Can I force a, b, and c to stay in registers?
Should I just trust the compiler?  (the compiler is your *friend*...)

I guess I can use setjmp/longjmp for the complex example, except that
if I had a real (long) tail-recursive chain I wouldn't want to be
allocating stack frames in the first place; I should replace them with
each successive call.

But I can't use setjmp/longjmp at all for the simple example.

There are two solutions I can think of.

/* Number 1 */

main()
{
    register int r1, r2, r3, r4, r5, continuation;

    r1 = 1; r2 = 2; r3 = 3; r4 = BAZ; r5 = BAR;
    continuation = FOO
    
    while(1)
    {
        switch (continuation)
        {
        case MAIN2:
	    r3++;
	    continuation = DONE;
	    break;
        case FOO:
	    if (r1 > r2)
                continuation = r4;
	    else
	        continuation = r5;
	    break;
        case BAZ:
	    continuation = MAIN2;
	    break;
        case BAR:
	    continuation = MAIN2;
	    break;
        case DONE:
	    exit(0);
	}
    }
}

/* Number 2 */

static void (*continuation)()
static int r1, r2, r3, r4, r5;

done()
{
    exit(0);
}

main2()
{
    r3++;
    continuation = done;
}

baz()
{
    continuation = main2;
}

bar()
{
    continuation = main2;
}

foo()
{
    if (r1 > r2)
	continuation = r4;
    else
	continuation = r5;
}

main()
{
    r1 = 1; r2 = 2; r3 = 3; r4 = baz; r5 = bar;
    continuation = foo;

    while(1)
    {
	(*continuation)();
    }
}

Okay, it's twisted.  That's the point :-).

Actually, I kind of like Number 2, except that I can't force my
registers to really be registers.

Is there anything better?

Adam



More information about the Comp.lang.c mailing list