Language.C (cont.) - flexible array member(C99) を サイズ0の配列(GNU C互換)に変換

深追い日記.
jhcが生成した手元のコードを gcc 2.95でコンパイルするにはもうひとつ障害があった.

JHCはサンクを以下の構造体で管理するようだ:

typedef struct node {
        fptr_t head;
        sptr_t rest[];
} A_MAYALIAS node_t;

typedef struct dnode {
        what_t what;
        sptr_t rest[];
} A_MAYALIAS dnode_t;

ここで、 rest[] は flexible array member とよばれ、そのままだと(おおむね)サイズ0の配列として扱われる.この構造体を使うには、malloc(sizeof(node_t)+ほげほげ)のようにして、rest分の領域を確保する.

これのp.103, 6.7.2.1節の16に載ってます:
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

しかし gcc 2.95 は flexible array memberをサポートしてない.代わりに、GNU Cでサポートされている長さ0の配列を使うように変換したい.

5.12 Arrays of Length Zero - Using the GNU Compiler Collection (GCC)

flexible array memberを使っているのは現状ここだけなので、もうsedで書き換えても良い(もしくはjhcのコードを書き換えるか…)くらいなのだけど、あえて Language.Cでこれを実装した.

リファレンスとLanguage.C.Syntax.ASTの型名とにらめっこすれば大抵のことは分かるので気合いで書けます

converting flexible array member to an array of size 0

可読性が超悪くてごめんなさい

convFlexibleArrayMember :: CTranslUnit -> CTranslUnit
convFlexibleArrayMember = everywhere (mkT conv)
  where 
   conv :: CStructUnion -> CStructUnion
   conv (CStruct CStructTag ident (Just members@(_:_)) attrs ninfo) = CStruct CStructTag ident (Just $ init members ++ [conv_ $ last members]) attrs ninfo
   conv x = x
   conv_ (CDecl specs vars@(_:_) ninfo) = CDecl specs (init vars ++ [conv__ $ last vars]) ninfo
   conv_ x = x
   conv__ (Just (CDeclr ident vars@(_:_) lit attrs ninfo), cinit, expr) = (Just $ CDeclr ident (init vars++[convArrDeclr $ last vars]) lit attrs ninfo,cinit,expr)
   conv__ x = x
   convArrDeclr :: CDerivedDeclr -> CDerivedDeclr
   convArrDeclr (CArrDeclr quals (CNoArrSize False) ninfo) =  (CArrDeclr quals (CArrSize False (CConst (CIntConst (fromRight (readCInteger DecRepr "0")) (OnlyPos (Position (posFile $ posOfNode ninfo) 0 0))))) ninfo)
   convArrDeclr x = x
   fromRight (Right i) = i
   fromRight _ = error "impossible"

usage

こんな感じで使ってます:

arm-linux-gcc -E hs.out_code.c |./MoveVarDecls |./ConvIncompleteArray >converted.c && arm-linux-gcc -mstructure-size-boundary=8 converted.c

-mstructure-size-boundary=4 は ARM専用のオプションでアラインメントを4の倍数にするために使ってます (現状、意図通りに動いているもよう)嘘でした。boundaryは8の倍数でないとダメです。
JHCはポインタの下位2ビットをガベコレ用フラグと遅延評価フラグに使ってるのでこれをやらないとまずいことになるっぽいですこれをやってもassert failureは起きます。なんでだろう。