Cで継続渡し(末尾最適化バージョン)修正版

以前のバージョンは、アセンブラを使っていた名残で、void*とintが同じサイズであるという移植性のない仮定をしていたので修正した。
変更点としては、キャストの代わりにunionを使うようにしただけ。

  • fib.c
#include <stdio.h>
#include <stdlib.h>

#include "obj.h"

int main(int argc, char *argv[])
{
    // 汎用レジスタのつもり
    var_t r1;
    var_t r2;

    closure_t c = newClosure(0);
    c->func = (func_t) &&fib_cps_0;

    for (int i = 1; i < 10; i++) {
        addRef((object_t) c);

        // printf("Fibonacci(%d) = %d\n", i, fib_cps(i, c));

        r1.intVal = i;
        r2.ptr = c;
        goto fib_cps;

 fib_cps_0: // int fib_cps_0(int n, closure_t k)
        {
            int n = r1.intVal;
            closure_t k = (closure_t) r2.ptr;
            release((object_t) k);

            printf("Fibonacci(%d) = %d\n", i, n);
        }
    }

    release((object_t) c);

    return EXIT_SUCCESS;

 fib_cps: // int fib_cps(int n, closure_t k)
    {
        int n = r1.intVal;
        closure_t k = (closure_t) r2.ptr;
        if ((n == 1) || (n == 2)) {
            r1.intVal = 1;
            r2.ptr = k;
            goto *(k->func);
            // return (int) k->func(1, k);
        } else {
            closure_t c = newClosure(2);
            c->func = &&fib_cps_1;
            c->vars[0].ptr = k;
            c->vars[1].intVal = n;

            r1.intVal = n - 1;
            r2.ptr = c;
            goto fib_cps;
            // return fib_cps(n - 1, c);
        }
    }

 fib_cps_1: // int fib_cps_1(int v1, closure_t k1)
    {
        int v1 = r1.intVal;
        closure_t k1 = (closure_t) r2.ptr;

        int n = k1->vars[1].intVal;
        closure_t c = newClosure(3);
        c->func = &&fib_cps_2;
        c->vars[0].ptr = k1->vars[0].ptr;
        c->vars[1].intVal = v1;
        c->vars[2].intVal = n;
        release((object_t) k1);

        r1.intVal = n - 2;
        r2.ptr = c;
        goto fib_cps;
        // return (int) fib_cps(n - 2, c);
    }

 fib_cps_2: // int fib_cps_2(int v2, closure_t k2)
    {
        int v2 = r1.intVal;
        closure_t k2 = (closure_t) r2.ptr;

        int v1 = k2->vars[1].intVal;
        closure_t k = (closure_t) k2->vars[0].ptr;
        release((object_t) k2);

        r1.intVal = v1 + v2;
        r2.ptr = k;
        goto *(k->func);
        // return (int) k->func(v1 + v2, k);
    }
}
  • obj.h
#ifndef __OBJ_H__
#define __OBJ_H__

typedef union {
    int intVal;
    void *ptr;
} var_t;

typedef var_t (*func_t)(/* unspecified */);
typedef void (*proc_t)(/* unspecified */);

typedef struct object {
    int ref_count;
    size_t size;
    proc_t destructor;
} *object_t;

void addRef(object_t obj);
void release(object_t obj);
object_t newObject(size_t size, proc_t destructor);
void deleteObject(object_t obj);

typedef struct closure {
    // inherited from object_t
    int ref_count;
    size_t size;
    proc_t destructor;
    // own data
    func_t func;
    int var_num;
    var_t vars[0];
} *closure_t;

closure_t newClosure(int var_num);

#endif /* __OBJ_H__ */
  • obj.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "obj.h"

void addRef(object_t obj)
{
    assert(obj);
    obj->ref_count++;
}

void release(object_t obj)
{
    assert(obj);
    obj->ref_count--;
    if (obj->ref_count <= 0) {
        if (obj->destructor)
            obj->destructor(obj);
        deleteObject(obj);
    }
}

object_t newObject(size_t size, proc_t destructor)
{
    assert(size >= sizeof(struct object));

    object_t obj = malloc(size);
#ifdef DEBUG
    fprintf(stderr, "malloc: %p\n", obj);
#endif
    obj->ref_count = 0;
    obj->size = size;
    obj->destructor = destructor;
    addRef(obj);
    return obj;
}

void deleteObject(object_t obj)
{
    assert(obj);
#ifdef DEBUG
    size_t size = obj->size;
    char *p = (char*) obj;

    // filled by `de ad be ef' ... pattern
    for (int i = 0; i < size / 4; i++) {
        *p++ = '\xde'; *p++ = '\xad';
        *p++ = '\xbe'; *p++ = '\xef';
    }
    switch (size % 4) {
        case 3: p[2] = '\xbe'; /* fall-through */
        case 2: p[1] = '\xad'; /* fall-through */
        case 1: p[0] = '\xde';
    }
#endif /* DEBUG */

    free(obj);

#ifdef DEBUG
    fprintf(stderr, "free: %p\n", obj);
#endif
}

closure_t newClosure(int var_num)
{
    assert(var_num >= 0);
    size_t size = sizeof(struct closure) + sizeof(var_t) * var_num;
    closure_t c = (closure_t) newObject(size, NULL);
    c->var_num = var_num;
    return c;
}