[C] コンパイラ作成 ポインターの演算を実装する際にハマった事

ポインターの演算はポインターだったらポインターのサイズ分加算などする必要があります。 ```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 件のコメント :

コメントを投稿