sdcc を6800向けに改造する (1)

BASICMASTERCコンパイラ

ひところに比べるとMC6800向けのCコンパイラも だいぶ増えてきた。2つは EtchedPixels氏によるもので、2つは私が移植したものだ。

ack-6800は C言語以外(BASICやPascalなど)も動く。まだPascalの集合型がおかしい(私の実装ミス)。いい案がなくて放置中。

以下はEtchedPixels氏によるもの。


C言語の構成

これらの実装にはそれぞれ特徴がある。

CC6303

6502用のCC65の移植版。ソースコードを解析しながら、オブジェクトを生成する(ASTを作らない)。

なので、関数引数は左から積む。元が6502用なので、スタックの使い方が独特。


Fuzix-Compiler-Kit

Fuzix用に開発されたCコンパイラ。多数のcpuをサポートしているのと、Fuzix上でのセルフコンパイルを目標としている。

セルフコンパイルのために、(速度をある程度犠牲にしても)小さなオブジェクトを出すようにしている。

コンパイラも最適化を追求するよりも、コンパクトになることを目標としている(と思われる)。

ASTを作ったあと、byte操作で済む部分にマークを付けて、その後オブジェクトを出す。


chibicc-6800

x64用の小さなコンパイラ実装であるchibiccを、私が6800向けに改造したもの。

ASTを作ったあと、最適化作業で木を修正し、その後オブジェクトを出す。

高速なオブジェクトを出すことを優先しているが、生成されたオブジェクトサイズはFuzix-CCとあまり変わらない。


ack-6800

なつかしの Amsterdam Compiler Kit のコンパイラを6800向けに私が移植したもの。

コンパイラは中間コード(EM)を作るので、EMからMC6800向けの変換テーブルを書けば 移植終わりである。もちろんライブラリは必要。

コードの質はそこそこであるが、高速にコンパイラ開発できて素晴らしい。

フロントエンドはいじらなくていいので、EMからの変換さえ書けば各種言語が動く( C, pascal, basic, fortran, occamなど)。

EMがlittle endian前提なので、罠が多数ある。

そろそろIRを使ったコンパイラに手を出したい

chibicc-6800も落ち着いてきたので、別のCを探してみた。sdccがあった。これはASTからIR(中間表現)を作って、そこからコードを生成する。

IRの時点で共通部分式の括りだしや、変数の生存管理を行うのでかなり良いコードが出る。

だけど、その分コードサイズが大きくて、手をつけたものの 作業は遅々として進まなかった。

AIに助けてもらう

AIは大きなコードを読んで何をしているかの把握に使うと非常に便利である。コードも書いてくれるが、品質はやや怪しい( Claude Opus 5 )。

Claude のWeb UIは、クラウド上にコンテナを作ってそこで作業してくれる。絶対に自分の環境を壊さないという安心感がある。

これは ChatGPT workでもできるけど、慣れている Claudeでやってみた。


以下、現在作業中の guthub repository。

Ackerman関数をコンパイルすると、chibicc-6800よりも速いコードを出す。素晴らしい。

デフォルトでは再帰呼び出しできないので、関数側に __reentrant を付けてある。コンパイルオプションに –stack-auto を付けても良い。


/*
 *	ackerman
 */

unsigned char
ack_c(unsigned char x, unsigned char y)
__reentrant
{
	if(x==0){
		return y+1;
	}else if(y==0){
		return ack_c(x-1,1);
	}else{
		return ack_c(x-1,ack_c(x,y-1));
	}
}

引数が2つともレジスタ渡しになっている。他に使う変数がないので、スタックを一切使っていない。

しかも末尾再帰を最適化している(引数をintにすると、末尾最適化はしなくなるけど、それでも速い)。


;------------------------------------------------------------
;Allocation info for local variables in function 'ack_c'
;------------------------------------------------------------
;y             Allocated to registers a 
;x             Allocated to registers b 
;------------------------------------------------------------
;9005-ack.c:6: ack_c(unsigned char x, unsigned char y)
;	-----------------------------------------
;	 function ack_c
;	-----------------------------------------
;	Register assignment is optimal.
;	Stack space usage: 0 bytes.
_ack_c:
;9005-ack.c:9: if(x==0){
	tstb
	bne	00105$
;9005-ack.c:10: return y+1;
	inca
	tab
	rts
00105$:
;9005-ack.c:11: }else if(y==0){
	tsta
	bne	00102$
;9005-ack.c:12: return ack_c(x-1,1);
	decb
	ldaa	#0x01
	bra	_ack_c
00102$:
;9005-ack.c:14: return ack_c(x-1,ack_c(x,y-1));
	deca
	pshb
	jsr	_ack_c
	tba
	pulb
	decb
;9005-ack.c:16: }
	bra	_ack_c



比較のために chibicc-6800-v1/ztest/9005-ack.cを実行してみた。

sdccにはucsimというエミュレーターがあるので、それを使う。実行サイクルは 456,142,659 であった。

chibicc-6800 + emu6800 だと 529,788,849(-O2)である。

sdcc-6800は まだ無駄なコードを出している。それで この速度は凄い。


Simulation started, PC=0x000100

Stop at 0x00017b: (110) Program stopped itself
F 0x00017b
CPU state= OK PC= 0x00017b frequency= 1000000 HZ
Operation since last reset= 250943514 vclks
Inst= 108170009 Fetch= 170428550 Read= 47562079 Write= 32952885
Total time since last reset= 456.142658234719704 sec (456142659 clks)
Time in isr = 0.000000000000000 sec (0 clks) 0.00%
Time in idle= 0.000000000000000 sec (0 clks) 0.00%
Most value of stack pointer= 0x000000
Simulation: stopped
Runtime: 24.221586 sec
0


ライブラリはまだちゃんと作っていないけど、そこそこ速い

乗除算などは まだCで書かれた共通関数を使っている。アセンブラに比べると遅い。でも 動く。凄い。

整数版の mandelbrot が 327,909,745 cyc。chibicc-6800は私の書いた高速乗除算が使えるので 83,727,146 cyc。


Simulation started, PC=0x000100
111111111111111111222222222333334568BC67443322222211111111111000000000000000000
11111111111111111122222222233344598C  77943333222221111111110000000000000000000
111111111111112222222233324444556       955433332211111111100000000000000000000
111111111211112222222333455665778       976554444222211111100000000000000000000
11111112222222233333334457 AB9              787B5432111111111100000000000000000
11112222222222333333444667                       532222111111111000000000000000
11111222333444444444555A                       96443322211111110000000000000000
12222223345D6657 6555679                        AA43322211111110000000000000000
222233334569  8C  E8789                          B43322111111110000000000000000
223333345578D        E                            43322211111111111100000000000
3344444789A                                      543322111111111111100000000000
5555658A                                       C6433222222111111111100000000000
                                              975443322221111111111100000000000
5555658A                                       C6433222222111111111100000000000
3344444789A                                      543322111111111111100000000000
223333345578D        E                            43322211111111111100000000000
222233334569  8C  E8789                          B43322111111110000000000000000
12222223345D6657 6555679                        AA43322211111110000000000000000
11111222333444444444555A                       96443322211111110000000000000000
11112222222222333333444667                       532222111111111000000000000000
11111112222222233333334457 AB9              787B5432111111111100000000000000000
111111111211112222222333455665778       976554444222211111100000000000000000000
111111111111112222222233324444556       955433332211111111100000000000000000000
11111111111111111122222222233344598C  77943333222221111111110000000000000000000
111111111111111111222222222333334568BC67443322222211111111111000000000000000000

Stop at 0x00017b: (110) Program stopped itself
F 0x00017b
CPU state= OK PC= 0x00017b frequency= 1000000 HZ
Operation since last reset= 272282183 vclks
Inst= 88907649 Fetch= 200602573 Read= 49780095 Write= 21899515
Total time since last reset= 327.909744572030320 sec (327909745 clks)
Time in isr = 0.000000000000000 sec (0 clks) 0.00%
Time in idle= 0.000000000000000 sec (0 clks) 0.00%
Most value of stack pointer= 0x000000
Simulation: stopped
Runtime: 19.570739 sec
0

まだバグが残っているし、ライブラリがないけど、簡単なテストプログラムは動く。興味ある方はお試しあれ。

以下はテスト用のスクリプト。


#!/bin/sh

PATH=/usr/local/bin:$PATH
OPT="-mmc6800"

OPT_O=""
OPT_g=""

while true; do
  case "$1" in
  --opt-code-speed|--opt-code-size)
    OPT_O=$1
    shift
    ;;
  -g)
    OPT_g="--i-code-in-asm"
    shift
    ;;
--stack-auto)
    OPT="$OPT --stack-auto"
    shift
    ;;
  --)
    shift
    break
    ;;
  -*)
    echo "Unknown option: $1" >&2
    exit 1
    ;;
  *)
    break
    ;;
  esac
done

CCRUN="sdcc $OPT $OPT_O $OPT_g "

s=$(basename $1 .c)
shift
echo  "compiling:" $s
echo $CCRUN $s.c $OPT_lm
$CCRUN $s.c $OPT_lm
err=$?
if [ $err -ne 0 ] ; then
	echo $err
	exit $err;
fi
ls -Ggl $s.s19
printf "run\nstate\nquit\n" | ucsim_m6800 -q -I 'if=rom[0xfefe]' $s.s19
ret=$?
echo $?
exit $ret


リンク

コメント

タイトルとURLをコピーしました