ポインターの演算はポインターだったらポインターのサイズ分加算などする必要があります。
```c
`gutter:true;
int *p;
int list[3];
list[2] = 12;
p = list;
return *(p + 2);
```
上記の場合、pは2を足すのではなく、8足す必要があります。
(pはintのポインターで+2はアドレスで8bit進める必要がある為)
## 初めのアプローチ
ポインターの加算なので、ポインターだったらグローバルな変数にオフセットを入れておいて
数値が出た時に掛け算する方法です。
初めはこの方法でうまくいっていたのですが、パースしながら行うのは限界がありました。
## 現在のアプローチ
コード生成の処理の前に、トークナイズした情報をいったん検査して
ポインターの加減算の時に数値の値を調整する様にしました。
```c
`gutter:true;
#include "9cc.h"
int current_pointer_offset_ = 1;
int offset_of_variable(Variable *val_info) {
if (val_info->type->ty == INT) {
// INTの変数
return 1;
}
if (val_info->type->ty == PTR && val_info->type->ptrof->ty == INT) {
// INTへのポインター
return SIZE_OF_INT;
}
if (val_info->type->ty == PTR && val_info->type->ptrof->ty == PTR) {
// ポインターへのポインター
return SIZE_OF_ADDRESS;
}
if (val_info->type->ty == ARRAY && val_info->type->ptrof->ty == INT) {
// INT 配列へのポインター
return SIZE_OF_INT;
}
// ここに来たら対応不足
error("不明な変数型です.", "");
return 1;
}
void walk(Node *node) {
if (node == NULL) return;
switch (node->ty) {
case ND_NUM:
node->val = node->val * current_pointer_offset_;
return;
case '+':
case '-':
// 左のNodeの値が変数の場合、+,-の計算の際にオフセットを設定する
// その後数値が出てきた時には、値にオフセットを掛ける
if (node->lhs->ty == ND_IDENT) {
int before_offset = current_pointer_offset_;
Variable *val_info = map_get(variables, node->lhs->name);
current_pointer_offset_ = offset_of_variable(val_info);
walk(node->rhs);
current_pointer_offset_ = before_offset;
return;
}
}
walk(node->lhs);
walk(node->rhs);
}
void sema() {
for (int i = 0; i < functions->len; i++) {
Function *function = (Function *)functions->data[i];
// ローカル変数のMapをグローバル領域にコピー (値の定義はcodegen.cにある)
variables = function->variables;
for (int i = 0; i < function->code->len; i++) {
// オフセットを初期化
current_pointer_offset_ = 1;
Node *node = function->code->data[i];
walk(node);
}
}
}
```
今の所これでテストはパスしています。
## 参考URL
* [低レイヤを知りたい人のためのCコンパイラ作成入門](https://www.sigbus.info/compilerbook)
* [開発状況はここ](https://github.com/k28/9cc)
0 件のコメント :
コメントを投稿