2008年12月9日火曜日

mipsel-nec-elf

Toppers/JSP の KZ-Vr4131PCI-01 ターゲットは、GCC の mipsel-nec-elf というターゲットを前提にしているらしいので、とりあえず作ってみた。

http://cvs.sourceforge.jp/cgi-bin/viewcvs.cgi/jsp4cm3/jsp/doc/mips3.txt?rev=1.1
http://cvs.sourceforge.jp/cgi-bin/viewcvs.cgi/jsp4cm3/jsp/doc/gnu_install.txt?rev=1.1

(ただし、binutils-2.13.2.1、gcc-3.3.2、newlib-1.11.0 の環境らしいので、以下の環境で通るかどうかはわからない。)
$ tar zxvf binutils-2.19.tar.gz
$ mkdir build-binutils-mipsel-nec-elf
$ cd build-binutils-mipsel-nec-elf
$ ../binutils-2.19/configure --prefix=/usr/local/mips --target=mipsel-nec-elf --disable-nls
$ make -i
$ make -i install
$ tar zxvf gcc-4.3.2.tar.gz
$ tar zxvf newlib-1.16.0.tar.gz
$ cd gcc-4.3.2.tar.gz
$ ln -s ../newlib-1.16.0/newlib
$ ln -s ../newlib-1.16.0/libgloss
$ cd ..
$ mkdir build-gcc-mipsel-nec-elf
$ cd build-gcc-mipsel-nec-elf
$ ../gcc-4.3.2/configure --with-gmp=/usr/local --with-mpfr=/usr/local --enable-languages=c,c++ --disable-nls --disable-win32-regist
ry --disable-shared --disable-libssp --prefix=/usr/local/mips/ --target=mipsel-nec-elf --disable-multilib --without-fp --disable-li
bmudflap --disable-libstdcxx-pch --with-newlib --with-headers=/home/aloha/work/mips/build/gcc-4.3.2/newlib/libc/include/
$ make
$ make install

重要なのは configure オプションだけなので、展開のところとかは適宜読み替えて適当に。
texinfo 絡みでエラーが出るので、make -i で強制的にビルドとインストールを進めている (他に致命的なエラーがあった場合、ひどい事になる場合もあるので、本当は随時確認しながら無視した方が良い。今回はコケることが最初からわかっているので、最初からエラーを無視 (-i) してビルド & インストールしてる)

2008年12月6日土曜日

mips-unknown-elf gcc with newlib

以前は mips-linux-elf で作ったので、linux に依存するヘッダを要求されてコケた。

linux kernel をビルドすることに拘らなければ、mips-unknown-elf で普通に通った。

newlib-1.16.0 を展開して、gcc-4.3.2/ 以下に newlib-1.6.0/newlib と newlib-1.6.0/libgloss へのシンボリックリンクを張る。

binutils -> gcc -> newlib の順に、以下のように configure ; make ; make install する (別途 build-*** のようなディレクトリを掘って、そこで行う)。

途中で makeinfo がどうこうというエラーが出たら、make -i で無視してビルドを続行する。
$ ../binutils-2.19/configure --prefix=/usr/local/mips --target=mipsel-unknown-elf --disable-nls --without-included-gette
xt

$ ../gcc-4.3.2/configure --with-gmp=/usr/local --with-mpfr=/usr/local --enable-languages=c,c++ --disable-nls --disable-w
in32-registry --disable-shared --without-included-gettext --disable-libssp --prefix=/usr/local/mips/ --target=mipsel-unk
nown-elf --disable-multilib --without-fp --disable-libmudflap --disable-libstdcxx-pch --with-newlib --with-headers=/home
/aloha/work/mips/build/gcc-4.3.2/newlib/libc/include/

$ ../newlib-1.16.0/configure --target=mipsel-unknown-elf --prefix=/usr/loca/mips

(newlib のビルドは gcc と同時に行われるから不要かもしれない。未確認)

できた gcc で、-EB オプションを付けると
$ /usr/local/mips/bin/mipsel-unknown-elf-gcc.exe -EB test.c
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crti.o: compiled for a little endian system and target is big endian
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crti.o: endianness incompatible with that of the selected emulation
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: failed to merge target specific data of file c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crti.o
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtbegin.o: compiled for a little endian system and target is big endian
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtbegin.o: endianness incompatible with that of the selected emulation
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: failed to merge target specific data of file c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtbegin.o
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtend.o: compiled for a little endian system and target is big endian
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtend.o: endianness incompatible with that of the selected emulation
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: failed to merge target specific data of file c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtend.o
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtn.o: compiled for a little endian system and target is big endian
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtn.o: endianness incompatible with that of the selected emulation
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: failed to merge target specific data of file c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/crtn.o
c:/msys/1.0/local/mips/bin/../lib/gcc/mipsel-unknown-elf/4.3.2/../../../../mipsel-unknown-elf/bin/ld.exe: warning: cannot find entry symbol _start; defaulting to 0000000000400050
collect2: ld returned 1 exit status

エラーになってしまう (最後の警告は、crt0.o と ld script を用意してないから当たり前)

これを両方サポートできるようにするために、enable-targets とかでなんとかできるかと思ったけど、結局上手く行かなかった。

2008年12月5日金曜日

VEC

gcc の VEC (vec.[ch]) データ構造は、プリプロセッサを使って C++ の template みたいなことをやってるらしい (C++ 言語のカラクリ とかでも触れられてるけど、cfront 時代の template の実現手法としては比較的ポピュラー)

ほんと GCC は C++ の機能を C で無理やり実装するのが得意だよなぁ。

2008年12月4日木曜日

GCC の MIPS ターゲット

newlib で作ろうとしたら、asm/unistd.h が無いよ!と、明らかに Linux Kernel の header らしきファイルを要求されて上手く行かなかった。

しかたがないので、--disable-libstdc と --disable-libstdcxx で作ってみた (MinGW + MSYS + flex + bison)。
$ ../binutils-2.19/configure --prefix=/usr/local/mips --target=mips-elf --disable-nls --without-included-gettext
...
$ ../gcc-4.3.2/configure --with-gmp=/usr/local --with-mpfr=/usr/local --enable-languages=c,c++ --disable-nls --disable-win32-registry --di
sable-shared --without-x --without-included-gettext --disable-libssp --prefix=/usr/local/mips/ --target=mips-elf --disable-multilib --with
out-fp --disable-libmudflap --disable-libstdc --disable-libstdcxx --disable-libstdcxx-pch --enable-threads --enable-hash-synchronization
...

enable-threads とか enable-hash-synchronizasion は、特に意味が無いようだ。disable-libstdcxx-pch も、たぶんいらない気がする (MinGW では、pch (PreCompiled Header) のテストが通らないから、いつまでたっても gcc が 4 系にならないらしい。ssp (StackSmashingProtector) と pch は disable にしないと、普通の gcc もビルドできない。
$ /usr/local/mips/bin/mips-elf-gcc-4.3.2.exe -v
Using built-in specs.
Target: mips-elf
Configured with: ../gcc-4.3.2/configure --with-gmp=/usr/local --with-mpfr=/usr/local --enable-languages=c,c++ --disable-nls --disable-win32-registry --disable-shared --without-x --without-included-gettext --disable-libssp --prefix=/usr/local/mips/ --target=mips-elf --disable-multilib --without-fp --disable-libmudflap --disable-libstdc --disable-libstdcxx --disable-libstdcxx-pch --enable-threads --enable-hash-synchronization
Thread model: single
gcc version 4.3.2 (GCC)

libgcc の target ごとに使われる関数

libgcc は、乗算や除算など C の言語仕様のうち、CPU が直接サポートしてない機能を補助するために、GCC が勝手にくっ付けるライブラリ。

gcc-4.3.2/gcc/config/arm/t-linux とかを見ると。
LIB1ASMSRC = arm/lib1funcs.asm
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_lnx

のように、呼ばれる関数が書いてある。

target ごとの設定は t-* とかに書いてあるらしい。

2008年12月3日水曜日

GCC のサンプルバックエンド

Incremental Machine Descriptions for Spim を参考に、gcc-4.3.2 に対応させて作ってみた。
http://github.com/alohakun/gcc-sample-backend/tree/master
最小限の C コードだけに対応したバックエンド。
$ ../bin/gcc-spim.exe -v
Using built-in specs.
Target: spim
Configured with: ../gcc-4.3.2/configure --with-gmp=/usr/local
--with-mpfr=/usr/local --enable-languages=c
--prefix=/home/aloha/test/gcc --target=spim --disable-libgcc
--enable-threads --disable-nls --disable-win32-registry --disable-shared
--without-x --enable-hash-synchronization
--enable-version-specific-runtime-libs --without-included-gettext
--disable-bootstrap --disable-libssp --program-suffix=-spim
--with-as=/usr/local/bin/as --with-ld=/usr/local/bin/ld
Thread model: single
gcc version 4.3.2 (GCC)

$ cat test0.c
void f()
{
L:
goto L;
}

void g()
{
}

$ ../bin/gcc-spim.exe -S -O2 test0.c

$ cat test0.s
.text
.align 2
.globl f
f:
L2: sw $ra, 0($sp)
sw $sp, -4($sp)
sw $fp, -8($sp)
move $fp,$sp
addi $sp, $fp, -44
L3: j L3
.align 2
.globl g
g:
sw $ra, 0($sp)
sw $sp, -4($sp)
sw $fp, -8($sp)
move $fp,$sp
sw $s0, 0($fp)
sw $s1, -4($fp)
sw $s2, -8($fp)
sw $s3, -12($fp)
sw $s4, -16($fp)
sw $s5, -20($fp)
sw $s6, -24($fp)
sw $s7, -28($fp)
addi $sp, $fp, -44
lw $s0, 0($fp)
lw $s1, -4($fp)
lw $s2, -8($fp)
lw $s3, -12($fp)
lw $s4, -16($fp)
lw $s5, -20($fp)
lw $s6, -24($fp)
lw $s7, -28($fp)
move $sp,$fp
lw $fp, -8($sp)
lw $ra, 0($sp)
jr $ra

こんだけでも move や addi が生成されるので、けっこう大きい。

変更ファイル : (spim ターゲットを追加しただけ)

gcc-4.3.2/config.sub
gcc-4.3.2/gcc/config.gcc

追加ファイル :

gcc-4.3.2/gcc/config/spim/spim.md (138 行)
gcc-43.2/gcc/config/spim/spim.h (370 行)
gcc-4.3.2/gcc/config/spim/spim.c (737 行)
gcc-4.3.2/gcc/config/spim/spim-protos.h (52 行)

展開して、--target=spim を指定して、通常通りビルドすればいけるはず (prefix を適当に指定)。
libgcc と as と ld を disable にしてあるので、binutils のインストールもたぶん不要 (もちろん、-S 付けてのアセンブリコード生成までしかできない)

2008年12月2日火曜日

RTL 式

RTL 式は、常にポインタ経由で扱われる C 構造体 struct rtx 型。

整数、ワイド整数(HOST_WIDE_INT)、C 文字列(char*)、配列を含む S 式。

RTL 式内の C 文字列 "..." は、基本的に C の文字列リテラルと同じだけど、複数行に渡って記述できるように拡張されている。

配列は [ ... ] という式表現。任意個のポインタを含む。
長さ 0 の配列は作れない。代わりにヌルポインタを使う。
ヌルポインタは (nil) と書く。

GET_CODE (x) で取得、PUT_CODE (x, newcode) で変更できる RTL コードにより、オペランドの数などが決まる。
rtl.def で定義されている列挙体。

gcc-4.3.2/gcc/rtl.def

RTL 式の中では "const_int" のような小文字だけの文字列。
C 式の中では CONST_INT のような大文字の定数が使われる。

RTX

rtx ってのは RTL eXpression の略らしい。

http://gcc.gnu.org/onlinedocs/gccint/RTL-Objects.html#RTL-Objects

2008年11月30日日曜日

GCC 4 のデータ

GCC 4 について学ぶ

GCC は 30 のアーキテクチャに対応し 1500 万行もあるらしい。

GCC Internals, D. Novillo, 2007 International Symposium on Code Generation and Optimization (CGO), San Jose, California, March 2007.

diego novillo は 220 万 LOC だと書いている (SLOCCount というツールを使ったらしい)

どういう勘定をしたのだろうか。いくらなんでも 1500 万 LOC は多すぎる気がする。

最新の GCC 4.4-2008128 で数えてみる。
~/work/gcc/gcc-4.4-20081128$ find . -print0 | xargs -0 wc -l
668335 合計

まぁ、こんなものだと思う。snapshot 版だから中身が少ないのかもしれないけど。

試しに 4.3.2.tar.bz2 でもやってみた。
~/work/gcc/gcc-4.3.2$ find . -print0 | xargs -0 wc -l
235370 合計

う~ん。

2008年11月28日金曜日

コード挿入

デバッグやプロファイルやセキュリティチェックなどを目的として、任意のコードを仕込みたいという需要は多いと思う。

やり方としては、GIMPLE レベルで仕込む方法と、MD レベルで仕込む方法の、主に 2 種類ぐらいがあるようだ。

GIMPLE レベルで何かを仕込むのは、rtx に対する知識が必要だし、最適化の過程でコードの順番などが入れ替わる可能性もある。

MD レベルの方が、最適化の後だし、基本的には GCC のインラインアセンブラのような感じに記述できるので、比較的容易な気がする。

GCC の SSP (Stack Smashing Protector) は、セキュリティ用のコードを MD ファイルで仕込んでいるらしい。

ちょっと gcc-4.3.2/gcc/config/i386/i386.md を見てみる。
; SSP patterns
(UNSPEC_SP_SET 100)
(UNSPEC_SP_TEST 101)
(UNSPEC_SP_TLS_SET 102)
(UNSPEC_SP_TLS_TEST 103)
...

(define_expand "stack_protect_set"
[(match_operand 0 "memory_operand" "")
(match_operand 1 "memory_operand" "")]
""
{
#ifdef TARGET_THREAD_SSP_OFFSET
if (TARGET_64BIT)
emit_insn (gen_stack_tls_protect_set_di (operands[0],
GEN_INT (TARGET_THREAD_SSP_OFFSET)));
else
emit_insn (gen_stack_tls_protect_set_si (operands[0],
GEN_INT (TARGET_THREAD_SSP_OFFSET)));
#else
if (TARGET_64BIT)
emit_insn (gen_stack_protect_set_di (operands[0], operands[1]));
else
emit_insn (gen_stack_protect_set_si (operands[0], operands[1]));
#endif
DONE;
})

(define_insn "stack_protect_set_si"
[(set (match_operand:SI 0 "memory_operand" "=m")
(unspec:SI [(match_operand:SI 1 "memory_operand" "m")] UNSPEC_SP_SET))
(set (match_scratch:SI 2 "=&r") (const_int 0))
(clobber (reg:CC FLAGS_REG))]
""
"mov{l}\t{%1, %2|%2, %1}\;mov{l}\t{%2, %0|%0, %2}\;xor{l}\t%2, %2"
[(set_attr "type" "multi")])

(define_insn "stack_protect_set_di"
[(set (match_operand:DI 0 "memory_operand" "=m")
(unspec:DI [(match_operand:DI 1 "memory_operand" "m")] UNSPEC_SP_SET))
(set (match_scratch:DI 2 "=&r") (const_int 0))
(clobber (reg:CC FLAGS_REG))]
"TARGET_64BIT"
"mov{q}\t{%1, %2|%2, %1}\;mov{q}\t{%2, %0|%0, %2}\;xor{l}\t%k2, %k2"
[(set_attr "type" "multi")])

(define_insn "stack_tls_protect_set_si"
[(set (match_operand:SI 0 "memory_operand" "=m")
(unspec:SI [(match_operand:SI 1 "const_int_operand" "i")] UNSPEC_SP_TLS_SET))
(set (match_scratch:SI 2 "=&r") (const_int 0))
(clobber (reg:CC FLAGS_REG))]
""
"mov{l}\t{%%gs:%P1, %2|%2, DWORD PTR gs:%P1}\;mov{l}\t{%2, %0|%0, %2}\;xor{l}\t%2, %2"
[(set_attr "type" "multi")])

(define_insn "stack_tls_protect_set_di"
[(set (match_operand:DI 0 "memory_operand" "=m")
(unspec:DI [(match_operand:DI 1 "const_int_operand" "i")] UNSPEC_SP_TLS_SET))
(set (match_scratch:DI 2 "=&r") (const_int 0))
(clobber (reg:CC FLAGS_REG))]
"TARGET_64BIT"
{
/* The kernel uses a different segment register for performance reasons; a
system call would not have to trash the userspace segment register,
which would be expensive */
if (ix86_cmodel != CM_KERNEL)
return "mov{q}\t{%%fs:%P1, %2|%2, QWORD PTR fs:%P1}\;mov{q}\t{%2, %0|%0, %2}\;xor{l}\t%k2, %k2";
else
return "mov{q}\t{%%gs:%P1, %2|%2, QWORD PTR gs:%P1}\;mov{q}\t{%2, %0|%0, %2}\;xor{l}\t%k2, %k2";
}
[(set_attr "type" "multi")])

(define_expand "stack_protect_test"
[(match_operand 0 "memory_operand" "")
(match_operand 1 "memory_operand" "")
(match_operand 2 "" "")]
""
{
rtx flags = gen_rtx_REG (CCZmode, FLAGS_REG);
ix86_compare_op0 = operands[0];
ix86_compare_op1 = operands[1];
ix86_compare_emitted = flags;

#ifdef TARGET_THREAD_SSP_OFFSET
if (TARGET_64BIT)
emit_insn (gen_stack_tls_protect_test_di (flags, operands[0],
GEN_INT (TARGET_THREAD_SSP_OFFSET)));
else
emit_insn (gen_stack_tls_protect_test_si (flags, operands[0],
GEN_INT (TARGET_THREAD_SSP_OFFSET)));
#else
if (TARGET_64BIT)
emit_insn (gen_stack_protect_test_di (flags, operands[0], operands[1]));
else
emit_insn (gen_stack_protect_test_si (flags, operands[0], operands[1]));
#endif
emit_jump_insn (gen_beq (operands[2]));
DONE;
})

(define_insn "stack_protect_test_si"
[(set (match_operand:CCZ 0 "flags_reg_operand" "")
(unspec:CCZ [(match_operand:SI 1 "memory_operand" "m")
(match_operand:SI 2 "memory_operand" "m")]
UNSPEC_SP_TEST))
(clobber (match_scratch:SI 3 "=&r"))]
""
"mov{l}\t{%1, %3|%3, %1}\;xor{l}\t{%2, %3|%3, %2}"
[(set_attr "type" "multi")])

(define_insn "stack_protect_test_di"
[(set (match_operand:CCZ 0 "flags_reg_operand" "")
(unspec:CCZ [(match_operand:DI 1 "memory_operand" "m")
(match_operand:DI 2 "memory_operand" "m")]
UNSPEC_SP_TEST))
(clobber (match_scratch:DI 3 "=&r"))]
"TARGET_64BIT"
"mov{q}\t{%1, %3|%3, %1}\;xor{q}\t{%2, %3|%3, %2}"
[(set_attr "type" "multi")])

(define_insn "stack_tls_protect_test_si"
[(set (match_operand:CCZ 0 "flags_reg_operand" "")
(unspec:CCZ [(match_operand:SI 1 "memory_operand" "m")
(match_operand:SI 2 "const_int_operand" "i")]
UNSPEC_SP_TLS_TEST))
(clobber (match_scratch:SI 3 "=r"))]
""
"mov{l}\t{%1, %3|%3, %1}\;xor{l}\t{%%gs:%P2, %3|%3, DWORD PTR gs:%P2}"
[(set_attr "type" "multi")])

(define_insn "stack_tls_protect_test_di"
[(set (match_operand:CCZ 0 "flags_reg_operand" "")
(unspec:CCZ [(match_operand:DI 1 "memory_operand" "m")
(match_operand:DI 2 "const_int_operand" "i")]
UNSPEC_SP_TLS_TEST))
(clobber (match_scratch:DI 3 "=r"))]
"TARGET_64BIT"
{
/* The kernel uses a different segment register for performance reasons; a
system call would not have to trash the userspace segment register,
which would be expensive */
if (ix86_cmodel != CM_KERNEL)
return "mov{q}\t{%1, %3|%3, %1}\;xor{q}\t{%%fs:%P2, %3|%3, QWORD PTR fs:%P2}";
else
return "mov{q}\t{%1, %3|%3, %1}\;xor{q}\t{%%gs:%P2, %3|%3, QWORD PTR gs:%P2}";
}
[(set_attr "type" "multi")])