chibicc compiler を6800向けに改造する (3) ++ と —

BASICMASTER, 昔のパソコン

ポストデクリメントとプリインクリメントという概念を初めて知ったのは、古のCPUであるSC/MP(スキャンプ) IIのデータシートであった。

当時のパソコン雑誌には、なぜか、SC/MP IIの記事が多数掲載されていた。しかしSC/MP IIに関する書籍は無く、情報を得るためにナショセミに資料請求してデータシートをもらった。

そこに記載されていたのが、Auto indexed アドレッシングである。インデックス修飾アドレッシングなのだが、オフセットが正であれば、アクセス後にポインタレジスタの中身が増える。

負であれば減らしたあとに、そこをアクセスする。 *–p と *p++ の動作をうまく実現している。


今だから英文を見て理解できるが、当時はさっぱりわからなかった。中学の英語の先生に聞きにいったら、やっぱりわからかなかった。知らない概念を知らない言語で読むのは無茶でした。

その後、6809を使うようになって、初めてSC/MP IIのアレはコレだったのか!とわかった。

その他にもSC/MP IIには、ミニコンを知っていたら自然な(でも知らないと意味不明な)機能がたくさんあって、面白いCPUである。


chibiccにおける ++ と — の実装

pre increment/decrementは簡単である。レジスタを1つ増減して、更新したレジスタの値を使う。ポインタアクセスのときは、更新後のポインタでデータを参照する。

これは、parse.c の static Node *unary(Token **rest, Token *tok) で実装されている。++i であれば、 i=i+1 しているだけだ。


  // Read ++i as i+=1
  if (equal(tok, "++"))
    return to_assign(new_add(unary(rest, tok->next), new_num(1, tok), tok));

  // Read --i as i-=1
  if (equal(tok, "--"))
    return to_assign(new_sub(unary(rest, tok->next), new_num(1, tok), tok));

post increment/decrementは難しい。増減する前の値を覚えておく必要がある。

static Node *postfix(Token **rest, Token *tok) の下請けの関数である new_inc_dec に書かれている。

コメントにあるように増減した値を格納後に、元に戻している。冗長だけど簡潔である。


// Convert A++ to `(typeof A)((A += 1) - 1)`
static Node *new_inc_dec(Node *node, Token *tok, int addend) {
  add_type(node);
  return new_cast(new_add(to_assign(new_add(node, new_num(addend, tok), tok)),
                          new_num(-addend, tok), tok),
                  node->ty);
}


to_assignとnew_lvar

両者で使われている to_assign 関数は、 A op= B 形式を処理する関数である。これは、コメントにもあるように、tmp = &A, *tmp = *tmp op B の形に変換している。

このときに一時変数を使って、アドレスを記憶している。ということは、 ++/– や op= を使うと一時変数が1つずつ増えていく。

一時変数は関数開始時(Prologue)にスタックに確保されるので、大量に使うとそれだけスタックが深くなる。

MC6800のインデックス修飾の上限(0-255)を考えると、ここは何とかしたいところである。


// Convert `A op= B` to ``tmp = &A, *tmp = *tmp op B`. Obj *var = new_lvar("", pointer_to(binary->lhs->ty)); Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok), new_unary(ND_ADDR, binary->lhs, tok), tok); Node *expr2 = new_binary(ND_ASSIGN, new_unary(ND_DEREF, new_var_node(var, tok), tok), new_binary(binary->kind, new_unary(ND_DEREF, new_var_node(var, tok), tok), binary->rhs, tok), tok); return new_binary(ND_COMMA, expr1, expr2, tok);


リンク