diff --git a/.github/workflows/deployment.yaml b/.github/workflows/deployment.yaml new file mode 100644 index 0000000..cd86532 --- /dev/null +++ b/.github/workflows/deployment.yaml @@ -0,0 +1,52 @@ +name: Update submodule in mikiken/mikiken.net + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + update_submodules: + name: Update submodules + runs-on: ubuntu-20.04 + defaults: + run: + working-directory: /home/runner/work/compiler_log/mikiken.net + + steps: + - name: Clone repository + run: | + git clone https://github.com/mikiken/mikiken.net.git + cd mikiken.net + git init + pwd + ls -la + working-directory: /home/runner/work/compiler_log/ + + - name: Configure git + run: | + pwd + git config --local user.name "mikiken" + git config --local user.email "mikiken.dev@gmail.com" + + - name: Change revision of submodule + run: | + pwd + cd compiler_log + pwd + git fetch + git reset --hard origin/main + git status + + - name: Add and commit files + run: | + pwd + git status + git add . + git commit -m "Update submodule via github-actions" + + - name: Push changes + run : git push origin main \ No newline at end of file diff --git a/index.html b/index.html index e72fc34..c2a25a4 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,943 @@ -
typedef struct Function Function;
+
+ メモ帳 - 作業ログ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ メモ帳
+ seccamp2022 / 作業ログ
+
+
+
+
+
+
+
+
+
+ mikiken 08/02/2022 11:59 AM
+ 関数定義 (edited)
+
+
+
+
+
+
+ 12:01
+
+
+ typedef struct Function Function;
struct Function {
Function *next;
@@ -14,51 +946,201 @@
Lvar params_head; // 引数リストの先頭
Node *body; // statement
Lvar *locals; // ローカル変数のリスト
-};
(edited)
-mikiken 08/02/2022 2:52 PM1.整数を返すだけの関数が定義できるように
-mikiken 08/06/2022 1:42 AMoffset計算するところでセグフォしてる
-mikiken 08/10/2022 9:22 AMstruct Expr typecheck_expression(struct AnalyzerState *ptr_ps,
- const struct UntypedExpr *ref_uexpr)
-mikiken 08/10/2022 10:00 AMhsjoihs「なので、それに一つずつ型情報を付与していく必要があります」
-hsjoihs「やり方は二通りあって、一つは、いまある Node を書き換えて、型情報を書き込んでいく方法」
-hsjoihs「もう一つは、今ある AST を見て、再帰的に『その AST の、型がついたバージョン』を作っていく方法」
-mikiken 08/10/2022 10:43 AM
-mikiken 08/10/2022 8:25 PM問題点
-そもそも関数名とか型の情報とか渡ってなくね?
-引数としてlistを受け取ってるけど結局使ってなくね?
-mikiken 08/11/2022 11:32 PMTODO(対応済み)
-変数のサイズによってレジスタの名前が変わるので、それに対応できるようにcodegen.c
を書き換える (edited)
-
-mikiken 08/22/2022 11:06 PMStep 21-2 配列からポインタへの暗黙の型変換を実装する
-・右辺に配列型変数が現れた場合 a
→ &a
のようにキャストする
-・明示的に&a
と書いたときは、上記のキャストを行わないようにする
-・*a = 1;
みたいな式が書けるように(次のステップでa[i]
を*(a+i)
に読み替えるための準備)
-・sizeof(a)
は(1つ分のサイズ) * (要素数)
を返す (edited)
-mikiken 08/23/2022 1:21 AM新たに処理を書き足す必要がある(と思われる)演算子
-・+
-
(済)
-・==
!=
<
<=
>
>=
→ そもそもポインタに対しても対応していないので後回し
-・=
(済)
-・*
(dereference) オペランドとして直接配列が来た場合に対応する必要あり (済)
-・sizeof
(済)
-※&
について 明示的に&
を書いているからキャストする必要ないはず) (edited)
-mikiken 08/23/2022 1:50 AMポインタの加算・減算のうち
-・lhs
が数字/rhs
がポインタのものは実装していないはず
-mikiken 08/23/2022 6:51 PM代入式の左辺(lhs
)に直接配列が来ることはありえない18:52#include <stdio.h>
+};
(edited)
+
+
+
+
+
+
+
+
+
+ mikiken 08/02/2022 2:52 PM
+ 1.整数を返すだけの関数が定義できるように
+
+
+
+
+
+
+
+
+
+ mikiken 08/06/2022 1:42 AM
+ offset計算するところでセグフォしてる
+
+
+
+
+
+
+
+
+
+ mikiken 08/10/2022 9:22 AM
+ struct Expr typecheck_expression(struct AnalyzerState *ptr_ps,
+ const struct UntypedExpr *ref_uexpr)
+
+
+
+
+
+
+
+
+
+ mikiken 08/10/2022 10:00 AM
+ hsjoihs「なので、それに一つずつ型情報を付与していく必要があります」
+ hsjoihs「やり方は二通りあって、一つは、いまある Node を書き換えて、型情報を書き込んでいく方法」
+ hsjoihs「もう一つは、今ある AST を見て、再帰的に『その AST の、型がついたバージョン』を作っていく方法」
+
+
+
+
+
+
+
+
+
+ mikiken 08/10/2022 10:43 AM
+
+
+
+
+
+
+
+
+
+
+ mikiken 08/10/2022 8:25 PM
+ 問題点
+ そもそも関数名とか型の情報とか渡ってなくね?
+ 引数としてlistを受け取ってるけど結局使ってなくね?
+
+
+
+
+
+
+
+
+
+ mikiken 08/11/2022 11:32 PM
+ TODO(対応済み)
+ 変数のサイズによってレジスタの名前が変わるので、それに対応できるようにcodegen.c
を書き換える (edited)
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 08/22/2022 11:06 PM
+ Step 21-2 配列からポインタへの暗黙の型変換を実装する
+ ・右辺に配列型変数が現れた場合 a
→ &a
のようにキャストする
+ ・明示的に&a
と書いたときは、上記のキャストを行わないようにする
+ ・*a = 1;
みたいな式が書けるように(次のステップでa[i]
を*(a+i)
に読み替えるための準備)
+ ・sizeof(a)
は(1つ分のサイズ) * (要素数)
を返す (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 08/23/2022 1:21 AM
+ 新たに処理を書き足す必要がある(と思われる)演算子
+ ・+
-
(済)
+ ・==
!=
<
<=
>
>=
→ そもそもポインタに対しても対応していないので後回し
+ ・=
(済)
+ ・*
(dereference) オペランドとして直接配列が来た場合に対応する必要あり (済)
+ ・sizeof
(済)
+ ※&
について 明示的に&
を書いているからキャストする必要ないはず) (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 08/23/2022 1:50 AM
+ ポインタの加算・減算のうち
+ ・lhs
が数字/rhs
がポインタのものは実装していないはず
+
+
+
+
+
+
+
+
+
+ mikiken 08/23/2022 6:51 PM
+ 代入式の左辺(lhs
)に直接配列が来ることはありえない
+
+
+
+
+
+
+ 18:52
+
+
+ #include <stdio.h>
int main(char *argv[], int argc) {
int a[2];
a = 80;
printf("%d", a[0]);
return 0;
}
-./Main.c: In function ‘main’:
+ ./Main.c: In function ‘main’:
./Main.c:4:5: error: assignment to expression with array type
4 | a = 80;
- | ^
(edited)
-mikiken 08/31/2022 3:58 PMTODO
-・int
を4byteに
-・(任意の型)*
を8byteに
-・関数呼び出しの際、rspを16byte境界にalignする
-mikiken 08/31/2022 5:31 PMvoid alloc4(int **p, int a, int b, int c, int d) {
+ | ^
(edited)
+
+
+
+
+
+
+
+
+
+ mikiken 08/31/2022 3:58 PM
+ TODO
+ ・int
を4byteに
+ ・(任意の型)*
を8byteに
+ ・関数呼び出しの際、rspを16byte境界にalignする
+
+
+
+
+
+
+
+
+
+ mikiken 08/31/2022 5:31 PM
+ void alloc4(int **p, int a, int b, int c, int d) {
*p = malloc(sizeof(int) * 5);
(*p)[0] = a;
(*p)[1] = b;
@@ -66,25 +1148,180 @@
(*p)[3] = d;
(*p)[4] = 100;
}
-int alloc4(); int main(){int *p; alloc4(&p, 1, 2, 4, 8); return *(p+2);} => 100
-これはgccがint
を4byteとして扱っている一方、9ccでは8byteとして扱っているため (edited)
-mikiken 08/31/2022 5:40 PMint main() {int a; a = 4; int b[2]; int *p; p = b; *p = 1; *(p+1) = 2; return a + *p + *(p+1);} => 7 expected, but got 5
-となるが、
-int main() {int a; a = 4; int b[2]; int *p; p = b; *p = 1; *(p-1) = 2; return a + *p + *(p-1);} => 7
-とすると上手くいく
-mikiken 09/10/2022 8:33 AMPUSH copies the specified register, memory location, or immediate value to the top of stack. This instruction decrements the stack pointer by 2, 4, or 8, depending on the operand size, and then copies the operand into the memory location pointed to by SS:rSP. (edited)08:34POP copies a word, doubleword, or quadword from the memory location pointed to by the SS:rSP registers (the top of stack) to a specified register or memory location. Then, the rSP register is incremented by 2, 4, or 8. After the POP operation, rSP points to the new top of stack.
-mikiken 09/25/2022 9:11 PMFor addition, either both operands shall have arithmetic type, or one operand shall be a pointer to a complete object type and the other shall have integer type. (Incrementing is equivalent to adding 1.)21:12For subtraction, one of the following shall hold:
-・both operands have arithmetic type;
-・both operands are pointers to qualified or unqualified versions of compatible complete object types; or
-・the left operand is a pointer to a complete object type and the right operand has integer type.
-(Decrementing is equivalent to subtracting 1.)
-ってなってるけど、スタック操作(pop
とかpush
)を行うときは8byteレジスタしか引数に取れない?(あまりよく分かってない) (edited)
-mikiken 09/28/2022 5:00 PMもしかしたらローカル変数のメモリ上での配置の仕方がgccとかと違うかも(おそらく逆順) (edited)17:00そもそもこういうテストケース
-int main() {int a; int b; a = 2; b = 8; int *p; int *q; p = &a; q = &b; return q - p;}
-に対して1が帰ってくる保証はないのでは?
-(int a
と int b
が連続して確保される保証はないため) (edited)
-mikiken 09/28/2022 5:17 PM2つのポインタを減算する場合において、その両方のポインタが同じ配列オブジェクトの要素か、その配列オブジェクトの最後の要素を一つ越えたところを指していない場合。(6.5.6)
-mikiken 10/07/2022 5:39 PMStep 25 文字列リテラルを実装する17:40tokenizerはこういうコードを追加すればいいはず17:41 // 文字列リテラルの場合
+ int alloc4(); int main(){int *p; alloc4(&p, 1, 2, 4, 8); return *(p+2);} => 100
+ これはgccがint
を4byteとして扱っている一方、9ccでは8byteとして扱っているため
(edited)
+
+
+
+
+
+
+
+
+
+ mikiken 08/31/2022 5:40 PM
+ int main() {int a; a = 4; int b[2]; int *p; p = b; *p = 1; *(p+1) = 2; return a + *p + *(p+1);} => 7 expected, but got 5
+ となるが、
+ int main() {int a; a = 4; int b[2]; int *p; p = b; *p = 1; *(p-1) = 2; return a + *p + *(p-1);} => 7
+ とすると上手くいく
+
+
+
+
+
+
+
+
+
+ mikiken 09/10/2022 8:33 AM
+
+
+
+ PUSH copies the specified register, memory location, or immediate value to the top of stack. This instruction decrements the stack pointer by 2, 4, or 8, depending on the operand size, and then copies the operand into the memory location pointed to by SS:rSP.
+
+ (edited)
+
+
+
+
+
+
+ 08:34
+
+
+
+
+
+ POP copies a word, doubleword, or quadword from the memory location pointed to by the SS:rSP registers (the top of stack) to a specified register or memory location. Then, the rSP register is incremented by 2, 4, or 8. After the POP operation, rSP points to the new top of stack.
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 09/25/2022 9:11 PM
+
+
+
+ For addition, either both operands shall have arithmetic type, or one operand shall be a pointer to a complete object type and the other shall have integer type. (Incrementing is equivalent to adding 1.)
+
+
+
+
+
+
+
+
+ 21:12
+
+
+
+
+
+ For subtraction, one of the following shall hold:
+ ・both operands have arithmetic type;
+ ・both operands are pointers to qualified or unqualified versions of compatible complete object types; or
+ ・the left operand is a pointer to a complete object type and the right operand has integer type.
+ (Decrementing is equivalent to subtracting 1.)
+
+ ってなってるけど、スタック操作(pop
とかpush
)を行うときは8byteレジスタしか引数に取れない?(あまりよく分かってない)
+ (edited)
+
+
+
+
+
+
+ 21:12
+
+
+
+
+
+
+
+
+
+
+ mikiken 09/28/2022 5:00 PM
+ もしかしたらローカル変数のメモリ上での配置の仕方がgccとかと違うかも(おそらく逆順) (edited)
+
+
+
+
+
+
+ 17:00
+
+
+ そもそもこういうテストケース
+ int main() {int a; int b; a = 2; b = 8; int *p; int *q; p = &a; q = &b; return q - p;}
+ に対して1が帰ってくる保証はないのでは?
+ (int a
と int b
が連続して確保される保証はないため) (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 09/28/2022 5:17 PM
+
+
+
+ 2つのポインタを減算する場合において、その両方のポインタが同じ配列オブジェクトの要素か、その配列オブジェクトの最後の要素を一つ越えたところを指していない場合。(6.5.6)
+
+
+
+
+
+
+
+
+ 17:17
+
+
+
+
+
+
+
+
+
+
+ mikiken 10/07/2022 5:39 PM
+ Step 25 文字列リテラルを実装する
+
+
+
+
+
+
+ 17:40
+
+
+ tokenizerはこういうコードを追加すればいいはず
+
+
+
+
+
+
+ 17:41
+
+
+ // 文字列リテラルの場合
if (startswith(p, "\"")) {
char *start = ++p; // ダブルクオートを読み飛ばす
while (*p != '\"')
@@ -92,9 +1329,29 @@
cur = new_token(TK_STR, cur, start, p - 1);
p++; // ダブルクオートを読み飛ばす
continue;
- }
17:44launch.json
のargs
にテストケースを渡すとき、ダブルクオーテーションを\\\"
とエスケープしないといけない
-mikiken 10/09/2022 11:07 PM文字列リテラルを変更しようとするとセグフォするのを実験
-#include <stdio.h>
+ }
+
+
+
+
+
+
+ 17:44
+
+
+ launch.json
のargs
にテストケースを渡すとき、ダブルクオーテーションを\\\"
とエスケープしないといけない
+
+
+
+
+
+
+
+
+
+ mikiken 10/09/2022 11:07 PM
+ 文字列リテラルを変更しようとするとセグフォするのを実験
+ #include <stdio.h>
char *str = "Hello, implementation-defined behavior!";
int main(int argc, int **argv) {
@@ -102,47 +1359,316 @@
str[5] = '!'; // ここでセグフォする(けどコンパイル時にwarningさえ出ない)
printf("%s\n", str);
return 0;
-}
23:09文字列リテラルがchar
の配列として扱われるのは、例外的な場合のみ
-char str[] = "Good bye"; // 初期化式だからcharの配列として書ける
-str[5] = 'B';
23:10なのでこの段階では気にする必要はなく、文字列リテラルはすべてグローバルに確保して、その領域へのアドレスを返すようにすればOKなはず
-mikiken 10/10/2022 2:25 AM既存のコンパイラの出力を観察してみる
-int main() { char *str = "abc"; return str[0]; }
-に対する出力02:26x86-64 clang 15.0.0
-https://godbolt.org/z/dYWx1zchqint main() { char *str = "abc"; return str[0]; }02:26x86-64 gcc 12.2
-https://godbolt.org/z/Y4c8r1Exrint main() { char *str = "abc"; return str[0]; }
-mikiken 10/11/2022 6:58 AMエスケープシーケンスを実装する
-Escape sequences are defined using themselves here. E.g. '\n' is implemented using '\n'. This tautological definition works because the compiler that compiles our compiler knows what '\n' actually is. In other words, we "inherit" the ASCII code of '\n' from the compiler that compiles our compiler, so we don't have to teach the actual code here. This fact has huge implications not only for the correctness of the compiler but also for the security of the generated code. For more info, read "Reflections on Trusting Trust" by Ken Thompson.
-https://github.com/rui314/chibicc/wiki/thompson1984.pdf (edited)06:59A small C compiler. Contribute to rui314/chibicc development by creating an account on GitHub.
-mikiken 10/12/2022 10:21 PM・最後の"を認識して、文字列リテラルの文字数分のcharの配列を確保する
-・一文字ずつ読んでいって、エスケープシーケンスじゃなかったらそのまま配列のi文字目に書き込む
-・エスケープシーケンスの場合はうまく扱えるように文字を置き換えて配列のi文字目に格納する (edited)
-mikiken 10/17/2022 6:05 PMStep27 行コメントとブロックコメントを実装する
-たぶんtokenize.c
に少し処理足せば一瞬でできるので、とりあえず後回しにして、Step28に進む(今のままだとテストが書きにくいので) (edited)
-mikiken 10/17/2022 6:23 PMStep28 テストをCで書き直す (edited)
-mikiken 10/17/2022 11:30 PMprintf
を使うにあたって
-・フォーマット指定子(%d
など)については、とりあえず引数さえちゃんと渡せば大丈夫なはず
-・可変長引数を取る関数を呼ぶので、call
命令の前で浮動小数点数引数の個数をal
に入れておく必要あり (edited)
-mikiken 10/18/2022 2:40 PMint printf();
-int exit();
-
-int assert(int expected, int actual, char *code) {
- if (expected == actual) {
- printf("%s => %d\n", code, actual);
- }
- else {
- printf("%s => %d expected, but got %d\n", code, expected, actual);
- exit(1);
- }
- return 0;
-}
+}
+
+
+
+
+
+
+ 23:09
+
+
+ 文字列リテラルがchar
の配列として扱われるのは、例外的な場合のみ
+ char str[] = "Good bye"; // 初期化式だからcharの配列として書ける
+str[5] = 'B';
+
+
+
+
+
+
+ 23:10
+
+
+ なのでこの段階では気にする必要はなく、文字列リテラルはすべてグローバルに確保して、その領域へのアドレスを返すようにすればOKなはず
+
+
+
+
+
+
+
+
+
+ mikiken 10/10/2022 2:25 AM
+ 既存のコンパイラの出力を観察してみる
+ int main() { char *str = "abc"; return str[0]; }
+ に対する出力
+
+
+
+
+
+
+ 02:26
+
+
+ x86-64 clang 15.0.0
+ https://godbolt.org/z/dYWx1zchq
+
+
+
+
+
+
+
+ int main() { char *str = "abc"; return str[0]; }
+
+
+
+
+
+
+
+
+
+
+
+
+ 02:26
+
+
+ x86-64 gcc 12.2
+ https://godbolt.org/z/Y4c8r1Exr
+
+
+
+
+
+
+
+ int main() { char *str = "abc"; return str[0]; }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 10/11/2022 6:58 AM
+ エスケープシーケンスを実装する
+
+
+ Escape sequences are defined using themselves here. E.g. '\n' is implemented using '\n'. This tautological definition works because the compiler that compiles our compiler knows what '\n' actually is. In other words, we "inherit" the ASCII code of '\n' from the compiler that compiles our compiler, so we don't have to teach the actual code here. This fact has huge implications not only for the correctness of the compiler but also for the security of the generated code. For more info, read "Reflections on Trusting Trust" by Ken Thompson.
+ https://github.com/rui314/chibicc/wiki/thompson1984.pdf
+
+
+ (edited)
+
+
+
+
+
+
+ 06:59
+
+
+
+
+
+
+
+
+
+
+ A small C compiler. Contribute to rui314/chibicc development by creating an account on GitHub.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 10/12/2022 10:21 PM
+ ・最後の"を認識して、文字列リテラルの文字数分のcharの配列を確保する
+ ・一文字ずつ読んでいって、エスケープシーケンスじゃなかったらそのまま配列のi文字目に書き込む
+ ・エスケープシーケンスの場合はうまく扱えるように文字を置き換えて配列のi文字目に格納する (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 10/17/2022 6:05 PM
+ Step27 行コメントとブロックコメントを実装する
+ たぶんtokenize.c
に少し処理足せば一瞬でできるので、とりあえず後回しにして、Step28に進む(今のままだとテストが書きにくいので) (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 10/17/2022 6:23 PM
+ Step28 テストをCで書き直す (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 10/17/2022 11:30 PM
+ printf
を使うにあたって
+ ・フォーマット指定子(%d
など)については、とりあえず引数さえちゃんと渡せば大丈夫なはず
+ ・可変長引数を取る関数を呼ぶので、call
命令の前で浮動小数点数引数の個数をal
に入れておく必要あり (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 10/18/2022 2:40 PM
+ デバッグ用
+ https://godbolt.org/z/6fb9K6c1a
+
+
+
+
+
+
+
+ int printf();
+ int exit();
-int main() {
- return 0;
-}
-mikiken 10/21/2022 7:12 PM実装するときに考えてたことのメモ
-何したらいいか分かりづらいので、とりあえず、test.c
1ファイルに全ての処理をベタ書きしてみる (edited)19:12まずは整数1つを返すテストケースを通せるようにする19:12これが通ったので、あとはいい感じに拡張すればいけそう19:13関数の引数に渡せるのはexpressionであってstatementは渡せないので、statementを渡したいときは、別途関数を作って、引数としてその関数(の返り値)を渡せば良さそう19:14全てのテストケースをtest.c
に移すと800行とかになったので、いくつかのファイルに分割した方が良さそう
-mikiken 10/22/2022 2:37 AM適当に7ファイルくらいに分割した02:38make clean
が、サブディレクトリに対してもいい感じに作用してほしい
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 10/21/2022 7:12 PM
+ 実装するときに考えてたことのメモ
+ 何したらいいか分かりづらいので、とりあえず、test.c
1ファイルに全ての処理をベタ書きしてみる (edited)
+
+
+
+
+
+
+ 19:12
+
+
+ まずは整数1つを返すテストケースを通せるようにする
+
+
+
+
+
+
+ 19:12
+
+
+ これが通ったので、あとはいい感じに拡張すればいけそう
+
+
+
+
+
+
+ 19:13
+
+
+ 関数の引数に渡せるのはexpressionであってstatementは渡せないので、statementを渡したいときは、別途関数を作って、引数としてその関数(の返り値)を渡せば良さそう
+
+
+
+
+
+
+ 19:14
+
+
+ 全てのテストケースをtest.c
に移すと800行とかになったので、いくつかのファイルに分割した方が良さそう
+
+
+
+
+
+
+
+
+
+ mikiken 10/22/2022 2:37 AM
+ 適当に7ファイルくらいに分割した
+
+
+
+
+
+
+ 02:38
+
+
+ make clean
が、サブディレクトリに対してもいい感じに作用してほしい
+
+
+
+
+
+
+ 02:38
+
+
+ # ccに渡すコンパイラオプションを指定
CFLAGS=-std=c11 -g -static
# src/*.cをソースとして指定
SRCS=$(wildcard src/*.c)
@@ -167,11 +1693,92 @@
rm -f 9cc src/*.o test/*.s tmp* .gdb_history peda-session-*.txt src/peda-session-*.txt .vscode/peda-session-*.txt
# testとcleanをダミーターゲット(実際に存在しないファイル)に指定
-.PHONY: test clean
02:39SUBDIR
的な変数を用意して、ゴニャゴニャしたらうまくいきそう(?)
-mikiken 10/24/2022 5:59 PM%演算子を実装する18:00Each of the operands shall have arithmetic type. The operands of the % operator shall have integer type.
-mikiken 10/24/2022 11:39 PMseccamp2018 c compiler. Contribute to hsjoihs/c-compiler development by creating an account on GitHub.23:41フォーマットしたやつ
-int putchar();
+.PHONY: test clean
+
+
+
+
+
+
+ 02:39
+
+
+ SUBDIR
的な変数を用意して、ゴニャゴニャしたらうまくいきそう(?)
+
+
+
+
+
+
+
+
+
+ mikiken 10/24/2022 5:59 PM
+ %演算子を実装する
+
+
+
+
+
+
+ 17:59
+
+
+
+
+
+
+
+ 18:00
+
+
+
+
+
+ Each of the operands shall have arithmetic type. The operands of the % operator shall have integer type.
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 10/24/2022 11:39 PM
+
+
+
+
+
+
+
+
+ seccamp2018 c compiler. Contribute to hsjoihs/c-compiler development by creating an account on GitHub.
+
+
+
+
+
+
+
+
+
+
+
+
+ 23:41
+
+
+ フォーマットしたやつ
+ int putchar();
void *memset();
int m(int a, int b) {
@@ -226,29 +1833,366 @@
usleep(50000);
}
return 0;
-}
23:42これ見ると、
-・初期化式全般
-・void
型
-・&&
演算子(というか論理演算子全般)
-・インクリメント++
とデクリメント--
演算子
-あたりが足りなさそう (edited)23:44ということで今週はこの辺を追加していきたい23:45あとポインタを返り値とするようなテストケースが全然ない気がするので、それも追加したい
-mikiken 10/25/2022 1:09 PMあとsizeof(int)
みたいな記法が未実装
-
-mikiken 11/08/2022 7:39 PM未初期化のポインタを参照するのは未定義動作だが、その場合も考慮した実装
-https://github.com/karintou8710/kcc/commit/8d56aea5861c72eae36a723aa662123268d67c2c (edited)19:41(左辺で未定義動作踏んだらセグフォするとは思うけど)19:42とりあえず未定義動作踏まなければ大丈夫なはずなので、一旦これでいく
-mikiken 11/13/2022 11:16 PMBNFの修正点
-・関数宣言を追加
-・Func_name(void)
みたいな記法に対応
-・関数のパラメータあたりのBNFがいろいろアレ (edited)
-mikiken 11/15/2022 2:18 PM関数宣言に今のところパラメータが書けない (対応済) (edited)
-mikiken 11/15/2022 3:05 PMStep ?? カンマ演算子を追加する
-mikiken 11/15/2022 3:17 PM15:18こんな感じの構文木にパースしないといけないはず
-mikiken 12/01/2022 4:22 PM例のドーナツを動かす
-mikiken 12/01/2022 4:37 PM破滅の刃() ~無限デバッグ編~ (edited)16:37原因調査16:37まず、z[o]
のところで落ちてそう16:38o
の値を調べてみると、定義した配列の要素数を大幅に上回っているので、これのせいで変なメモリアクセスが発生し、セグフォしてるっぽい16:39ということで各種変数の値について、ccの出力と9ccの出力を比較してみると、2ステップ目で既にN
の値が異なっていることが分かる(上記画像参照) (edited)16:39(恐らく)N
の値がバグってることで、後々o
も変な値になってる16:40ということで
-N = 8 * (m(m(l, r) - m(m(w, q), p), u) - m(m(w, r), p) - m(l, q) - m(m(e, v), p)) / s;
-の各オペランドごとに値を出力してみた16:4114.84 KB16:42op1
の値がバグってる
-mikiken 12/01/2022 4:55 PMさらにop1
を分解16:5516.53 KB16:56ちなみにここまで、こんな感じで展開している
- printf("<< %d >>\n", ++count);
+}
+
+
+
+
+
+
+ 23:42
+
+
+ これ見ると、
+ ・初期化式全般
+ ・void
型
+ ・&&
演算子(というか論理演算子全般)
+ ・インクリメント++
とデクリメント--
演算子
+ あたりが足りなさそう (edited)
+
+
+
+
+
+
+ 23:44
+
+
+ ということで今週はこの辺を追加していきたい
+
+
+
+
+
+
+ 23:45
+
+
+ あとポインタを返り値とするようなテストケースが全然ない気がするので、それも追加したい
+
+
+
+
+
+
+
+
+
+ mikiken 10/25/2022 1:09 PM
+ あとsizeof(int)
みたいな記法が未実装
+
+
+
+
+
+
+
+
+
+ mikiken 10/25/2022 1:46 PM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 11/08/2022 7:39 PM
+ 未初期化のポインタを参照するのは未定義動作だが、その場合も考慮した実装
+ https://github.com/karintou8710/kcc/commit/8d56aea5861c72eae36a723aa662123268d67c2c (edited)
+
+
+
+
+
+
+
+
+
+
+ 19:40
+
+
+
+
+
+
+
+ 19:41
+
+
+ (左辺で未定義動作踏んだらセグフォするとは思うけど)
+
+
+
+
+
+
+ 19:42
+
+
+ とりあえず未定義動作踏まなければ大丈夫なはずなので、一旦これでいく
+
+
+
+
+
+
+
+
+
+ mikiken 11/13/2022 11:16 PM
+ BNFの修正点
+ ・関数宣言を追加
+ ・Func_name(void)
みたいな記法に対応
+ ・関数のパラメータあたりのBNFがいろいろアレ (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 11/15/2022 2:18 PM
+ 関数宣言に今のところパラメータが書けない (対応済) (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 11/15/2022 3:05 PM
+ Step ?? カンマ演算子を追加する
+
+
+
+
+
+
+
+
+
+ mikiken 11/15/2022 3:17 PM
+
+
+
+
+
+
+
+ 15:18
+
+
+ こんな感じの構文木にパースしないといけないはず
+
+
+
+
+
+
+
+
+
+ mikiken 12/01/2022 4:22 PM
+ 例のドーナツを動かす
+
+
+
+
+
+
+
+
+
+
+ mikiken 12/01/2022 4:37 PM
+ 破滅の刃() ~無限デバッグ編~ (edited)
+
+
+
+
+
+
+ 16:37
+
+
+ 原因調査
+
+
+
+
+
+
+ 16:37
+
+
+ まず、z[o]
のところで落ちてそう
+
+
+
+
+
+
+ 16:38
+
+
+ o
の値を調べてみると、定義した配列の要素数を大幅に上回っているので、これのせいで変なメモリアクセスが発生し、セグフォしてるっぽい
+
+
+
+
+
+
+ 16:39
+
+
+ ということで各種変数の値について、ccの出力と9ccの出力を比較してみると、2ステップ目で既にN
の値が異なっていることが分かる(上記画像参照) (edited)
+
+
+
+
+
+
+ 16:39
+
+
+ (恐らく)N
の値がバグってることで、後々o
も変な値になってる
+
+
+
+
+
+
+ 16:40
+
+
+ ということで
+ N = 8 * (m(m(l, r) - m(m(w, q), p), u) - m(m(w, r), p) - m(l, q) - m(m(e, v), p)) / s;
+ の各オペランドごとに値を出力してみた
+
+
+
+
+
+
+ 16:41
+
+
+
+
+
+ 14.84 KB
+
+
+
+
+
+
+
+
+ 16:42
+
+
+ op1
の値がバグってる
+
+
+
+
+
+
+
+
+
+ mikiken 12/01/2022 4:55 PM
+ さらにop1
を分解
+
+
+
+
+
+
+ 16:55
+
+
+
+
+
+ 16.53 KB
+
+
+
+
+
+
+
+
+ 16:56
+
+
+ ちなみにここまで、こんな感じで展開している
+ printf("<< %d >>\n", ++count);
if (count >= 90)
return 0;
@@ -292,8 +2236,28 @@
printf("y = %d\n", y);
printf("o = %d\n", o);
printf("N = %d\n\n", N);
- */
16:59関数int m(int a, int b);
が返している値がおかしいと考えられる
-mikiken 12/01/2022 5:24 PMテストコードint m(int a, int b) {
+ */
+
+
+
+
+
+
+ 16:59
+
+
+ 関数int m(int a, int b);
が返している値がおかしいと考えられる
+
+
+
+
+
+
+
+
+
+ mikiken 12/01/2022 5:24 PM
+ テストコードint m(int a, int b) {
return (a * b + 5000) / 10000;
}
@@ -313,8 +2277,18 @@
printf("m(-1989, 10000) = %d\n", m(-1989, 10000));
return 0;
-}
(edited)17:25出力結果
-m( 0, 10000) = 0 m( 0, 10000) = 0
+}
(edited)
+
+
+
+
+
+
+ 17:25
+
+
+ 出力結果
+ m( 0, 10000) = 0 m( 0, 10000) = 0
m(-200, 10000) = 429297 | m(-200, 10000) = -199
m(-400, 10000) = 429097 | m(-400, 10000) = -399
m(-600, 10000) = 428897 | m(-600, 10000) = -599
@@ -324,9 +2298,19 @@
m(-1397, 10000) = 428100 | m(-1397, 10000) = -1396
m(-1595, 10000) = 427902 | m(-1595, 10000) = -1594
m(-1792, 10000) = 427705 | m(-1792, 10000) = -1791
-m(-1989, 10000) = 427508 | m(-1989, 10000) = -1988
-mikiken 12/01/2022 5:48 PMm
のどこで事故ってるか調査
-int printf();
+m(-1989, 10000) = 427508 | m(-1989, 10000) = -1988
+
+
+
+
+
+
+
+
+
+ mikiken 12/01/2022 5:48 PM
+ m
のどこで事故ってるか調査
+ int printf();
int m(int a, int b) {
printf("a = %d\n", a);
@@ -364,9 +2348,19 @@
*/
return 0;
-}
-mikiken 12/01/2022 6:50 PMm
のどこで事故ってるか調査
-int printf();
+}
+
+
+
+
+
+
+
+
+
+ mikiken 12/01/2022 6:50 PM
+ m
のどこで事故ってるか調査
+ int printf();
int m(int a, int b) {
printf("a = %d\n", a);
@@ -404,21 +2398,247 @@
*/
return 0;
-}
18:51符号拡張を型のサイズによって変える必要があるっぽい18:52@mikikeen これ僕もバグらせたんですが、ログを「割り算」などで検索すると当時の会話でそのことについて話してます。https://t.co/pCdLWPAyRm Twitter • 12/01/2022 5:44 PM 18:53型のサイズに応じて符号拡張する関数書いたら、ドーナツ出た
-(ただ、まだセグフォする)
-https://twitter.com/mikikeen/status/1598726498948591616 (edited) Twitter • 12/03/2022 2:11 AM
-mikiken 12/12/2022 7:50 PM久々にデバッグの続きをやっていく~~19:51とりあえず
-・符号拡張直したやつ
-・配列をポインタにキャストし忘れてたやつを追加
-をcommitした19:52それで何がどうなっていたか、忘れてたのでMakeFileの差分もdiscardした19:52codegen.cのコメントアウトでいい感じにnodeの種類を表示するやつはそのまま置いてある19:53seccamp2018 c compiler. Contribute to hsjoihs/c-compiler development by creating an account on GitHub.19:54donutifyされてるやつを普通に戻して初期化式を排除して、下のメッセージを消去したdonut.cがこれ19:551.54 KB19:55とりあえず9ccでコンパイルしてみる19:55usleepでセグフォしてる19:56こういうの
-void usleep();
+}
+
+
+
+
+
+
+ 18:51
+
+
+ 符号拡張を型のサイズによって変える必要があるっぽい
+
+
+
+
+
+
+
+
+
+ 18:52
+
+
+
+
+
+
+
+
+
+
+ @mikikeen これ僕もバグらせたんですが、ログを「割り算」などで検索すると当時の会話でそのことについて話してます。https://t.co/pCdLWPAyRm
+
+
+
+ Twitter • 12/01/2022 5:44 PM
+
+
+
+
+
+
+
+
+ 18:53
+
+
+ 型のサイズに応じて符号拡張する関数書いたら、ドーナツ出た
+ (ただ、まだセグフォする)
+ https://twitter.com/mikikeen/status/1598726498948591616 (edited)
+
+
+
+
+
+
+
+
+ 自作コンパイラ、伝家の宝刀†printfデバッグ†してたら、printfの有無で挙動変わって頭抱えてる
+
+
+
+
+ Likes
+
+
+ 149
+
+
+
+
+
+ Twitter • 12/03/2022 2:11 AM
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 12/12/2022 7:50 PM
+ 久々にデバッグの続きをやっていく~~
+
+
+
+
+
+
+ 19:51
+
+
+ とりあえず
+ ・符号拡張直したやつ
+ ・配列をポインタにキャストし忘れてたやつを追加
+ をcommitした
+
+
+
+
+
+
+ 19:52
+
+
+ それで何がどうなっていたか、忘れてたのでMakeFileの差分もdiscardした
+
+
+
+
+
+
+ 19:52
+
+
+ codegen.cのコメントアウトでいい感じにnodeの種類を表示するやつはそのまま置いてある
+
+
+
+
+
+
+ 19:53
+
+
+
+
+
+
+
+
+
+
+ seccamp2018 c compiler. Contribute to hsjoihs/c-compiler development by creating an account on GitHub.
+
+
+
+
+
+
+
+
+
+
+
+
+ 19:54
+
+
+ donutifyされてるやつを普通に戻して初期化式を排除して、下のメッセージを消去したdonut.cがこれ
+
+
+
+
+
+
+ 19:55
+
+
+
+
+
+ 1.54 KB
+
+
+
+
+
+
+
+
+ 19:55
+
+
+ とりあえず9ccでコンパイルしてみる
+
+
+
+
+
+
+ 19:55
+
+
+ usleepでセグフォしてる
+
+
+
+
+
+
+ 19:56
+
+
+ こういうの
+ void usleep();
int printf();
int main() {
usleep(2000000);
printf("Now testing usleep() calling.\n");
-}
19:56がコンパイルできるか確認したところ、これは通る
-mikiken 12/14/2022 2:36 AMProgram received signal SIGSEGV, Segmentation fault.
+}
+
+
+
+
+
+
+ 19:56
+
+
+ がコンパイルできるか確認したところ、これは通る
+
+
+
+
+
+
+
+
+
+ mikiken 12/14/2022 2:36 AM
+ Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0x7fffff7feffc
RBX: 0x2
@@ -454,8 +2674,28 @@
Stopped reason: SIGSEGV
m () at tmp.s:18
18 mov DWORD PTR [rax], edi
-gdb-peda$ q
-mikiken 12/14/2022 1:15 PMワンチャン原因これか?13:15mikiken@DESKTOP-CM4259U:~/compiler/9cc$ ulimit -a
+gdb-peda$ q
+
+
+
+
+
+
+
+
+
+ mikiken 12/14/2022 1:15 PM
+ ワンチャン原因これか?
+
+
+
+
+
+
+ 13:15
+
+
+ mikiken@DESKTOP-CM4259U:~/compiler/9cc$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
@@ -471,10 +2711,102 @@
cpu time (seconds, -t) unlimited
max user processes (-u) 31257
virtual memory (kbytes, -v) unlimited
-file locks (-x) unlimited
13:18はい正解~~~
-mikiken 12/14/2022 4:55 PM顛末自作のmodintライブラリのテストがてら、( 10^6 \times 10 )サイズの配列を宣言してみたらsegmentation faultが出た。計算途中にオーバーフローを起こすような処理(例えばmodを取る前の値がlong longのmaxを超えているとか)を行っているわけではないのでおそらくメモリ関連のエラーであると推測する。同様のサイズの配列をWindows環境でMinGW G++を用いてコンパイルして実行しても正常に通るのでおそらくLinux(Ubuntu on WSL...
-mikiken 12/14/2022 5:37 PMなんかusleep.c
がないって言われてるっぽい? (edited)17:377.35 KB
-mikiken 12/21/2022 12:39 AMスタックの16byte-alignmentが崩れている00:39.L.end.3:
+file locks (-x) unlimited
+
+
+
+
+
+
+
+ 13:18
+
+
+ はい正解~~~
+
+
+
+
+
+
+
+
+
+ mikiken 12/14/2022 4:55 PM
+
+
+
+
+
+
+
+
+ 顛末自作のmodintライブラリのテストがてら、( 10^6 \times 10 )サイズの配列を宣言してみたらsegmentation faultが出た。計算途中にオーバーフローを起こすような処理(例えばmodを取る前の値がlong longのmaxを超えているとか)を行っているわけではないのでおそらくメモリ関連のエラーであると推測する。同様のサイズの配列をWindows環境でMinGW G++を用いてコンパイルして実行しても正常に通るのでおそらくLinux(Ubuntu on WSL...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 12/14/2022 5:37 PM
+ なんかusleep.c
がないって言われてるっぽい? (edited)
+
+
+
+
+
+
+ 17:37
+
+
+
+
+
+ 7.35 KB
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 12/21/2022 12:39 AM
+ スタックの16byte-alignmentが崩れている
+
+
+
+
+
+
+ 00:39
+
+
+ .L.end.3:
lea rdi, [rbp-8844]
push rdi
lea rdi, [rbp-8844]
@@ -521,19 +2853,136 @@
call a
push rax
jmp .L.begin.2
-.L.end.2:
00:41最初におかしくなってるのが、tmp.s
の1133行目で、これに対応するのはdonut.c
の50行目の
- for (j = 0; j < 314; j++, a(&e, &w, s - 2, 200)) {
-関数a
を呼び出す部分だと考えられる
-mikiken 01/08/2023 11:13 PM~1ヶ月経過~
-for(expr; expr; expr)
のexpr
の値がpushされたままになってたのが原因
-mikiken 01/11/2023 1:30 AM
-mikiken 02/14/2023 6:38 PM再び1ヶ月経過18:381ヶ月ぶりに自作コンパイラのコード見てるけど、何をしようとしてたか忘れた Twitter • 02/13/2023 4:54 PM 18:39ローカルな配列の初期化式を実装しようとしてたっぽい Twitter • 02/13/2023 4:55 PM 18:39というわけでやっていき18:40こんな感じのコードを追加し、
-Node *array_init(Function *func, Token *ident, Token *tok) {
+.L.end.2:
+
+
+
+
+
+
+ 00:41
+
+
+ 最初におかしくなってるのが、tmp.s
の1133行目で、これに対応するのはdonut.c
の50行目の
+ for (j = 0; j < 314; j++, a(&e, &w, s - 2, 200)) {
+ 関数a
を呼び出す部分だと考えられる
+
+
+
+
+
+
+
+
+
+ mikiken 01/08/2023 11:13 PM
+ ~1ヶ月経過~
+ for(expr; expr; expr)
のexpr
の値がpushされたままになってたのが原因
+
+
+
+
+
+
+
+
+
+ mikiken 01/11/2023 1:30 AM
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 02/14/2023 6:38 PM
+ 再び1ヶ月経過
+
+
+
+
+
+
+ 18:38
+
+
+
+
+
+
+
+
+
+
+ 1ヶ月ぶりに自作コンパイラのコード見てるけど、何をしようとしてたか忘れた
+
+
+
+ Twitter • 02/13/2023 4:54 PM
+
+
+
+
+
+
+
+
+ 18:39
+
+
+
+
+
+
+
+
+
+
+ ローカルな配列の初期化式を実装しようとしてたっぽい
+
+
+
+ Twitter • 02/13/2023 4:55 PM
+
+
+
+
+
+
+
+
+ 18:39
+
+
+ というわけでやっていき
+
+
+
+
+
+
+ 18:40
+
+
+ こんな感じのコードを追加し、
+ Node *array_init(Function *func, Token *ident, Token *tok) {
Node head;
Node *cur = &head;
for (int offset = 0; !consume(tok, TK_RIGHT_BRACE); offset++) {
@@ -546,31 +2995,283 @@
expect(tok, TK_COMMA);
}
return head.next;
-}
18:40こういうコードが通るようになった
-int array_test13() {
+}
+
+
+
+
+
+
+ 18:40
+
+
+ こういうコードが通るようになった
+ int array_test13() {
int a[5] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++)
sum += a[i];
return sum;
-}
18:40ケツカンマがあっても通る実装になってる18:41あと実装しないといけない仕様としては、18:41配列の長さを明示的に書かずに初期化するやつ
-int arr[] = {1,2,3};
(edited)18:43あとは初期化式が一部しか与えられてない場合に、残りの要素を0埋めするやつとか
-int x[5] = {1, 2, 3}; // これは下と等価
-int x[5] = {1, 2, 3, 0, 0};
18:45そもそも、ただ配列を宣言しただけのときって、初期化されたっけ?
-int arr[3]; // これの初期値どうなってる?
18:48あとは、定義したデカさ以上の要素を参照しようとしたときに警告出すとか
-(gccで試したらwarningになったので、未定義動作っぽい?)←確認が必要
-mikiken 02/15/2023 2:21 PMポインタの配列のテストケース入れてないな、そういえば
-mikiken 02/15/2023 3:06 PMとりあえず配列の長さを明示的に書かないやつを実装していく
-mikiken 02/15/2023 5:59 PM適当にコード足したら、変数のオフセットの処理書き忘れたっぽくて、出力したアセンブリがセグフォする
-mikiken 02/15/2023 9:54 PMテストでコケても、他のテストケースも流せるようにしたいかも
-mikiken 02/15/2023 11:34 PMND_LVARっていうNodekind、実質使ってないから抹消したさあるな
-(追記) int a[3];
みたいな感じで宣言のみを行う場合、木構造の葉にあたるnodeがなくなってしまうので、あった方が便利 (edited)
-mikiken 02/16/2023 5:12 PM本来出力されるべきアセンブリと現状出力されているものとの差分17:127.91 KB
-21:20あと初期化の要素が配列の長さより短い場合に、残りの要素に0を代入するやつを実装した
-https://github.com/mikiken/9cc/commit/e45a6b45485e02bdcf6d5855b96f2641cfb337c3 (edited)if not all elements are given at initalizer21:21char str[] = "hoge"; みたいな構文を実装しようとしたが、文字リテラルをまだ実装していなかった
-(文字列リテラルはある) Twitter • 02/17/2023 3:39 PM 21:21というわけで、文字リテラルを実装していく21:22大体できたけど、エスケープシーケンスが2文字と解釈されてエラーでる(それはそう)21:23こんな感じのコードを追加し、tok->valの値とすることで、ひとまず動くようにはなった
-char read_escape_char(char *p) {
+}
+
+
+
+
+
+
+ 18:40
+
+
+ ケツカンマがあっても通る実装になってる
+
+
+
+
+
+
+ 18:41
+
+
+ あと実装しないといけない仕様としては、
+
+
+
+
+
+
+ 18:41
+
+
+ 配列の長さを明示的に書かずに初期化するやつ
+ int arr[] = {1,2,3};
(edited)
+
+
+
+
+
+
+ 18:43
+
+
+ あとは初期化式が一部しか与えられてない場合に、残りの要素を0埋めするやつとか
+ int x[5] = {1, 2, 3}; // これは下と等価
+int x[5] = {1, 2, 3, 0, 0};
+
+
+
+
+
+
+ 18:45
+
+
+ そもそも、ただ配列を宣言しただけのときって、初期化されたっけ?
+ int arr[3]; // これの初期値どうなってる?
+
+
+
+
+
+
+ 18:48
+
+
+ あとは、定義したデカさ以上の要素を参照しようとしたときに警告出すとか
+ (gccで試したらwarningになったので、未定義動作っぽい?)←確認が必要
+
+
+
+
+
+
+
+
+
+ mikiken 02/15/2023 2:21 PM
+ ポインタの配列のテストケース入れてないな、そういえば
+
+
+
+
+
+
+
+
+
+ mikiken 02/15/2023 3:06 PM
+ とりあえず配列の長さを明示的に書かないやつを実装していく
+
+
+
+
+
+
+
+
+
+ mikiken 02/15/2023 5:59 PM
+ 適当にコード足したら、変数のオフセットの処理書き忘れたっぽくて、出力したアセンブリがセグフォする
+
+
+
+
+
+
+
+
+
+ mikiken 02/15/2023 9:54 PM
+ テストでコケても、他のテストケースも流せるようにしたいかも
+
+
+
+
+
+
+
+
+
+ mikiken 02/15/2023 11:34 PM
+ ND_LVARっていうNodekind、実質使ってないから抹消したさあるな
+ (追記) int a[3];
みたいな感じで宣言のみを行う場合、木構造の葉にあたるnodeがなくなってしまうので、あった方が便利 (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 02/16/2023 5:12 PM
+ 本来出力されるべきアセンブリと現状出力されているものとの差分
+
+
+
+
+
+
+ 17:12
+
+
+
+
+
+ 7.91 KB
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 02/17/2023 9:19 PM
+ 配列の長さを省略する記法をサポート
+ https://github.com/mikiken/9cc/commit/7335f8135ffd28916d86c738c526180645331b50 (edited)
+
+
+
+
+
+
+
+
+
+
+ 21:20
+
+
+ あと初期化の要素が配列の長さより短い場合に、残りの要素に0を代入するやつを実装した
+ https://github.com/mikiken/9cc/commit/e45a6b45485e02bdcf6d5855b96f2641cfb337c3 (edited)
+
+
+
+
+
+
+
+ if not all elements are given at initalizer
+
+
+
+
+
+
+
+
+
+
+
+
+ 21:21
+
+
+
+
+
+
+
+
+
+
+ char str[] = "hoge"; みたいな構文を実装しようとしたが、文字リテラルをまだ実装していなかった
+ (文字列リテラルはある)
+
+
+
+ Twitter • 02/17/2023 3:39 PM
+
+
+
+
+
+
+
+
+ 21:21
+
+
+ というわけで、文字リテラルを実装していく
+
+
+
+
+
+
+ 21:22
+
+
+ 大体できたけど、エスケープシーケンスが2文字と解釈されてエラーでる(それはそう)
+
+
+
+
+
+
+ 21:23
+
+
+ こんな感じのコードを追加し、tok->valの値とすることで、ひとまず動くようにはなった
+ char read_escape_char(char *p) {
switch (*p) {
case 'a':
return 7;
@@ -597,19 +3298,93 @@
default:
return *p;
}
-}
21:24文字列リテラルでも似たようなコードを書いてしまってるのをどうにかしたい
-mikiken 02/17/2023 9:33 PM実は文字列リテラル実装するとき、Compiler Explorerの出力を見ようみまねで真似ただけで、いまいち仕組みが分かっていない (edited)
-mikiken 02/21/2023 3:56 PMえーとchibiccでは、文字列リテラルはdataセクションにchar
の配列として記録されているっぽい (edited)
-mikiken 02/21/2023 4:29 PM一方、自分のやつの場合、.string
という記法を使ってお茶を濁している (edited)16:34このディレクティブあたりの話は、以下のURL見ると良さそう
-http://www.swlab.cs.okayama-u.ac.jp/~nom/lect/p3/what-is-directive.html
-https://suu-g.hateblo.jp/entry/20080510/1210408956 (edited)アセンブリコードをアセンブルすると機械語になる。こういう説明が世の中じゃされてるけど、でもそれは半分ウソ。アセンブリコードの中でも機械語に直接対応しないものがある。それがディレクティブと呼ばれているもので、ドットから始まる命令がこれにあたる。 疑似命令 (psuedo-ops) とも呼ばれるこのアセンブラディレクティブは、機械語への直接の対応ではなくて、アセンブラ(GNU as)に対する命令。 前回の記事で説明したのはセクションについてだけだったけれども、ディレクティブにはもっといろいろな意味がある。 以下、その具体的な例を挙げていくよ。 文字列を配置する [.ascii .asciz .st…
-mikiken 02/21/2023 4:53 PM現在のfind_string_literal_end()
の実装
-char *find_string_literal_end(char *start) {
+}
+
+
+
+
+
+
+ 21:24
+
+
+ 文字列リテラルでも似たようなコードを書いてしまってるのをどうにかしたい
+
+
+
+
+
+
+
+
+
+ mikiken 02/17/2023 9:33 PM
+ 実は文字列リテラル実装するとき、Compiler Explorerの出力を見ようみまねで真似ただけで、いまいち仕組みが分かっていない (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 02/21/2023 3:56 PM
+ えーとchibiccでは、文字列リテラルはdataセクションにchar
の配列として記録されているっぽい (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 02/21/2023 4:29 PM
+ 一方、自分のやつの場合、.string
という記法を使ってお茶を濁している (edited)
+
+
+
+
+
+
+ 16:34
+
+
+ このディレクティブあたりの話は、以下のURL見ると良さそう
+ http://www.swlab.cs.okayama-u.ac.jp/~nom/lect/p3/what-is-directive.html
+ https://suu-g.hateblo.jp/entry/20080510/1210408956 (edited)
+
+
+
+
+
+
+
+
+ アセンブリコードをアセンブルすると機械語になる。こういう説明が世の中じゃされてるけど、でもそれは半分ウソ。アセンブリコードの中でも機械語に直接対応しないものがある。それがディレクティブと呼ばれているもので、ドットから始まる命令がこれにあたる。 疑似命令 (psuedo-ops) とも呼ばれるこのアセンブラディレクティブは、機械語への直接の対応ではなくて、アセンブラ(GNU as)に対する命令。 前回の記事で説明したのはセクションについてだけだったけれども、ディレクティブにはもっといろいろな意味がある。 以下、その具体的な例を挙げていくよ。 文字列を配置する [.ascii .asciz .st…
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 02/21/2023 4:53 PM
+ 現在のfind_string_literal_end()
の実装
+ char *find_string_literal_end(char *start) {
char *p;
for (p = start; *p != '\"'; p++) {
if (*p == '\0' || *p == '\n')
@@ -618,21 +3393,81 @@
p++;
}
return p;
-}
16:58ちょっと実験
-int main() {
+}
+
+
+
+
+
+
+ 16:58
+
+
+ ちょっと実験
+ int main() {
char c = '"'; // => Program returned: 34
return c;
-}
16:58int main() {
+}
+
+
+
+
+
+
+ 16:58
+
+
+ int main() {
char c = '\"'; // => Program returned: 34
return c;
-}
17:00int main() {
+}
+
+
+
+
+
+
+ 17:00
+
+
+ int main() {
// char c = '\'; // => コンパイルエラー (2個目のシングルクオートがエスケープされている)
char c = '\\'; // => Program returned: 92
return c;
-}
-mikiken 02/22/2023 10:33 PM22:34find_string_literal_end()
関数は、for文の更新式は条件判定の前に評価されることを上手く利用している (edited)
-mikiken 02/23/2023 9:47 PM文字列リテラルの実装をとりあえずこうしてみた
- // 文字列リテラルの場合
+}
+
+
+
+
+
+
+
+
+
+ mikiken 02/22/2023 10:33 PM
+
+
+
+
+
+
+
+ 22:34
+
+
+ find_string_literal_end()
関数は、for文の更新式は条件判定の前に評価されることを上手く利用している (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 02/23/2023 9:47 PM
+ 文字列リテラルの実装をとりあえずこうしてみた
+ // 文字列リテラルの場合
if (startswith(p, "\"")) {
char *start = ++p; // ダブルクオートを読み飛ばす
char *end = find_string_literal_end(p);
@@ -660,54 +3495,281 @@
cur = new_token(TK_STR, cur, buf, buf + len - 1);
p++; // ダブルクオートを読み飛ばす
continue;
- }
21:49\a
とか\e
が動かないのは、.string
ディレクティブがこれらのエスケープシーケンスに対応していないっぽい21:50http://web.mit.edu/gnu/doc/html/as_7.html#SEC120
-.string "str"
-Copy the characters in str to the object file. You may specify more than one string to copy, separated by commas. Unless otherwise specified for a particular machine, the assembler marks the end of each string with a 0 byte. You can use any of the escape sequences described in section Strings. (edited)21:51Stringsのsectionを見てみると、21:55Strings
-A string is written between double-quotes. It may contain double-quotes or null characters. The way to get special characters into a string is to escape these characters: precede them with a backslash \
character. For example \\
represents one backslash: the first \
is an escape which tells as to interpret the second character literally as a backslash (which prevents as from recognizing the second \
as an escape character). The complete list of escapes follows. (edited)21:55\b
-Mnemonic for backspace; for ASCII this is octal code 010.
-\f
-Mnemonic for FormFeed; for ASCII this is octal code 014.
-\n
-Mnemonic for newline; for ASCII this is octal code 012.
-\r
-Mnemonic for carriage-Return; for ASCII this is octal code 015.
-\t
-Mnemonic for horizontal Tab; for ASCII this is octal code 011.
-\ digit digit digit
-An octal character code. The numeric code is 3 octal digits. For compatibility with other Unix systems, 8 and 9 are accepted as digits: for example, \008
has the value 010, and \009
the value 011.
-\x hex-digit hex-digit
-A hex character code. The numeric code is 2 hexadecimal digits. Either upper or lower case x works.
-\\
-Represents one \
character.
-\"
-Represents one "
character. Needed in strings to represent this character, because an unescaped "
would end the string.
-\ anything-else
-Any other character when escaped by \
gives a warning, but assembles as if the \
was not present. The idea is that if you used an escape sequence you clearly didn't want the literal interpretation of the following character. However as has no other interpretation, so as knows it is giving you the wrong code and warns you of the fact.
-Which characters are escapable, and what those escapes represent, varies widely among assemblers. The current set is what we think the BSD 4.2 assembler recognizes, and is a subset of what most C compilers recognize. If you are in doubt, do not use an escape sequence. (edited)
-mikiken 02/23/2023 11:37 PMgccは上記に記載されていないエスケープシーケンスの場合(例えば\a
)、\007
のように8進コードポイントを使って表現している
-mikiken 02/24/2023 2:28 PMコンパイラのソースには書いていないのにバイナリだけで代々伝わっていく情報というのがあって、それはコンピュータのセキュリティに大きく関わっている。ここではそれについて書いてみよう。 僕は8ccというCコンパイラをスクラッチから書いたことがあるのだけど、8ccには文字列を読む部分で、""の後に"n"がきたら"\n"という文字(改行文字)を読んだことにするという箇所がある。これはよく考えてみれば自己言及的になっていて、ソースコードの中に、コンパイラが実際に必要とする改行文字のASCIIコードの情報が含まれていない。しかしコンパイラをコンパイルするコンパイラからその情報が受け継がれるので、
-mikiken 02/24/2023 2:36 PMそういえば、gen_data_section()
のコードを書き換えてたときに、printf
とかputchar
の出力がすぐには表示されなかったが、これは一旦出力がストリームにバッファリングされるのが原因
-(必要な場合はfflush
関数を用いることで、即座にバッファを吐き出すことができる)
-mikiken 02/24/2023 4:18 PMとりあえず、文字列リテラル内のエスケープシーケンスがちゃんと出力されるようになった
-https://github.com/mikiken/9cc/commit/613ade31af9bbad8bfef1d13cbc9dc0ae5f57593 (edited)16:20かなり横道に逸れたけど、なにをしていたかというと、
-char str[] = "hoge";
-みたいな構文を書けるようにしようとしていたはず16:23char *str = "hoge";
-とは違って、上記の構文はちゃんとスタック領域に確保されたcharの配列として扱わないとだめ
-
-mikiken 02/26/2023 4:27 PM続いてグローバル変数の初期化式を実装していく
-(追記)一旦保留 (edited)
-mikiken 02/26/2023 10:36 PMとりあえず文字列リテラルの初期化式は後回しにして、ただのグローバル変数の初期化式とグローバルの配列の初期化式を実装していく
-mikiken 03/02/2023 7:15 PMローカル変数の初期化式は、単に宣言の式と代入の式に分解してパースすれば良いだけだったけど、グローバル変数の場合はそうはいかない
-mikiken 03/03/2023 11:47 AM例えば、トップレベルに
-int x = 3;
-と書いた場合、
-x:
+ }
+
+
+
+
+
+
+ 21:49
+
+
+ \a
とか\e
が動かないのは、.string
ディレクティブがこれらのエスケープシーケンスに対応していないっぽい
+
+
+
+
+
+
+ 21:50
+
+
+ http://web.mit.edu/gnu/doc/html/as_7.html#SEC120
+
+
+ .string "str"
+ Copy the characters in str to the object file. You may specify more than one string to copy, separated by commas. Unless otherwise specified for a particular machine, the assembler marks the end of each string with a 0 byte. You can use any of the escape sequences described in section Strings.
+
+ (edited)
+
+
+
+
+
+
+ 21:51
+
+
+ Stringsのsectionを見てみると、
+
+
+
+
+
+
+ 21:55
+
+
+
+
+
+ Strings
+ A string is written between double-quotes. It may contain double-quotes or null characters. The way to get special characters into a string is to escape these characters: precede them with a backslash \
character. For example \\
represents one backslash: the first \
is an escape which tells as to interpret the second character literally as a backslash (which prevents as from recognizing the second \
as an escape character). The complete list of escapes follows.
+
+ (edited)
+
+
+
+
+
+
+ 21:55
+
+
+
+
+
+ \b
+ Mnemonic for backspace; for ASCII this is octal code 010.
+ \f
+ Mnemonic for FormFeed; for ASCII this is octal code 014.
+ \n
+ Mnemonic for newline; for ASCII this is octal code 012.
+ \r
+ Mnemonic for carriage-Return; for ASCII this is octal code 015.
+ \t
+ Mnemonic for horizontal Tab; for ASCII this is octal code 011.
+ \ digit digit digit
+ An octal character code. The numeric code is 3 octal digits. For compatibility with other Unix systems, 8 and 9 are accepted as digits: for example, \008
has the value 010, and \009
the value 011.
+ \x hex-digit hex-digit
+ A hex character code. The numeric code is 2 hexadecimal digits. Either upper or lower case x works.
+ \\
+ Represents one \
character.
+ \"
+ Represents one "
character. Needed in strings to represent this character, because an unescaped "
would end the string.
+ \ anything-else
+ Any other character when escaped by \
gives a warning, but assembles as if the \
was not present. The idea is that if you used an escape sequence you clearly didn't want the literal interpretation of the following character. However as has no other interpretation, so as knows it is giving you the wrong code and warns you of the fact.
+ Which characters are escapable, and what those escapes represent, varies widely among assemblers. The current set is what we think the BSD 4.2 assembler recognizes, and is a subset of what most C compilers recognize. If you are in doubt, do not use an escape sequence.
+
+
+ (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 02/23/2023 11:37 PM
+ gccは上記に記載されていないエスケープシーケンスの場合(例えば\a
)、\007
のように8進コードポイントを使って表現している
+
+
+
+
+
+
+
+
+
+ mikiken 02/24/2023 2:28 PM
+
+
+
+
+
+
+
+
+ コンパイラのソースには書いていないのにバイナリだけで代々伝わっていく情報というのがあって、それはコンピュータのセキュリティに大きく関わっている。ここではそれについて書いてみよう。 僕は8ccというCコンパイラをスクラッチから書いたことがあるのだけど、8ccには文字列を読む部分で、""の後に"n"がきたら"\n"という文字(改行文字)を読んだことにするという箇所がある。これはよく考えてみれば自己言及的になっていて、ソースコードの中に、コンパイラが実際に必要とする改行文字のASCIIコードの情報が含まれていない。しかしコンパイラをコンパイルするコンパイラからその情報が受け継がれるので、
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 02/24/2023 2:36 PM
+ そういえば、gen_data_section()
のコードを書き換えてたときに、printf
とかputchar
の出力がすぐには表示されなかったが、これは一旦出力がストリームにバッファリングされるのが原因
+ (必要な場合はfflush
関数を用いることで、即座にバッファを吐き出すことができる)
+
+
+
+
+
+
+
+
+
+ mikiken 02/24/2023 4:18 PM
+ とりあえず、文字列リテラル内のエスケープシーケンスがちゃんと出力されるようになった
+ https://github.com/mikiken/9cc/commit/613ade31af9bbad8bfef1d13cbc9dc0ae5f57593 (edited)
+
+
+
+
+
+
+
+
+
+
+ 16:20
+
+
+ かなり横道に逸れたけど、なにをしていたかというと、
+ char str[] = "hoge";
+ みたいな構文を書けるようにしようとしていたはず
+
+
+
+
+
+
+ 16:23
+
+
+ char *str = "hoge";
+ とは違って、上記の構文はちゃんとスタック領域に確保されたcharの配列として扱わないとだめ
+
+
+
+
+
+
+
+
+
+ mikiken 02/26/2023 4:11 PM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 02/26/2023 4:27 PM
+ 続いてグローバル変数の初期化式を実装していく
+ (追記)一旦保留 (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 02/26/2023 10:36 PM
+ とりあえず文字列リテラルの初期化式は後回しにして、ただのグローバル変数の初期化式とグローバルの配列の初期化式を実装していく
+
+
+
+
+
+
+
+
+
+ mikiken 03/02/2023 7:15 PM
+ ローカル変数の初期化式は、単に宣言の式と代入の式に分解してパースすれば良いだけだったけど、グローバル変数の場合はそうはいかない
+
+
+
+
+
+
+
+
+
+ mikiken 03/03/2023 11:47 AM
+ 例えば、トップレベルに
+ int x = 3;
+ と書いた場合、
+ x:
.long 3
-とコンパイルしないといけない (edited)
-mikiken 03/03/2023 12:26 PM現状、
-codegen.c
-void gen_data_secton(Obj *gvar_list) {
+ とコンパイルしないといけない
(edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/03/2023 12:26 PM
+ 現状、
+ codegen.c
+ void gen_data_secton(Obj *gvar_list) {
printf(".data\n");
for (Obj *obj = gvar_list; obj->next != NULL; obj = obj->next) {
// 文字列リテラルの場合
@@ -721,8 +3783,18 @@
}
printf("\n");
}
-となっているので、 (edited)12:29Obj
にinit_expr
みたいなメンバを追加するのが良さそう
-typedef struct Obj Obj;
+ となっているので、
(edited)
+
+
+
+
+
+
+ 12:29
+
+
+ Obj
にinit_expr
みたいなメンバを追加するのが良さそう
+ typedef struct Obj Obj;
struct Obj {
Obj *next; // 次のオブジェクトまたはNULL
@@ -736,17 +3808,183 @@
// 文字列リテラル
char *init_data;
int str_id;
-};
(edited)
-mikiken 03/03/2023 11:10 PMと思ったけど、めんどくさそうなので、グローバル変数の初期化式は、一旦後回しにしようかな23:11例えば、
-int x = 3 + 2 * 4;
-みたいな式もトップレベルに書けるはずだが、これをコンパイルしようとすると、静的解析の時点で値を計算しておかないといけない気がする(?)23:12とりあえず、単に定数を代入する場合のみ書けるようにしてもいいけど、そもそも今の実装でグローバル変数を0以外で初期化してるところってそんなにないと思うから、とりあえず後に回そうという判断23:15現時点でのparse.c
を保存しておく23:1526.75 KB23:16ということで、次に実装したいと思ってた多次元配列を実装していく
-mikiken 03/04/2023 1:36 AM初めに 「配列編」と銘打っていますが、続編が投稿される保証はありません。 そうそう この記事は言語実装 Advent Calendar 2018とC言語 Advent Calendar 2018の17日目の記事です。 想定している読者層 (どういう読者層を想定しているんだろう、書いていて自分でもよく分からなくなった)(Cコンパイラ書いていて「配列の配列(いわゆる二次元配列)がなんかバグるなぁ」となった人のための記事かもなぁ)(というか、多分バグらせていた当時の自分への手紙) 本題に入ろう Cコンパイラを書く上で微妙にハマった、配列へのポインタの話、それに付随して構造体を実装する際の話について軽…
-mikiken 03/04/2023 1:46 AMたぶん配列全体へのポインタさえ実装できれば、割とすぐに実装できそう
-mikiken 03/05/2023 12:36 AMint arr[3][5];
という宣言に対して、arr[2][4]
は*(*(arr+2)+4)
としてパースすればOK
-mikiken 03/06/2023 12:51 PMparse_type()
とdeclaration()
で型のパースを(実質)2回やってるの、設計悪そう12:51識別子だけ保存しといて、parse_type()
でトークンを読み進めておけばよさそう?
-mikiken 03/06/2023 4:22 PMとりあえず、parse_type()
をparse_base_type()
とparse_variable_type()
に分割し、各関数の中でトークンを読み進めるようにした16:23declaration()
の中がまだ直せてないので、直す (edited)
-mikiken 03/06/2023 10:06 PM一旦メモ
- // 配列の場合
+};
(edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/03/2023 11:10 PM
+ と思ったけど、めんどくさそうなので、グローバル変数の初期化式は、一旦後回しにしようかな
+
+
+
+
+
+
+ 23:11
+
+
+ 例えば、
+ int x = 3 + 2 * 4;
+ みたいな式もトップレベルに書けるはずだが、これをコンパイルしようとすると、静的解析の時点で値を計算しておかないといけない気がする(?)
+
+
+
+
+
+
+ 23:12
+
+
+ とりあえず、単に定数を代入する場合のみ書けるようにしてもいいけど、そもそも今の実装でグローバル変数を0以外で初期化してるところってそんなにないと思うから、とりあえず後に回そうという判断
+
+
+
+
+
+
+ 23:15
+
+
+ 現時点でのparse.c
を保存しておく
+
+
+
+
+
+
+ 23:15
+
+
+
+
+
+ 26.75 KB
+
+
+
+
+
+
+
+
+ 23:16
+
+
+ ということで、次に実装したいと思ってた多次元配列を実装していく
+
+
+
+
+
+
+
+
+
+ mikiken 03/04/2023 1:36 AM
+
+
+
+
+
+
+
+
+
+ 初めに 「配列編」と銘打っていますが、続編が投稿される保証はありません。 そうそう この記事は言語実装 Advent Calendar 2018とC言語 Advent Calendar 2018の17日目の記事です。 想定している読者層 (どういう読者層を想定しているんだろう、書いていて自分でもよく分からなくなった)(Cコンパイラ書いていて「配列の配列(いわゆる二次元配列)がなんかバグるなぁ」となった人のための記事かもなぁ)(というか、多分バグらせていた当時の自分への手紙) 本題に入ろう Cコンパイラを書く上で微妙にハマった、配列へのポインタの話、それに付随して構造体を実装する際の話について軽…
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 03/04/2023 1:46 AM
+ たぶん配列全体へのポインタさえ実装できれば、割とすぐに実装できそう
+
+
+
+
+
+
+
+
+
+ mikiken 03/05/2023 12:36 AM
+ int arr[3][5];
という宣言に対して、arr[2][4]
は*(*(arr+2)+4)
としてパースすればOK
+
+
+
+
+
+
+
+
+
+ mikiken 03/06/2023 12:51 PM
+ parse_type()
とdeclaration()
で型のパースを(実質)2回やってるの、設計悪そう
+
+
+
+
+
+
+ 12:51
+
+
+ 識別子だけ保存しといて、parse_type()
でトークンを読み進めておけばよさそう?
+
+
+
+
+
+
+
+
+
+ mikiken 03/06/2023 4:22 PM
+ とりあえず、parse_type()
をparse_base_type()
とparse_variable_type()
に分割し、各関数の中でトークンを読み進めるようにした
+
+
+
+
+
+
+ 16:23
+
+
+ declaration()
の中がまだ直せてないので、直す (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/06/2023 10:06 PM
+ 一旦メモ
+ // 配列の場合
if (dec_type->kind == TYPE_ARRAY) {
while (dec_type->ptr_to) {
// 配列の要素数が明示されている場合
@@ -787,18 +4025,102 @@
return node;
}
}
- }
-mikiken 03/06/2023 10:14 PM初期化式が存在するときに配列宣言の要素数を省略できる記法は、難しそうなので、一旦消してから後で復活させるか (edited)
-mikiken 03/07/2023 1:08 AMsemantic_analysis.c
で、
-不正な加算を行うことはできません
-というエラーが出たので、原因を調べる (edited)01:09ここで、配列全体へのポインタを実装する必要が出てくるのだと思われる
-mikiken 03/07/2023 11:12 PM23:15int arr[2][3]
がint [3]
の要素数2の配列型であることに注意する必要がある
-(直感に反する)
-mikiken 03/07/2023 11:27 PM多次元配列のパースの仕方をミスってたので、直す
-順方向に伸びるLinked Listとして実装すればOKなはず (edited)
-mikiken 03/07/2023 11:48 PM直せた
-mikiken 03/08/2023 12:59 AM今までは、一次元配列であることを(暗黙に)仮定してたので、
- case ND_ADD: {
+ }
+
+
+
+
+
+
+
+
+
+ mikiken 03/06/2023 10:14 PM
+ 初期化式が存在するときに配列宣言の要素数を省略できる記法は、難しそうなので、一旦消してから後で復活させるか (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/07/2023 1:08 AM
+ semantic_analysis.c
で、
+
+
+ 不正な加算を行うことはできません
+
+ というエラーが出たので、原因を調べる
+ (edited)
+
+
+
+
+
+
+ 01:09
+
+
+ ここで、配列全体へのポインタを実装する必要が出てくるのだと思われる
+
+
+
+
+
+
+
+
+
+ mikiken 03/07/2023 11:12 PM
+
+
+
+
+
+
+
+ 23:15
+
+
+ int arr[2][3]
がint [3]
の要素数2の配列型であることに注意する必要がある
+ (直感に反する)
+
+
+
+
+
+
+
+
+
+ mikiken 03/07/2023 11:27 PM
+ 多次元配列のパースの仕方をミスってたので、直す
+ 順方向に伸びるLinked Listとして実装すればOKなはず (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/07/2023 11:48 PM
+ 直せた
+
+
+
+
+
+
+
+
+
+ mikiken 03/08/2023 12:59 AM
+ 今までは、一次元配列であることを(暗黙に)仮定してたので、
+ case ND_ADD: {
Node *lhs = add_type_to_node(lvar_list, node->lhs);
Node *rhs = add_type_to_node(lvar_list, node->rhs);
// 左辺が配列の場合、ポインタにキャストする
@@ -830,24 +4152,177 @@
else {
error("不正な加算を行うことはできません");
}
- }
01:00の中で、
- // 左辺がポインタ型、右辺がint型の場合
+ }
+
+
+
+
+
+
+ 01:00
+
+
+ の中で、
+ // 左辺がポインタ型、右辺がint型の場合
if (lhs->type->kind == TYPE_PTR && rhs->type->kind == TYPE_INT) {
Node *size = new_size_node(lhs->type->ptr_to->kind);
Node *mul_scaling = new_typed_binary(new_typed_node(new_type(TYPE_INT), new_node(ND_MUL)), size, rhs);
return new_typed_binary(new_typed_node(lhs->type, node), lhs, mul_scaling);
}
-のようにキャストすることで、上手くいっていた01:01しかし、多次元配列の場合は、lhs->type->ptr_to
にint
のような基本型が来るとは限らない
-実際、二次元配列の場合は、lhs->type->ptr_to
にTYPE_ARRAY
が来ている (edited)01:03add_type_to_node
を再帰的に呼び出す際に、型情報も、1段深いものを渡す必要がある(?)01:08話が戻るけど、グローバル変数の初期化式、定数をベタ書きする記法だけサポートしておくのはありかもしれない
-mikiken 03/08/2023 11:37 AMやっていき11:40int arr[2][3]
という宣言があったとして、
-1回目の間接参照では、ND_DEREF
の型はint [3]
であるべきであり、
-2回目の間接参照では、ND_DEREF
の型はint
であるべきである
-ってことになるはず (edited)
-mikiken 03/08/2023 6:18 PM低レイヤを知りたい人のためのCコンパイラ作成入門を、Rustでコツコツとやっている。 www.sigbus.info 今まで苦労しながらもgithubのソースを見ながらまっすぐに来れたが、二次元配列の所で躓いたので整理してみた。 動かしたいソース まだここまでは動かないけど、やりたいことはこのCソースの演算部分を動かす事。二次元配列の宣言と、ポインターへの変換、デリファレンス。 流石にCの動きが分からないと言うことはないが、ちゃんと動くアセンブラを自力で出力するのには難しい。 #include int main() { int x [2][3]; int *y = x; i…
-mikiken 03/08/2023 6:28 PMint x[2][3];
という宣言に対して、int (*x_ptr)[3] = &x[2]
みたいなコードも動かないといけないはず(要検証)
-mikiken 03/08/2023 6:37 PMとりあえず、add_type_to_node
のND_DEREF
でlhs
の型を見て、その型が配列型である場合は、ptr_to
に入っている型情報を与えた上で、add_type_to_node
を再度呼び出せばOKそう? (edited)
-mikiken 03/11/2023 9:39 AM数日経過09:40そもそも、add_type_to_node
がデカすぎる説はあるな09:43この関数は、ただ機械的に型をつけるだけにして、その上で、後から型を修正したり、ポインタ演算用にnodeを追加した方が見通し良さそう (edited)
-mikiken 03/11/2023 10:10 AM// ND_DEREFで間接参照を行った場合、そのnodeの以下の型はnode->type->ptr_toとなるので、型を付け直す
+ のようにキャストすることで、上手くいっていた
+
+
+
+
+
+
+ 01:01
+
+
+ しかし、多次元配列の場合は、lhs->type->ptr_to
にint
のような基本型が来るとは限らない
+ 実際、二次元配列の場合は、lhs->type->ptr_to
にTYPE_ARRAY
が来ている (edited)
+
+
+
+
+
+
+ 01:03
+
+
+ add_type_to_node
を再帰的に呼び出す際に、型情報も、1段深いものを渡す必要がある(?)
+
+
+
+
+
+
+ 01:08
+
+
+ 話が戻るけど、グローバル変数の初期化式、定数をベタ書きする記法だけサポートしておくのはありかもしれない
+
+
+
+
+
+
+
+
+
+ mikiken 03/08/2023 11:37 AM
+ やっていき
+
+
+
+
+
+
+ 11:40
+
+
+ int arr[2][3]
という宣言があったとして、
+
+
+ 1回目の間接参照では、ND_DEREF
の型はint [3]
であるべきであり、
+ 2回目の間接参照では、ND_DEREF
の型はint
であるべきである
+
+ ってことになるはず
+ (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/08/2023 6:18 PM
+
+
+
+
+
+
+
+
+
+ 低レイヤを知りたい人のためのCコンパイラ作成入門を、Rustでコツコツとやっている。 www.sigbus.info 今まで苦労しながらもgithubのソースを見ながらまっすぐに来れたが、二次元配列の所で躓いたので整理してみた。 動かしたいソース まだここまでは動かないけど、やりたいことはこのCソースの演算部分を動かす事。二次元配列の宣言と、ポインターへの変換、デリファレンス。 流石にCの動きが分からないと言うことはないが、ちゃんと動くアセンブラを自力で出力するのには難しい。 #include int main() { int x [2][3]; int *y = x; i…
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 03/08/2023 6:28 PM
+ int x[2][3];
という宣言に対して、int (*x_ptr)[3] = &x[2]
みたいなコードも動かないといけないはず(要検証)
+
+
+
+
+
+
+
+
+
+ mikiken 03/08/2023 6:37 PM
+ とりあえず、add_type_to_node
のND_DEREF
でlhs
の型を見て、その型が配列型である場合は、ptr_to
に入っている型情報を与えた上で、add_type_to_node
を再度呼び出せばOKそう? (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/11/2023 9:39 AM
+ 数日経過
+
+
+
+
+
+
+ 09:40
+
+
+ そもそも、add_type_to_node
がデカすぎる説はあるな
+
+
+
+
+
+
+ 09:43
+
+
+ この関数は、ただ機械的に型をつけるだけにして、その上で、後から型を修正したり、ポインタ演算用にnodeを追加した方が見通し良さそう (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/11/2023 10:10 AM
+ // ND_DEREFで間接参照を行った場合、そのnodeの以下の型はnode->type->ptr_toとなるので、型を付け直す
Node *fix_node_type_to_ptr_to(Node *node, Type *node_type) {
switch (node->kind) {
case ND_DEREF:
@@ -856,9 +4331,19 @@
default:
return node;
}
-}
-mikiken 03/12/2023 1:00 AMテンプレを書いた
-Node *add_type_to_node(Node *node) {
+}
+
+
+
+
+
+
+
+
+
+ mikiken 03/12/2023 1:00 AM
+ テンプレを書いた
+ Node *add_type_to_node(Node *node) {
switch (node->kind) {
case ND_STMT:
return;
@@ -919,50 +4404,432 @@
case ND_SIZEOF:
return;
}
-}
-mikiken 03/12/2023 9:46 PM(作り直し中の)add_type_to_node
にND_GVAR
があるが、これはあくまで関数のスコープの中でグローバル変数を読んだ場合であって、トップレベルにグローバル変数に宣言を書いた場合ではない21:46(そもそもグローバル変数の初期化式を実装していない以上、それはそう)
-mikiken 03/17/2023 4:37 PMさらに数日経過16:38とりあえず、add_type_to_node
では機械的に型を付けるだけにした16:39配列をその先頭要素へのポインタにキャストする処理や、ポインタの加算減算で、オフセットをsizeof(lhs->type->ptr_to)
倍する処理は、別の関数で行うことにした16:41なんか、改めて意味解析の処理を見返すと、型の扱い方合ってる?って思う箇所があるけど、とりあえず気にせずに分割していく (edited)
-mikiken 03/22/2023 4:45 PMとりあえず関数を分割したが、semantic_analysis() : 代入式の両辺の型が異なります
が出るな16:47どこでエラーが出てるかというと、test/array.c
のarray_test1
の代入式*p=1
の部分 (edited)16:49int array_test1() {
+}
+
+
+
+
+
+
+
+
+
+ mikiken 03/12/2023 9:46 PM
+ (作り直し中の)add_type_to_node
にND_GVAR
があるが、これはあくまで関数のスコープの中でグローバル変数を読んだ場合であって、トップレベルにグローバル変数に宣言を書いた場合ではない
+
+
+
+
+
+
+ 21:46
+
+
+ (そもそもグローバル変数の初期化式を実装していない以上、それはそう)
+
+
+
+
+
+
+
+
+
+ mikiken 03/17/2023 4:37 PM
+ さらに数日経過
+
+
+
+
+
+
+ 16:38
+
+
+ とりあえず、add_type_to_node
では機械的に型を付けるだけにした
+
+
+
+
+
+
+ 16:39
+
+
+ 配列をその先頭要素へのポインタにキャストする処理や、ポインタの加算減算で、オフセットをsizeof(lhs->type->ptr_to)
倍する処理は、別の関数で行うことにした
+
+
+
+
+
+
+ 16:41
+
+
+ なんか、改めて意味解析の処理を見返すと、型の扱い方合ってる?って思う箇所があるけど、とりあえず気にせずに分割していく (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/22/2023 4:45 PM
+ とりあえず関数を分割したが、semantic_analysis() : 代入式の両辺の型が異なります
が出るな
+
+
+
+
+
+
+ 16:47
+
+
+ どこでエラーが出てるかというと、test/array.c
のarray_test1
の代入式*p=1
の部分 (edited)
+
+
+
+
+
+
+ 16:49
+
+
+ int array_test1() {
int a[2];
int *p;
p = a;
*p = 1;
*(p + 1) = 2;
return *p + *(p + 1);
-}
16:51ND_DEREF
自体の型は、lhs->type->ptr_to
にしないといけないのに、そうなってなかったので修正した (edited)
-mikiken 03/22/2023 5:23 PMそれはそうとして、大量のSegmentation fault
とsemantic_analysis() : 代入式の両辺の型が異なります
が出ているのが気になる17:24arith.c
ですらセグフォしてるので原因を調査する17:29codegen.c
で関数呼び出しの引数の型を参照しようとしたところ、ND_FUNCALL
のbody->type
に型情報が入っておらずエラーになっているらしい17:32semantic_analysis()
を確認したところ、普通にcase ND_FUNCALL:
の実装忘れてたわ (edited)
-mikiken 03/22/2023 5:52 PM実装した
-mikiken 03/22/2023 6:06 PMarith.c
を9ccでコンパイルすると、 assert(21, 5 + 20 - 4, "5 + 20 - 4");
のテストケースでコケてる18:07そもそもND_NUM
に型情報が入っていないっぽい
-mikiken 03/23/2023 10:42 PMadd_type_to_node
とsemantic_analysis
で、for文でarg->body
に対して処理を行うべきところがarg
に対して処理を行ってたのが原因22:43これで、arith.c
のコンパイルは通るようになった22:43次に、array.c
のコンパイルを試みると、やはりセグフォ22:4722:49とりあえず、代入式に型情報が入っていないことが分かる22:53確かにsemantic_analysis()
のND_ASSIGN
のところを見ると、node自体の型を指定し忘れてるな
-mikiken 03/24/2023 12:00 PM左辺の型に合わせるようにした
-mikiken 03/24/2023 12:25 PMなんかcodegen.c
でメモリ上の値をレジスタにセットしようとしたときに配列型が登場してエラーになってる12:25どのテストケースでコケてるんだろ (edited)
-mikiken 03/24/2023 3:38 PM構文木はこんな感じ
-mikiken 03/24/2023 4:03 PM該当するテストケース無くね?
-(追記) callstackを確認したらarray_test13()
でコケてることが判明 (edited)16:06parse.c
は特に触ってないから、意味解析のところで構文木を構築するのをミスってそう
-mikiken 03/26/2023 4:12 PM現時点で
-arith.c
+}
+
+
+
+
+
+
+ 16:51
+
+
+ ND_DEREF
自体の型は、lhs->type->ptr_to
にしないといけないのに、そうなってなかったので修正した (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/22/2023 5:23 PM
+ それはそうとして、大量のSegmentation fault
とsemantic_analysis() : 代入式の両辺の型が異なります
が出ているのが気になる
+
+
+
+
+
+
+ 17:24
+
+
+ arith.c
ですらセグフォしてるので原因を調査する
+
+
+
+
+
+
+ 17:29
+
+
+ codegen.c
で関数呼び出しの引数の型を参照しようとしたところ、ND_FUNCALL
のbody->type
に型情報が入っておらずエラーになっているらしい
+
+
+
+
+
+
+ 17:32
+
+
+ semantic_analysis()
を確認したところ、普通にcase ND_FUNCALL:
の実装忘れてたわ (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/22/2023 5:52 PM
+ 実装した
+
+
+
+
+
+
+
+
+
+ mikiken 03/22/2023 6:06 PM
+ arith.c
を9ccでコンパイルすると、 assert(21, 5 + 20 - 4, "5 + 20 - 4");
のテストケースでコケてる
+
+
+
+
+
+
+ 18:07
+
+
+ そもそもND_NUM
に型情報が入っていないっぽい
+
+
+
+
+
+
+
+
+
+ mikiken 03/23/2023 10:42 PM
+ add_type_to_node
とsemantic_analysis
で、for文でarg->body
に対して処理を行うべきところがarg
に対して処理を行ってたのが原因
+
+
+
+
+
+
+ 22:43
+
+
+ これで、arith.c
のコンパイルは通るようになった
+
+
+
+
+
+
+ 22:43
+
+
+ 次に、array.c
のコンパイルを試みると、やはりセグフォ
+
+
+
+
+
+
+ 22:47
+
+
+
+
+
+
+
+
+
+ 22:49
+
+
+ とりあえず、代入式に型情報が入っていないことが分かる
+
+
+
+
+
+
+ 22:53
+
+
+ 確かにsemantic_analysis()
のND_ASSIGN
のところを見ると、node自体の型を指定し忘れてるな
+
+
+
+
+
+
+
+
+
+ mikiken 03/24/2023 12:00 PM
+ 左辺の型に合わせるようにした
+
+
+
+
+
+
+
+
+
+ mikiken 03/24/2023 12:25 PM
+ なんかcodegen.c
でメモリ上の値をレジスタにセットしようとしたときに配列型が登場してエラーになってる
+
+
+
+
+
+
+ 12:25
+
+
+ どのテストケースでコケてるんだろ (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/24/2023 3:38 PM
+ 構文木はこんな感じ
+
+
+
+
+
+
+
+
+
+
+ mikiken 03/24/2023 4:03 PM
+ 該当するテストケース無くね?
+ (追記) callstackを確認したらarray_test13()
でコケてることが判明 (edited)
+
+
+
+
+
+
+ 16:06
+
+
+ parse.c
は特に触ってないから、意味解析のところで構文木を構築するのをミスってそう
+
+
+
+
+
+
+
+
+
+ mikiken 03/26/2023 4:12 PM
+ 現時点で
+ arith.c
array.c
control_statement.c
function.c
string.c
test.c
variable.c
-はコンパイル可能 (edited)16:12一方で、pointer.c
はコンパイルエラーになる (edited)
-mikiken 該当するテストケース無くね?
-(追記) callstackを確認したらarray_test13()
でコケてることが判明 (edited)mikiken 03/26/2023 4:38 PMVSCodeのgdb拡張(?)、めっちゃ便利 (edited)16:39int array_test13() {
+ はコンパイル可能
(edited)
+
+
+
+
+
+
+ 16:12
+
+
+ 一方で、pointer.c
はコンパイルエラーになる (edited)
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken
+ 該当するテストケース無くね?
+ (追記) callstackを確認したらarray_test13()
でコケてることが判明 (edited)
+
+ mikiken 03/26/2023 4:38 PM
+ VSCodeのgdb拡張(?)、めっちゃ便利 (edited)
+
+
+
+
+
+
+
+ 16:39
+
+
+ int array_test13() {
int a[5] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++)
sum += a[i];
return sum;
-}
16:42この関数のsum += a[i]
のところで型の不一致が起きてる16:46右辺の配列a
がポインタにキャストされてないのが原因っぽい
-mikiken 03/26/2023 4:55 PMsemantic_analysis()
でND_FOR
のnode->then
の構文木の意味解析をし忘れてるのが原因17:01 case ND_FOR:
+}
+
+
+
+
+
+
+ 16:42
+
+
+ この関数のsum += a[i]
のところで型の不一致が起きてる
+
+
+
+
+
+
+ 16:46
+
+
+ 右辺の配列a
がポインタにキャストされてないのが原因っぽい
+
+
+
+
+
+
+
+
+
+ mikiken 03/26/2023 4:55 PM
+ semantic_analysis()
でND_FOR
のnode->then
の構文木の意味解析をし忘れてるのが原因
+
+
+
+
+
+
+ 17:01
+
+
+ case ND_FOR:
if (node->init)
semantic_analysis(node->init);
if (node->cond)
@@ -975,15 +4842,65 @@
if (node->type->kind == TYPE_NULL)
return;
else
- error("semantic_analysis() : if文の意味解析を行うことができませんでした");
17:01真ん中に1行追加したらarray.c
のコンパイルが通るようになった (edited)17:05あとpointer.c
を通せば、移植完了しそう17:06pointer.c
のpointer_test12()
でコケてる
-mikiken 03/26/2023 5:32 PMint pointer_test12() {
+ error("semantic_analysis() : if文の意味解析を行うことができませんでした");
+
+
+
+
+
+
+ 17:01
+
+
+ 真ん中に1行追加したらarray.c
のコンパイルが通るようになった (edited)
+
+
+
+
+
+
+ 17:05
+
+
+ あとpointer.c
を通せば、移植完了しそう
+
+
+
+
+
+
+ 17:06
+
+
+ pointer.c
のpointer_test12()
でコケてる
+
+
+
+
+
+
+
+
+
+ mikiken 03/26/2023 5:32 PM
+ int pointer_test12() {
int a[2];
int k;
k = &a[1] - &a[0];
return k;
-}
-mikiken 03/26/2023 6:04 PMND_SUB
でポインタ同士の引き算をいい感じに意味解析するところのコード
- // 両辺がポインタ型の場合
+}
+
+
+
+
+
+
+
+
+
+ mikiken 03/26/2023 6:04 PM
+ ND_SUB
でポインタ同士の引き算をいい感じに意味解析するところのコード
+ // 両辺がポインタ型の場合
else if (is_ptr_type(node->lhs->type) && is_ptr_type(node->rhs->type)) {
if (node->lhs->type->ptr_to->kind != node->rhs->type->ptr_to->kind)
error("semantic_analysis() : 異なる型へのポインタ同士で減算を行うことはできません");
@@ -991,14 +4908,169 @@
Node *diff_addr = new_typed_binary(new_typed_node(node->lhs->type, new_node(ND_SUB)), node->lhs, node->rhs);
*node = *new_typed_binary(new_typed_node(new_type(TYPE_INT), new_node(ND_DIV)), diff_addr, size);
return;
- }
(edited)18:04node = new_typed_binary(~);
となっていたが、これではND_ASSIGN
のnode->rhs
の参照先は古い構文木のままになってしまう。そこで、*node = *new_typed_binary(~);
として、Node *node
の参照先自体を新しい構文木に書き換える必要がある。 (edited)
-mikiken 03/28/2023 12:49 PMこれで、pointer.c
のコンパイルも通るようになった12:50make test
すると、string.c
でエラーが出るな12:51[FAIL] {char x[3]; x[0] = -1; x[1] = 2; int y; y = 4; return x[0] + y;} => 3 expected, but got -253
-mikiken 03/28/2023 12:58 PMgdbでchar_type_test1()
からreturnした直後のraxの値を観察すると何か分かりそう
-
-mikiken 03/31/2023 4:07 PMたぶん元々raxのビットがすべて立ってる状態0xffffffff
から、raxの下位1byteのみ書き換えられた結果、0xffffff03
というビットパターンになった (edited)16:09なのでrax
のビットパターンの下位1byteのみを読むと、ちゃんと3
という値になる16:10一方で、rax
のビットパターンを(signed) int
として読むと、0xff03
となるので、-253
という値になる16:10ってことだと思う
-mikiken 03/31/2023 5:23 PMとりあえず、古い実装が吐くアセンブリと新しい実装が吐くアセンブリの差分を取ってみた17:248.9 KB17:29少なくとも、表面的に出力されるアセンブリを比較すると、古い方ではきちんとrdi
の下位1byteを読んでいる一方、新しい方ではrdi
の下位4byteを読んでしまっていることが分かる
-mikiken 03/31/2023 6:18 PMテストケースがデカいので、バグが再現するもう少し小さいテストケースを考える
-int printf();
+ }
(edited)
+
+
+
+
+
+
+ 18:04
+
+
+ node = new_typed_binary(~);
となっていたが、これではND_ASSIGN
のnode->rhs
の参照先は古い構文木のままになってしまう。そこで、*node = *new_typed_binary(~);
として、Node *node
の参照先自体を新しい構文木に書き換える必要がある。 (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 03/28/2023 12:49 PM
+ これで、pointer.c
のコンパイルも通るようになった
+
+
+
+
+
+
+ 12:50
+
+
+ make test
すると、string.c
でエラーが出るな
+
+
+
+
+
+
+ 12:51
+
+
+ [FAIL] {char x[3]; x[0] = -1; x[1] = 2; int y; y = 4; return x[0] + y;} => 3 expected, but got -253
+
+
+
+
+
+
+
+
+
+ mikiken 03/28/2023 12:58 PM
+ gdbでchar_type_test1()
からreturnした直後のraxの値を観察すると何か分かりそう
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 03/31/2023 4:07 PM
+ たぶん元々raxのビットがすべて立ってる状態0xffffffff
から、raxの下位1byteのみ書き換えられた結果、0xffffff03
というビットパターンになった (edited)
+
+
+
+
+
+
+ 16:09
+
+
+ なのでrax
のビットパターンの下位1byteのみを読むと、ちゃんと3
という値になる
+
+
+
+
+
+
+ 16:10
+
+
+ 一方で、rax
のビットパターンを(signed) int
として読むと、0xff03
となるので、-253
という値になる
+
+
+
+
+
+
+ 16:10
+
+
+ ってことだと思う
+
+
+
+
+
+
+
+
+
+ mikiken 03/31/2023 5:23 PM
+ とりあえず、古い実装が吐くアセンブリと新しい実装が吐くアセンブリの差分を取ってみた
+
+
+
+
+
+
+ 17:24
+
+
+
+
+
+ 8.9 KB
+
+
+
+
+
+
+
+
+ 17:29
+
+
+ 少なくとも、表面的に出力されるアセンブリを比較すると、古い方ではきちんとrdi
の下位1byteを読んでいる一方、新しい方ではrdi
の下位4byteを読んでしまっていることが分かる
+
+
+
+
+
+
+
+
+
+ mikiken 03/31/2023 6:18 PM
+ テストケースがデカいので、バグが再現するもう少し小さいテストケースを考える
+ int printf();
char char_type_test1() {
char x = -1;
@@ -1009,10 +5081,87 @@
int main() {
printf("%d\n", char_type_test1());
return 0;
-}
18:226.56 KB
-mikiken 03/31/2023 7:02 PMとりあえず、算術型の加減算のnode自体の型を全てint
にすることで、ひとまず動くようになった
-
-mikiken 03/31/2023 11:51 PMgit rebase -i
してたらconflict起きた23:52mikiken@DESKTOP-CM4259U:~/compiler/9cc$ git rebase -i HEAD~25
+}
+
+
+
+
+
+
+ 18:22
+
+
+
+
+
+ 6.56 KB
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 03/31/2023 7:02 PM
+ とりあえず、算術型の加減算のnode自体の型を全てint
にすることで、ひとまず動くようになった
+
+
+
+
+
+
+
+
+
+ mikiken 03/31/2023 7:13 PM
+ origin/master
とmaster
の乖離がすごいことに
+
+
+
+
+
+
+
+ 19:13
+
+
+ さすがにコミットログが汚いので、きれいにしたい
+
+
+
+
+
+
+
+
+
+ mikiken 03/31/2023 11:51 PM
+ git rebase -i
してたらconflict起きた
+
+
+
+
+
+
+ 23:52
+
+
+ mikiken@DESKTOP-CM4259U:~/compiler/9cc$ git rebase -i HEAD~25
Auto-merging src/semantic_analysis.c
CONFLICT (content): Merge conflict in src/semantic_analysis.c
error: could not apply 59f3168... `ND_ADD`,`ND_SUB`でnodeそのものの型をちゃんとした
@@ -1020,43 +5169,282 @@
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
-Could not apply 59f3168... `ND_ADD`,`ND_SUB`でnodeそのものの型をちゃんとした
-
-mikiken 04/02/2023 5:49 PMというわけでようやく多次元配列の実装に着手していく17:51int arr[3][5];
みたいな宣言に対して、arr[2][4] = 3;
と書いたら、*(*(arr + 2) + 4) = 3;
とパースするようには既になってるはず17:52とりあえずコンパイルを試みると、semantic_analysis() : 不正な加算を行うことはできません
というエラーが出るので、semantic_analysis()
のND_ADD
で両辺の型の不一致が起こっていそう
-mikiken 04/02/2023 10:00 PM
-mikiken 04/03/2023 3:27 PMまず試しているテストケースはこれ
-int main() {
+Could not apply 59f3168... `ND_ADD`,`ND_SUB`でnodeそのものの型をちゃんとした
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 04/02/2023 5:49 PM
+ というわけでようやく多次元配列の実装に着手していく
+
+
+
+
+
+
+ 17:51
+
+
+ int arr[3][5];
みたいな宣言に対して、arr[2][4] = 3;
と書いたら、*(*(arr + 2) + 4) = 3;
とパースするようには既になってるはず
+
+
+
+
+
+
+ 17:52
+
+
+ とりあえずコンパイルを試みると、semantic_analysis() : 不正な加算を行うことはできません
というエラーが出るので、semantic_analysis()
のND_ADD
で両辺の型の不一致が起こっていそう
+
+
+
+
+
+
+
+
+
+ mikiken 04/02/2023 10:00 PM
+
+
+
+
+
+
+
+
+
+
+ mikiken 04/03/2023 3:27 PM
+ まず試しているテストケースはこれ
+ int main() {
int arr[3][5];
*(*(arr + 2) + 4) = 3;
return *(*(arr + 2) + 4);
-}
15:27何も考えずに、./9cc a.c
したところ、semantic_analysis() : 不正な加算を行うことはできません
が出た15:28それはそう
-(semantic_analysis()
において、左辺に配列型、右辺がint
型の場合の処理が入っていないので) (edited)15:30ということで、ND_ADD
の処理で、左辺に配列型、右辺にint
型が来た場合、サイズ調整用のnodeを付加するようなコードを追加した
- // 左辺が配列型、右辺がint型の場合
+}
+
+
+
+
+
+
+ 15:27
+
+
+ 何も考えずに、./9cc a.c
したところ、semantic_analysis() : 不正な加算を行うことはできません
が出た
+
+
+
+
+
+
+ 15:28
+
+
+ それはそう
+ (semantic_analysis()
において、左辺に配列型、右辺がint
型の場合の処理が入っていないので) (edited)
+
+
+
+
+
+
+ 15:30
+
+
+ ということで、ND_ADD
の処理で、左辺に配列型、右辺にint
型が来た場合、サイズ調整用のnodeを付加するようなコードを追加した
+ // 左辺が配列型、右辺がint型の場合
if (is_array_type(node->lhs->type) && node->rhs->type->kind == TYPE_INT) {
// node自体の型は左辺の型に合わせる
node->type = node->lhs->type;
Node *size_node = new_size_node(node->lhs->type->ptr_to);
node->rhs = new_typed_binary(new_typed_node(new_type(TYPE_INT), new_node(ND_MUL)), size_node, node->rhs);
return;
- }
15:30再び./9cc a.c
すると、semantic_analysis() : ポインタでないものを間接参照することはできません
が出た15:32今までは1次元配列しかなかったので、ND_DEREF
のlhs
には必ず(配列型がキャストされたことで生じた)ポインタ型が来ていたが、その前提が崩れた15:32ということで、ひとまずコメントアウトしといた15:33再びコンパイルを試みると、一部アセンブリが出力されるものの、途中でcodegen.c
においてレジスタに値をセットできませんでした
が出てるな
-mikiken 04/03/2023 7:48 PMとりあえずgdbを走らせて確認したところ、内側のND_DEREF
でレジスタに入っているアドレスを参照しようとしたところ、mov_memory_to_register
に配列型の扱いがないため、エラーが出ているっぽい
- case ND_DEREF:
+ }
+
+
+
+
+
+
+ 15:30
+
+
+ 再び./9cc a.c
すると、semantic_analysis() : ポインタでないものを間接参照することはできません
が出た
+
+
+
+
+
+
+ 15:32
+
+
+ 今までは1次元配列しかなかったので、ND_DEREF
のlhs
には必ず(配列型がキャストされたことで生じた)ポインタ型が来ていたが、その前提が崩れた
+
+
+
+
+
+
+ 15:32
+
+
+ ということで、ひとまずコメントアウトしといた
+
+
+
+
+
+
+ 15:33
+
+
+ 再びコンパイルを試みると、一部アセンブリが出力されるものの、途中でcodegen.c
においてレジスタに値をセットできませんでした
が出てるな
+
+
+
+
+
+
+
+
+
+ mikiken 04/03/2023 7:48 PM
+ とりあえずgdbを走らせて確認したところ、内側のND_DEREF
でレジスタに入っているアドレスを参照しようとしたところ、mov_memory_to_register
に配列型の扱いがないため、エラーが出ているっぽい
+ case ND_DEREF:
gen_expr(node->lhs);
pop_addr(RDI);
mov_memory_to_register(node->type, RAX, RDI); // この関数で「レジスタに値をセットできませんでした」が出てる
push(node->type, RAX);
- return;
19:50ND_DEREF
のlhs
の型が、配列型(=ポインタ型ではない)である場合、gen_expr(node->lhs);
で生成されたアドレスの中身を参照する処理を飛ばさなければいけないのでは? (edited)
-14:29現在は、例えばND_DEREF
などで左辺値と右辺値を区別せずにコード生成をしているが、左辺値としてアドレスを生成した場合は、アドレスの指す先をメモリから読む処理を飛ばすようにしないとダメそう (edited)14:29左辺値と右辺値で関数分けた方がいいかもしれない
-mikiken 04/07/2023 5:37 PMということで、関数gen_expr()
の分割を開始 (edited)17:38まずは二項演算子のコード生成の処理をgen_binary_operator_expr()
という別関数に切り出した、これは簡単17:39そういえば、READMEのBNF全然更新してないなあ17:42a = b = 3;
みたいな式を書いた場合、
- =
+ return;
+
+
+
+
+
+
+ 19:50
+
+
+ ND_DEREF
のlhs
の型が、配列型(=ポインタ型ではない)である場合、gen_expr(node->lhs);
で生成されたアドレスの中身を参照する処理を飛ばさなければいけないのでは? (edited)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 14:29
+
+
+ 現在は、例えばND_DEREF
などで左辺値と右辺値を区別せずにコード生成をしているが、左辺値としてアドレスを生成した場合は、アドレスの指す先をメモリから読む処理を飛ばすようにしないとダメそう (edited)
+
+
+
+
+
+
+ 14:29
+
+
+ 左辺値と右辺値で関数分けた方がいいかもしれない
+
+
+
+
+
+
+
+
+
+ mikiken 04/07/2023 5:37 PM
+ ということで、関数gen_expr()
の分割を開始 (edited)
+
+
+
+
+
+
+ 17:38
+
+
+ まずは二項演算子のコード生成の処理をgen_binary_operator_expr()
という別関数に切り出した、これは簡単
+
+
+
+
+
+
+ 17:39
+
+
+ そういえば、READMEのBNF全然更新してないなあ
+
+
+
+
+
+
+ 17:42
+
+
+ a = b = 3;
みたいな式を書いた場合、
+ =
/ \
a =
/ \
b 3
-という構文木になる
-mikiken 04/12/2023 6:36 PMまず、gen_expr()
をこんな感じにした
-void gen_expr(Node *node) {
+ という構文木になる
+
+
+
+
+
+
+
+
+
+ mikiken 04/12/2023 6:36 PM
+ まず、gen_expr()
をこんな感じにした
+ void gen_expr(Node *node) {
switch (node->kind) {
case ND_ASSIGN:
gen_addr(node->lhs); // 左辺のアドレスをスタックにpush
@@ -1070,8 +5458,28 @@
gen_expr_as_rvalue(node);
return;
}
-}
18:36ND_ASSIGN
の場合は特別視し、それ以外は全て右辺値のコード生成として扱う18:37そして、gen_addr()
がこんな感じになってる
-void gen_addr(Node *node) {
+}
+
+
+
+
+
+
+ 18:36
+
+
+ ND_ASSIGN
の場合は特別視し、それ以外は全て右辺値のコード生成として扱う
+
+
+
+
+
+
+ 18:37
+
+
+ そして、gen_addr()
がこんな感じになってる
+ void gen_addr(Node *node) {
switch (node->kind) {
case ND_LVAR:
printf(" lea rdi, [rbp-%d]\n", node->offset); // アドレスは8byte
@@ -1092,19 +5500,89 @@
error("nodeのアドレスを生成することができません");
return;
}
-}
(edited)18:38これで、gen_expr_as_lvalue
の中を書いていけば、いい感じになりそう18:42現状gen_expr_as_lvalue()
のcallerはND_DEREF
のはずなので、例えばND_NUM
の処理は削っても大丈夫そう(?) (edited)
-mikiken 04/12/2023 6:54 PMメモ gen_expr_as_lvalue
の中身を検討
-mikiken 04/12/2023 7:36 PMこういうコードは合法
-int main() {
+}
(edited)
+
+
+
+
+
+
+ 18:38
+
+
+ これで、gen_expr_as_lvalue
の中を書いていけば、いい感じになりそう
+
+
+
+
+
+
+ 18:42
+
+
+ 現状gen_expr_as_lvalue()
のcallerはND_DEREF
のはずなので、例えばND_NUM
の処理は削っても大丈夫そう(?) (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 04/12/2023 6:54 PM
+ メモ gen_expr_as_lvalue
の中身を検討
+
+
+
+
+
+
+
+
+
+ mikiken 04/12/2023 7:36 PM
+ こういうコードは合法
+ int main() {
int arr[2];
int *p;
*(p = arr) = 3;
return arr[0];
-}
-mikiken 04/12/2023 8:06 PMとりあえず、いい感じに関数分割できたっぽいんだが、make test
したら
-[FAIL] {int x; int *y; x=3; y=&x; *y=5; return x; } => 5 expected, but got 3
-になった
-mikiken 04/14/2023 7:30 AMgdbで見た感じ、*y = 5;
の処理がうまくいってない07:31gdb-peda$ c
+}
+
+
+
+
+
+
+
+
+
+ mikiken 04/12/2023 8:06 PM
+ とりあえず、いい感じに関数分割できたっぽいんだが、make test
したら
+ [FAIL] {int x; int *y; x=3; y=&x; *y=5; return x; } => 5 expected, but got 3
+ になった
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 7:30 AM
+ gdbで見た感じ、*y = 5;
の処理がうまくいってない
+
+
+
+
+
+
+ 07:31
+
+
+ gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
RAX: 0x5
@@ -1149,9 +5627,39 @@
Breakpoint 2, main () at tmp.s:37
37 mov DWORD PTR [rdi], eax # ここでおかしくなった
gdb-peda$ p/d (int)*0x7fffffffdc6c
-$1 = 3
07:32この状態で、本来ならrdiにx
のアドレス(0x7fffffffdc6c
)がストアされてないとおかしいはず07:33おそらく間接参照のコードがうまく生成できてない
-mikiken 04/14/2023 8:44 AMgen_addr()
のND_DEREF
のところに数行追加したら動いた
-void gen_addr(Node *node) {
+$1 = 3
+
+
+
+
+
+
+ 07:32
+
+
+ この状態で、本来ならrdiにx
のアドレス(0x7fffffffdc6c
)がストアされてないとおかしいはず
+
+
+
+
+
+
+ 07:33
+
+
+ おそらく間接参照のコードがうまく生成できてない
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 8:44 AM
+ gen_addr()
のND_DEREF
のところに数行追加したら動いた
+ void gen_addr(Node *node) {
switch (node->kind) {
case ND_LVAR:
printf(" lea rdi, [rbp-%d]\n", node->offset); // アドレスは8byte
@@ -1175,31 +5683,274 @@
error("nodeのアドレスを生成することができません");
return;
}
-}
(edited)
-mikiken 04/14/2023 10:54 AM続いてmake test
すると、このテストケースでセグフォした
-int global_variable1;
+}
(edited)
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 10:54 AM
+ 続いてmake test
すると、このテストケースでセグフォした
+ int global_variable1;
int global_variable2[20];
int global_variable_test3() {
global_variable1 = 3;
global_variable2[2] = 2;
return global_variable1 + global_variable2[2];
-}
-mikiken 04/14/2023 11:40 AMグローバル変数の実装も、gccの出力を真似してるだけで中身理解してないので、今調べよう11:42グローバル変数を扱うアセンブリを書いているときに、最初よく分からずに非PIEでのコードを書いてしまっていた。
-最近のLinuxディストリビューションにインストールされているGCCはデフォルトでPIEとしてリンクしようとするため、非PI...
-mikiken 04/14/2023 12:30 PM.LC0:
- .string "hello"
+}
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 11:40 AM
+ グローバル変数の実装も、gccの出力を真似してるだけで中身理解してないので、今調べよう
+
+
+
+
+
+
+ 11:42
+
+
+
+
+
+
+
+
+
+
+ グローバル変数を扱うアセンブリを書いているときに、最初よく分からずに非PIEでのコードを書いてしまっていた。
+ 最近のLinuxディストリビューションにインストールされているGCCはデフォルトでPIEとしてリンクしようとするため、非PI...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 12:30 PM
+
+
+
+
+
+
+
+
+ .LC0:
+ .string "hello"
-main:
- 〜〜〜
- lea rax, .LC0[rip]
+ main:
+ 〜〜〜
+ lea rax, .LC0[rip]
-が上手く行かなくて死亡してる Twitter • 07/26/2020 4:10 PM 12:30int x;
みたいな宣言に対して、従来はmov rax, OFFSET FLAT:x
のように書くことでグローバル変数のアドレスをレジスタにストアしていた
-mikiken 04/14/2023 12:42 PMこのように変数のアドレスを決め打ちする記法は、プログラムの移植性・セキュリティ上の問題から、現在ではあまり使われなくなっている (edited)
-mikiken 04/14/2023 12:52 PMそこで、アドレスを決め打ちせず、プログラムがメモリ上のどのアドレスに配置されても動作するような実行形式として、Position-Independent Executable という形式が使われている12:55このページにある図
-https://tanakamura.github.io/pllp/docs/pic_pie.html (edited)13:02このようにプログラムカウンタからのオフセットでアドレスを指定する方式を、PC相対アドレッシングという13:03さっきの例でいくと、lea rdi, x[rip]
のように書くとリンカがグローバル変数x
とrip
とのオフセットを自動計算してくれるということらしい (edited)13:07ということでセグフォの原因を探っていく13:07上のテストケースで、配列の要素に代入しているところglobal_variable2[2] = 2;
でセグフォしてる13:08さっきからの流れでいくと、間接参照の実装がまずそう
-mikiken 04/14/2023 1:40 PMさっきgen_addr()
に追加した処理によって、node->lhs
が変数以外の場合も、アドレスの指す先を参照しようとした結果セグフォしてるっぽい13:41なので、さっき追加した処理が発動するのを、node->lhs
に変数が来た場合に限定すれば良さそう
-mikiken 04/14/2023 3:12 PMこのテストケースは通るようになった15:12次は、このテストケースでセグフォしてる15:13int alloc4_array[5];
+ が上手く行かなくて死亡してる
+
+
+
+ Twitter • 07/26/2020 4:10 PM
+
+
+
+
+
+
+
+
+ 12:30
+
+
+ int x;
みたいな宣言に対して、従来はmov rax, OFFSET FLAT:x
のように書くことでグローバル変数のアドレスをレジスタにストアしていた
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 12:42 PM
+ このように変数のアドレスを決め打ちする記法は、プログラムの移植性・セキュリティ上の問題から、現在ではあまり使われなくなっている (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 12:52 PM
+ そこで、アドレスを決め打ちせず、プログラムがメモリ上のどのアドレスに配置されても動作するような実行形式として、Position-Independent Executable という形式が使われている
+
+
+
+
+
+
+ 12:54
+
+
+
+
+
+
+
+ 12:55
+
+
+ このページにある図
+ https://tanakamura.github.io/pllp/docs/pic_pie.html (edited)
+
+
+
+
+
+
+
+ 13:02
+
+
+ このようにプログラムカウンタからのオフセットでアドレスを指定する方式を、PC相対アドレッシングという
+
+
+
+
+
+
+ 13:03
+
+
+ さっきの例でいくと、lea rdi, x[rip]
のように書くとリンカがグローバル変数x
とrip
とのオフセットを自動計算してくれるということらしい (edited)
+
+
+
+
+
+
+ 13:07
+
+
+ ということでセグフォの原因を探っていく
+
+
+
+
+
+
+ 13:07
+
+
+ 上のテストケースで、配列の要素に代入しているところglobal_variable2[2] = 2;
でセグフォしてる
+
+
+
+
+
+
+ 13:08
+
+
+ さっきからの流れでいくと、間接参照の実装がまずそう
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 1:40 PM
+ さっきgen_addr()
に追加した処理によって、node->lhs
が変数以外の場合も、アドレスの指す先を参照しようとした結果セグフォしてるっぽい
+
+
+
+
+
+
+ 13:41
+
+
+ なので、さっき追加した処理が発動するのを、node->lhs
に変数が来た場合に限定すれば良さそう
+
+
+
+
+
+
+
+
+
+ mikiken 04/14/2023 3:12 PM
+ このテストケースは通るようになった
+
+
+
+
+
+
+ 15:12
+
+
+ 次は、このテストケースでセグフォしてる
+
+
+
+
+
+
+ 15:13
+
+
+ int alloc4_array[5];
int alloc4(int **p, int a, int b, int c, int d) {
*p = alloc4_array;
@@ -1217,7 +5968,37 @@
int *q;
q = p + 2;
return *q;
-}
15:13gdbを走らせたが、どこでセグフォしたかが検出できない15:13ということで、バグが再現しそうな小さいテストケースを作った15:14int arr[3];
+}
+
+
+
+
+
+
+ 15:13
+
+
+ gdbを走らせたが、どこでセグフォしたかが検出できない
+
+
+
+
+
+
+ 15:13
+
+
+ ということで、バグが再現しそうな小さいテストケースを作った
+
+
+
+
+
+
+ 15:14
+
+
+ int arr[3];
int alloc(int **p) {
*p = arr;
@@ -1228,9 +6009,49 @@
int main() {
int *p;
return alloc(&p);
-}
15:14ちゃんとセグフォしてくれた
-mikiken 04/15/2023 11:00 PM23:01(*p)[1] = 3;
のコード生成がうまくいってない23:02該当するアセンブリ
- lea rdi, [rbp-8]
+}
+
+
+
+
+
+
+ 15:14
+
+
+ ちゃんとセグフォしてくれた
+
+
+
+
+
+
+
+
+
+ mikiken 04/15/2023 11:00 PM
+
+
+
+
+
+
+
+ 23:01
+
+
+ (*p)[1] = 3;
のコード生成がうまくいってない
+
+
+
+
+
+
+ 23:02
+
+
+ 該当するアセンブリ
+ lea rdi, [rbp-8]
push rdi
push 4
push 1
@@ -1247,41 +6068,320 @@
pop rdi
mov DWORD PTR [rdi], eax
push rax
- pop rax
(edited)23:03ここで本来は、オフセットを加算する前に0x55~8010
のアドレスがスタックに積まれていないといけないはず
-mikiken 04/15/2023 11:26 PM代入式でnode->lhs->kind == ND_DEREF
の場合、gen_expr
→gen_addr
→gen_expr_as_lvalue
の経路で呼ばれるが、これがまずそう23:27gen_addr
は単に、変数や文字列リテラルのアドレスを生成する処理に集中したほうが良さそう23:28直そうとしたら、nodeのアドレスを生成することができません
が出た23:29pointer.c
のコード生成でエラーが出てる
-
-mikiken 04/15/2023 11:53 PMもう一回分間接参照する必要があるが、そのコードが生成されていない
-(具体的には、0x~dc98
のアドレスはスタックに積まれているが、それを間接参照して0x~8010
のアドレスをスタックに積む必要がある) (edited)
-mikiken 04/16/2023 3:38 PM
-mikiken 04/20/2023 3:02 PM混乱しているので一旦整理15:03int x = 2;
+ pop rax
(edited)
+
+
+
+
+
+
+ 23:03
+
+
+ ここで本来は、オフセットを加算する前に0x55~8010
のアドレスがスタックに積まれていないといけないはず
+
+
+
+
+
+
+
+
+
+
+ mikiken 04/15/2023 11:26 PM
+ 代入式でnode->lhs->kind == ND_DEREF
の場合、gen_expr
→gen_addr
→gen_expr_as_lvalue
の経路で呼ばれるが、これがまずそう
+
+
+
+
+
+
+ 23:27
+
+
+ gen_addr
は単に、変数や文字列リテラルのアドレスを生成する処理に集中したほうが良さそう
+
+
+
+
+
+
+ 23:28
+
+
+ 直そうとしたら、nodeのアドレスを生成することができません
が出た
+
+
+
+
+
+
+ 23:29
+
+
+ pointer.c
のコード生成でエラーが出てる
+
+
+
+
+
+
+
+
+
+ mikiken 04/15/2023 11:41 PM
+ とりあえず、コンパイルは通るようになった
+
+
+
+
+
+
+ 23:41
+
+
+ 以前、同じテストケースでセグフォしてる
+
+
+
+
+
+
+ 23:41
+
+
+ ただ、セグフォする位置が変わった
+
+
+
+
+
+
+
+
+
+ mikiken 04/15/2023 11:53 PM
+ もう一回分間接参照する必要があるが、そのコードが生成されていない
+ (具体的には、0x~dc98
のアドレスはスタックに積まれているが、それを間接参照して0x~8010
のアドレスをスタックに積む必要がある) (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 04/16/2023 3:38 PM
+
+
+
+
+
+
+
+
+
+
+ mikiken 04/20/2023 3:02 PM
+ 混乱しているので一旦整理
+
+
+
+
+
+
+ 15:03
+
+
+ int x = 2;
int *p;
p = &x; // 1
int y = *p; // 2
-*p = 3; // 3
15:041については、
-1. p
そのもののアドレスを生成
-し、そのアドレスに&x
をストアすればいい (edited)15:042については、
-1. p
そのもののアドレスを生成
-2. p
にストアされているアドレス(=x
のアドレス)を生成
-3. p
にストアされているアドレスの中身(=x
の値)を生成
-の3ステップを行い、その値をy
にストアすればいい (edited)15:083については、
-1. p
そのもののアドレスを生成
-2. p
にストアされてるアドレス(=x
のアドレス)を生成
-し、そのアドレスに3
をストアすればいい
-mikiken 04/20/2023 4:47 PMそして解決へ
-完全制覇p.195にめっちゃいい説明あった (edited)16:47多次元配列のように見えるものは「配列の配列」です。
-この「多次元配列」(もどき)は、通常hoge[i][j]
のようにアクセスするわけですが、このときに何が起きているかを以下で説明します。
-int hoge[3][5];
-という「配列の配列」があり、それをhoge[i][j]
という形でアクセスするとします(FIg. 3-15参照)。
-① hoge
の型は「int
の配列(要素数5)の配列(要素数3)」である。
-② だが、式の中なので、配列はポインタに読み替えられる。よって、hoge
の型は「int
の配列(要素数5)へのポインタ」となる。
-③ hoge[i]
は、*(hoge + i)
のシンタックスシュガーである。
-③-① ポインタにi
加算することは、そのポインタが指す型のサイズ×i
だけ、ポインタを進めることを意味する。hoge
の指す先の型は「int
の配列(要素数5)」であるから、hoge + i
では、sizeof(int [5]) * i
だけ進む。
-③-② *(hoge + i)
の*
により、ポインタが1つ剥がされる。*(hoge + i)
の型は「int
の配列(要素数5)」となる。
-③-③ が、式の中なので、配列がポインタに読み替えられる。*(hoge + i)
の最終的な型は「int
へのポインタ」となる。
-④ (*(hoge + i))[j]
は、*(*(hoge + i) + j)
に等しい。したがって、(*(hoge + i))[j]
は「int
へのポインタにj
だけ加算したアドレスの内容」であり、型はint
である。 (edited)
-mikiken 04/20/2023 11:59 PMこの説明を読んで、コード生成で配列型が出てきている時点で間違っている(ポインタにキャストされているべき)ことが分かった23:59cast_array_to_pointer()
が、node->kind
がND_LVAR
かND_GVAR
じゃないと発動しないようになっていたので、この条件を削除した00:00すると、多次元配列のコードが動くようになった 1
-mikiken 04/21/2023 4:13 PM多次元配列が動くようになったので、ライフゲームをコンパイルしてみたいな16:13GitHub Gist: instantly share code, notes, and snippets.16:14二次元配列の初期化がまだ書けない
- int grid[SIZE][SIZE] = {
+*p = 3; // 3
+
+
+
+
+
+
+ 15:04
+
+
+ 1については、
+
+
+ 1. p
そのもののアドレスを生成
+
+ し、そのアドレスに&x
をストアすればいい
+ (edited)
+
+
+
+
+
+
+ 15:04
+
+
+ 2については、
+
+
+ 1. p
そのもののアドレスを生成
+ 2. p
にストアされているアドレス(=x
のアドレス)を生成
+ 3. p
にストアされているアドレスの中身(=x
の値)を生成
+
+ の3ステップを行い、その値をy
にストアすればいい
+ (edited)
+
+
+
+
+
+
+ 15:08
+
+
+ 3については、
+
+
+ 1. p
そのもののアドレスを生成
+ 2. p
にストアされてるアドレス(=x
のアドレス)を生成
+ し、そのアドレスに3
をストアすればいい
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 04/20/2023 4:47 PM
+ そして解決へ
+ 完全制覇p.195にめっちゃいい説明あった (edited)
+
+
+
+
+
+
+ 16:47
+
+
+
+
+
+ 多次元配列のように見えるものは「配列の配列」です。
+ この「多次元配列」(もどき)は、通常hoge[i][j]
のようにアクセスするわけですが、このときに何が起きているかを以下で説明します。
+
+ int hoge[3][5];
+
+
+ という「配列の配列」があり、それをhoge[i][j]
という形でアクセスするとします(FIg. 3-15参照)。
+ ① hoge
の型は「int
の配列(要素数5)の配列(要素数3)」である。
+ ② だが、式の中なので、配列はポインタに読み替えられる。よって、hoge
の型は「int
の配列(要素数5)へのポインタ」となる。
+ ③ hoge[i]
は、*(hoge + i)
のシンタックスシュガーである。
+ ③-① ポインタにi
加算することは、そのポインタが指す型のサイズ×i
だけ、ポインタを進めることを意味する。hoge
の指す先の型は「int
の配列(要素数5)」であるから、hoge + i
では、sizeof(int [5]) * i
だけ進む。
+ ③-② *(hoge + i)
の*
により、ポインタが1つ剥がされる。*(hoge + i)
の型は「int
の配列(要素数5)」となる。
+ ③-③ が、式の中なので、配列がポインタに読み替えられる。*(hoge + i)
の最終的な型は「int
へのポインタ」となる。
+ ④ (*(hoge + i))[j]
は、*(*(hoge + i) + j)
に等しい。したがって、(*(hoge + i))[j]
は「int
へのポインタにj
だけ加算したアドレスの内容」であり、型はint
である。
+
+ (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 04/20/2023 11:59 PM
+ この説明を読んで、コード生成で配列型が出てきている時点で間違っている(ポインタにキャストされているべき)ことが分かった
+
+
+
+
+
+
+ 23:59
+
+
+ cast_array_to_pointer()
が、node->kind
がND_LVAR
かND_GVAR
じゃないと発動しないようになっていたので、この条件を削除した
+
+
+
+
+
+
+ 00:00
+
+
+ すると、多次元配列のコードが動くようになった
+
+ 1
+
+
+
+
+
+
+
+
+
+
+ mikiken 04/21/2023 4:13 PM
+ 多次元配列が動くようになったので、ライフゲームをコンパイルしてみたいな
+
+
+
+
+
+
+ 16:13
+
+
+
+
+
+
+
+
+
+
+ GitHub Gist: instantly share code, notes, and snippets.
+
+
+
+
+
+
+
+
+
+
+
+
+ 16:14
+
+
+ 二次元配列の初期化がまだ書けない
+ int grid[SIZE][SIZE] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
@@ -1301,8 +6401,18 @@
{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
16:15ということで、for文で代用しよう
- // 上のような初期化式がまだ書けないので、for文で代用
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
+
+
+
+
+
+
+ 16:15
+
+
+ ということで、for文で代用しよう
+ // 上のような初期化式がまだ書けないので、for文で代用
int grid[20][20];
for (int a = 0; a < 20; a++)
for (int b = 0; b < 20; b++)
@@ -1316,36 +6426,384 @@
grid[15][5] = 1;
grid[16][5] = 1;
grid[17][1] = 1;
- grid[17][4] = 1;
16:17関数定義の引数にまだ配列が書けなかった気がする
-int count_nbr(int grid[20][20], int i, int j, int size) {
+ grid[17][4] = 1;
+
+
+
+
+
+
+ 16:17
+
+
+ 関数定義の引数にまだ配列が書けなかった気がする
+ int count_nbr(int grid[20][20], int i, int j, int size) {
…(中略)…
-}
16:17これをコンパイルできるようにしなければ
-mikiken 04/21/2023 11:21 PMえーと、Cにおいて関数定義の引数に配列型を書いた場合にどうなるかというと、 (edited)23:23型分類としての配列が、ポインタに読み替えられる23:23ただし、このルールが適用されるのは最外周の配列(型分類としての配列)のみであることに注意23:25例えば、
-void func(int a[]) {}
は
-void func(int *a) {}
に読み替えられる23:28二次元配列の場合、
-void func(int a[][5]) {}
とか
-void func(int a[3][5]) {}
とかは、
-void func((*a)[5]) {}
に読み替えられる23:28多次元配列の場合も同様23:29なので、とりあえず
-void func((*a)[5]) {}
-のような型宣言をパースできるようにする必要がある
-mikiken 04/22/2023 1:22 AMいよいよCの✝️ 型宣言✝️ のパースに着手していく
-mikiken 04/22/2023 1:32 AMたぶん関数ポインタも、この際パースできるようにした方が見通しは良いんだろうなあ
-mikiken 05/11/2023 7:09 PMとりあえず型宣言のパースの処理をまるごと書き直している19:10TypeKind
にTYPE_FUNC
というのを追加したので、変数と関数の型を統一的に扱えるようになりそう19:11ただ、そのためにはstruct Type
に対してstruct Function
とをマッピングできるような仕組みを用意する必要がありそう
-mikiken 05/29/2023 2:02 PMやっぱり、identifierの前後に情報が分散しているのがかなりめんどい14:03とりあえず、構造体Type
が関数の引数リストを持てるようにしたほうが良さそう (edited)
-mikiken 05/29/2023 5:56 PM型宣言で、引数の名前を省略できる記法はとりあえずサポートしないことにする17:56これやりだすと、さすがに頭爆発する
-mikiken 05/29/2023 6:21 PMとりあえず、これをパースできるようにしたい
-void (*signal(int sig, void (*func)(int)))(int);
-18:36これを見ると分かるけど、何重にネストしていたとしても識別子の直後を見れば、(型の図における)最も外側の型が分かるというのが、ポイントになりそう (edited)18:36例えば、int (*x())() // func() * func() int
(edited)18:37int (*x[])() // [] * func() int
(edited)18:37int (**x)() // * * func() int
(edited)18:37なので、とにかく識別子を見たら大枠の型を確定させるように処理を組んでいく必要がある18:38なので、parse_nested_type
の処理を書いていく上では、識別子の処理を最初に書いていくのが近道だと思う
-mikiken 05/29/2023 7:08 PMたぶんここを起点に処理を考えて、それに合うように周辺の処理を書き直す方がいい
-mikiken 05/29/2023 7:46 PM型がネストしている以上、nested_type
のptr_to
が存在するはず
-
-mikiken 05/31/2023 4:48 PMコンパイルエラーが出ている箇所については、型宣言のparserのインターフェースに合わせた16:48い つ も の
-mikiken@DESKTOP-CM4259U:~/compiler/9cc$ make test
+}
+
+
+
+
+
+
+ 16:17
+
+
+ これをコンパイルできるようにしなければ
+
+
+
+
+
+
+
+
+
+ mikiken 04/21/2023 11:21 PM
+ えーと、Cにおいて関数定義の引数に配列型を書いた場合にどうなるかというと、 (edited)
+
+
+
+
+
+
+ 23:23
+
+
+ 型分類としての配列が、ポインタに読み替えられる
+
+
+
+
+
+
+ 23:23
+
+
+ ただし、このルールが適用されるのは最外周の配列(型分類としての配列)のみであることに注意
+
+
+
+
+
+
+ 23:25
+
+
+ 例えば、
+ void func(int a[]) {}
は
+ void func(int *a) {}
に読み替えられる
+
+
+
+
+
+
+ 23:28
+
+
+ 二次元配列の場合、
+ void func(int a[][5]) {}
とか
+ void func(int a[3][5]) {}
とかは、
+ void func((*a)[5]) {}
に読み替えられる
+
+
+
+
+
+
+ 23:28
+
+
+ 多次元配列の場合も同様
+
+
+
+
+
+
+ 23:29
+
+
+ なので、とりあえず
+ void func((*a)[5]) {}
+ のような型宣言をパースできるようにする必要がある
+
+
+
+
+
+
+
+
+
+ mikiken 04/22/2023 1:22 AM
+ いよいよCの✝️ 型宣言✝️ のパースに着手していく
+
+
+
+
+
+
+
+
+
+ mikiken 04/22/2023 1:32 AM
+ たぶん関数ポインタも、この際パースできるようにした方が見通しは良いんだろうなあ
+
+
+
+
+
+
+
+
+
+ mikiken 05/11/2023 7:09 PM
+ とりあえず型宣言のパースの処理をまるごと書き直している
+
+
+
+
+
+
+ 19:10
+
+
+ TypeKind
にTYPE_FUNC
というのを追加したので、変数と関数の型を統一的に扱えるようになりそう
+
+
+
+
+
+
+ 19:11
+
+
+ ただ、そのためにはstruct Type
に対してstruct Function
とをマッピングできるような仕組みを用意する必要がありそう
+
+
+
+
+
+
+
+
+
+ mikiken 05/29/2023 2:02 PM
+ やっぱり、identifierの前後に情報が分散しているのがかなりめんどい
+
+
+
+
+
+
+ 14:03
+
+
+ とりあえず、構造体Type
が関数の引数リストを持てるようにしたほうが良さそう (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 05/29/2023 5:56 PM
+ 型宣言で、引数の名前を省略できる記法はとりあえずサポートしないことにする
+
+
+
+
+
+
+ 17:56
+
+
+ これやりだすと、さすがに頭爆発する
+
+
+
+
+
+
+
+
+
+ mikiken 05/29/2023 6:21 PM
+ とりあえず、これをパースできるようにしたい
+ void (*signal(int sig, void (*func)(int)))(int);
+
+
+
+
+
+
+
+
+
+
+
+ 18:36
+
+
+ これを見ると分かるけど、何重にネストしていたとしても識別子の直後を見れば、(型の図における)最も外側の型が分かるというのが、ポイントになりそう (edited)
+
+
+
+
+
+
+ 18:36
+
+
+ 例えば、int (*x())() // func() * func() int
(edited)
+
+
+
+
+
+
+ 18:37
+
+
+ int (*x[])() // [] * func() int
(edited)
+
+
+
+
+
+
+ 18:37
+
+
+ int (**x)() // * * func() int
(edited)
+
+
+
+
+
+
+ 18:37
+
+
+ なので、とにかく識別子を見たら大枠の型を確定させるように処理を組んでいく必要がある
+
+
+
+
+
+
+ 18:38
+
+
+ なので、parse_nested_type
の処理を書いていく上では、識別子の処理を最初に書いていくのが近道だと思う
+
+
+
+
+
+
+
+
+
+ mikiken 05/29/2023 7:08 PM
+ たぶんここを起点に処理を考えて、それに合うように周辺の処理を書き直す方がいい
+
+
+
+
+
+
+
+
+
+ mikiken 05/29/2023 7:46 PM
+ 型がネストしている以上、nested_type
のptr_to
が存在するはず
+
+
+
+
+
+
+
+
+
+ mikiken 05/31/2023 10:21 AM
+ 一応型宣言のparserができた
+
+
+
+
+
+
+ 10:21
+
+
+ pushした
+
+
+
+
+
+
+ 10:22
+
+
+
+
+
+
+
+ 10:23
+
+
+ あとは、parser側の実装を型宣言のparserに合わせる必要がある
+
+
+
+
+
+
+
+
+
+ mikiken 05/31/2023 4:48 PM
+ コンパイルエラーが出ている箇所については、型宣言のparserのインターフェースに合わせた
+
+
+
+
+
+
+ 16:48
+
+
+ い つ も の
+ mikiken@DESKTOP-CM4259U:~/compiler/9cc$ make test
./test/test_driver.sh
./test/test_driver.sh: line 3: 25339 Segmentation fault ../9cc ${file%.*}.c > ${file%.*}.s
./test/test_driver.sh: line 3: 25342 Segmentation fault ../9cc ${file%.*}.c > ${file%.*}.s
@@ -1360,12 +6818,102 @@
collect2: error: ld returned 1 exit status
./test/test_driver.sh: line 9: ./tmp: No such file or directory
rm: cannot remove 'tmp': No such file or directory
-make: *** [Makefile:17: test] Error 1
16:53セグフォ解消まつりを開催16:56とりあえず、型宣言のパースでバグってるので直す (edited)16:58そもそも型宣言のパーサーのテスト書いた方が絶対いいんよな
-mikiken 05/31/2023 5:05 PMToken *ident
のkind
がTK_SEMICOLON
で草
-mikiken 05/31/2023 5:15 PM例によってshallow copyとdeep copyを間違えてた17:15Token *ident = tok;
-↓
-Token ident = *tok;
-mikiken 05/31/2023 5:27 PMFuncDeclaration
を確保したら、引数のfunc_type
が書き換わるとかいう謎の現象発生した17:28void new_func_declaration(Type *func_type) {
+make: *** [Makefile:17: test] Error 1
+
+
+
+
+
+
+ 16:53
+
+
+ セグフォ解消まつりを開催
+
+
+
+
+
+
+ 16:56
+
+
+ とりあえず、型宣言のパースでバグってるので直す (edited)
+
+
+
+
+
+
+ 16:58
+
+
+ そもそも型宣言のパーサーのテスト書いた方が絶対いいんよな
+
+
+
+
+
+
+
+
+
+ mikiken 05/31/2023 5:05 PM
+ Token *ident
のkind
がTK_SEMICOLON
で草
+
+
+
+
+
+
+
+
+
+
+ mikiken 05/31/2023 5:15 PM
+ 例によってshallow copyとdeep copyを間違えてた
+
+
+
+
+
+
+ 17:15
+
+
+ Token *ident = tok;
+ ↓
+ Token ident = *tok;
+
+
+
+
+
+
+
+
+
+ mikiken 05/31/2023 5:27 PM
+ FuncDeclaration
を確保したら、引数のfunc_type
が書き換わるとかいう謎の現象発生した
+
+
+
+
+
+
+ 17:28
+
+
+ void new_func_declaration(Type *func_type) {
FuncDeclaration *func_dec = calloc(1, sizeof(FuncDeclaration));
func_dec->ret_type = func_type->return_type;
func_dec->name = calloc(func_type->ident->len + 1, sizeof(char));
@@ -1373,17 +6921,83 @@
func_dec->len = func_type->ident->len;
func_dec->next = func_declaration_list;
func_declaration_list = func_dec;
-}
-mikiken FuncDeclaration
を確保したら、引数のfunc_type
が書き換わるとかいう謎の現象発生した mikiken 06/04/2023 12:12 AMデバッガの挙動的に、またshallow copyとdeep copyの間違いっぽい? (edited)
-mikiken 06/05/2023 8:25 PMあ〜、なるほど
- // 識別子を記録
+}
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken
+ FuncDeclaration
を確保したら、引数のfunc_type
が書き換わるとかいう謎の現象発生した
+
+ mikiken 06/04/2023 12:12 AM
+ デバッガの挙動的に、またshallow copyとdeep copyの間違いっぽい? (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 06/05/2023 8:25 PM
+ あ〜、なるほど
+ // 識別子を記録
Token ident = *tok;
-ident
がスタック領域に確保されてるので、関数を抜けると消滅してしまい、上のような謎挙動を引き起こしている (edited)20:26ということでident
をcalloc
すれば大丈夫なはず
- // 識別子を記録
+ ident
がスタック領域に確保されてるので、関数を抜けると消滅してしまい、上のような謎挙動を引き起こしている
(edited)
+
+
+
+
+
+
+ 20:26
+
+
+ ということでident
をcalloc
すれば大丈夫なはず
+ // 識別子を記録
Token *ident = calloc(1, sizeof(Token));
- *ident = *tok;
(edited)20:27お、セグフォが消えた
-mikiken 06/05/2023 11:55 PMコンパイルが通るように色々修正23:56簡単なテストケース
-int printf();
+ *ident = *tok;
(edited)
+
+
+
+
+
+
+ 20:27
+
+
+ お、セグフォが消えた
+
+
+
+
+
+
+
+
+
+ mikiken 06/05/2023 11:55 PM
+ コンパイルが通るように色々修正
+
+
+
+
+
+
+ 23:56
+
+
+ 簡単なテストケース
+ int printf();
int func(int x) {
return x;
@@ -1391,7 +7005,47 @@
int main() {
return func(3);
-}
23:56とりあえず、これのコンパイルが通るようになった23:56ただ、実行結果がおかしい23:57正しい出力との差分をとってみた23:57.intel_syntax noprefix .intel_syntax noprefix
+}
+
+
+
+
+
+
+ 23:56
+
+
+ とりあえず、これのコンパイルが通るようになった
+
+
+
+
+
+
+ 23:56
+
+
+ ただ、実行結果がおかしい
+
+
+
+
+
+
+ 23:57
+
+
+ 正しい出力との差分をとってみた
+
+
+
+
+
+
+ 23:57
+
+
+ .intel_syntax noprefix .intel_syntax noprefix
.data .data
@@ -1414,24 +7068,194 @@
mov rsp, rbp mov rsp, rbp
pop rbp pop rbp
ret ret
-(以下略)
23:58引数をローカル変数にコピーする処理が抜けてそう00:02いや、むしろそれはできてて、引数リストの中身が入ってない
-mikiken 06/06/2023 11:46 AMいろいろ実装が噛み合ってないところを修正
-mikiken 06/06/2023 12:23 PMいろいろ変えたら今度はローカル変数のパースで事故ってる12:24ローカル変数のoffsetを計算しようとしてるのに、lvar_list_tail
がないって言われるっぽい
-mikiken 06/06/2023 4:10 PMarith.c
+(以下略)
+
+
+
+
+
+
+ 23:58
+
+
+ 引数をローカル変数にコピーする処理が抜けてそう
+
+
+
+
+
+
+ 00:02
+
+
+ いや、むしろそれはできてて、引数リストの中身が入ってない
+
+
+
+
+
+
+
+
+
+ mikiken 06/06/2023 11:46 AM
+ いろいろ実装が噛み合ってないところを修正
+
+
+
+
+
+
+
+
+
+ mikiken 06/06/2023 12:23 PM
+ いろいろ変えたら今度はローカル変数のパースで事故ってる
+
+
+
+
+
+
+ 12:24
+
+
+ ローカル変数のoffsetを計算しようとしてるのに、lvar_list_tail
がないって言われるっぽい
+
+
+
+
+
+
+
+
+
+ mikiken 06/06/2023 4:10 PM
+ arith.c
control_statement.c
pointer.c
string.c
variable.c
test.c
-のコンパイルは通るようになった (edited)16:14次にarray.c
を試す16:15セグフォ(定期)16:15配列型をパースするときに、構造体Type
に識別子を記録紙忘れてた16:16次、意味解析でsemantic_analysis(): ポインタでないものを間接参照することはできません
が出た16:16例の多次元配列のテストケースでコケてる
-int array_test17() {
+ のコンパイルは通るようになった
(edited)
+
+
+
+
+
+
+ 16:14
+
+
+ 次にarray.c
を試す
+
+
+
+
+
+
+ 16:15
+
+
+ セグフォ(定期)
+
+
+
+
+
+
+ 16:15
+
+
+ 配列型をパースするときに、構造体Type
に識別子を記録紙忘れてた
+
+
+
+
+
+
+ 16:16
+
+
+ 次、意味解析でsemantic_analysis(): ポインタでないものを間接参照することはできません
が出た
+
+
+
+
+
+
+ 16:16
+
+
+ 例の多次元配列のテストケースでコケてる
+ int array_test17() {
int arr[3][5];
arr[2][4] = 21;
return arr[2][4];
-}
16:18とりあえず型宣言のパースのところをステップ実行してみて、思った通りに多次元配列がパースできてるか確認した方がよさそう
-mikiken 06/13/2023 7:34 PMなんか多次元配列の型宣言が上手くパースできてなくて、配列の配列が表現できていない
-mikiken 06/13/2023 7:57 PMparse_suffix_type()
とparse_outermost_type()
の実装がまずかったので修正19:58function.c
以外はコンパイルできるようになった20:02function.cの関数ポインタの宣言のテストケースをコメントアウトしたら、"コンパイルは通った"20:02なお実行結果
-mikiken@DESKTOP-CM4259U:~/compiler/9cc$ make test
+}
+
+
+
+
+
+
+ 16:18
+
+
+ とりあえず型宣言のパースのところをステップ実行してみて、思った通りに多次元配列がパースできてるか確認した方がよさそう
+
+
+
+
+
+
+
+
+
+ mikiken 06/13/2023 7:34 PM
+ なんか多次元配列の型宣言が上手くパースできてなくて、配列の配列が表現できていない
+
+
+
+
+
+
+
+
+
+ mikiken 06/13/2023 7:57 PM
+ parse_suffix_type()
とparse_outermost_type()
の実装がまずかったので修正
+
+
+
+
+
+
+ 19:58
+
+
+ function.c
以外はコンパイルできるようになった
+
+
+
+
+
+
+ 20:02
+
+
+ function.cの関数ポインタの宣言のテストケースをコメントアウトしたら、"コンパイルは通った"
+
+
+
+
+
+
+ 20:02
+
+
+ なお実行結果
+ mikiken@DESKTOP-CM4259U:~/compiler/9cc$ make test
./test/test_driver.sh
[PASS] 0 => 21993
[PASS] 42 => 21993
@@ -1478,24 +7302,170 @@
[PASS] comma_opetator_test1((1, 2), (3, 4)) => 21993
All arithmetical test cases have passed.
-./test/test_driver.sh: line 9: 3231 Segmentation fault ./tmp
20:02
-mikiken 06/14/2023 10:29 AM正しく動いていた段階のコミットに戻して、差分をとってみた (edited)10:2915.7 KB10:33ローカル変数のoffsetの計算が壊れているのが原因っぽい (edited)
-mikiken 06/14/2023 10:48 AMcalc_lvar_offset
で直前のローカル変数をうまく渡せてなさそう
-mikiken 06/16/2023 5:35 PM引数をローカル変数のリストにコピーする処理を書き忘れたりしていたのを修正17:35ひとまず、全てのテストが通るようになった17:35関数ポインタを含むテストケースが正しくパースできていない疑惑がある (edited)
-mikiken 06/16/2023 6:01 PM型宣言の各関数の責務を明確にしたほうが良さそう
-Type *parse_base_type(Token *tok);
+./test/test_driver.sh: line 9: 3231 Segmentation fault ./tmp
+
+
+
+
+
+
+ 20:02
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 06/14/2023 10:29 AM
+ 正しく動いていた段階のコミットに戻して、差分をとってみた (edited)
+
+
+
+
+
+
+ 10:29
+
+
+
+
+
+ 15.7 KB
+
+
+
+
+
+
+
+
+ 10:33
+
+
+ ローカル変数のoffsetの計算が壊れているのが原因っぽい (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 06/14/2023 10:48 AM
+ calc_lvar_offset
で直前のローカル変数をうまく渡せてなさそう
+
+
+
+
+
+
+
+
+
+ mikiken 06/16/2023 5:35 PM
+ 引数をローカル変数のリストにコピーする処理を書き忘れたりしていたのを修正
+
+
+
+
+
+
+ 17:35
+
+
+ ひとまず、全てのテストが通るようになった
+
+
+
+
+
+
+ 17:35
+
+
+ 関数ポインタを含むテストケースが正しくパースできていない疑惑がある (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 06/16/2023 6:01 PM
+ 型宣言の各関数の責務を明確にしたほうが良さそう
+ Type *parse_base_type(Token *tok);
Type *parse_pointer_type(Type *base_type, Token *tok);
Type *parse_nested_type(Type *pointed_type, Token *tok);
Type *parse_outermost_type(Type *pointed_type, Token *tok);
Type *parse_suffix_type(Type *pointer_type, Token *tok);
-Type *parse_declaration_type(Token *tok);
(edited)
-mikiken 06/22/2023 12:56 PMとりあえずいろいろ変更12:56diff --git a/src/parse.c b/src/parse.c
+Type *parse_declaration_type(Token *tok);
(edited)
+
+
+
+
+
+
+
+
+
+ mikiken 06/22/2023 12:56 PM
+ とりあえずいろいろ変更
+
+
+
+
+
+
+ 12:56
+
+
+ diff --git a/src/parse.c b/src/parse.c
index 5e48914..b7abb73 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -209,50 +209,35 @@ Type *parse_pointer_type(Type *base_type, Token *tok) {
return base_type;
- }
(edited)12:57-Type *parse_nested_type(Type *pointed_type, Token *tok) {
+ }
(edited)
+
+
+
+
+
+
+ 12:57
+
+
+ -Type *parse_nested_type(Type *pointed_type, Token *tok) {
- Type *pointer_type = parse_pointer_type(pointed_type, tok);
+Type *parse_nested_type(Type *pointer_type, Token *tok) {
if (!pointer_type)
@@ -1549,7 +7519,17 @@
+ Type *outermost_pointer_type = parse_pointer_type(inner_type, tok);
+ return parse_outermost_type(outermost_pointer_type, tok);
+ }
- }
(edited)12:57 // 識別子のレベル(型の図において最も外側の型)をパース
+ }
(edited)
+
+
+
+
+
+
+ 12:57
+
+
+ // 識別子のレベル(型の図において最も外側の型)をパース
-Type *parse_outermost_type(Type *pointed_type, Token *tok) {
- Type *pointer_type = parse_pointer_type(pointed_type, tok);
+Type *parse_outermost_type(Type *pointer_type, Token *tok) {
@@ -1578,57 +7558,604 @@
+ }
}
// 関数の引数が (void)である場合
- else if (pointer_type->kind == TYPE_VOID) {
12:59結構いい線いってるっぽいが、
-// 配列型・関数型の場合
+ else if (pointer_type->kind == TYPE_VOID) {
+
+
+
+
+
+
+ 12:59
+
+
+ 結構いい線いってるっぽいが、
+ // 配列型・関数型の場合
if (suffix_type)
- *inner_type = *suffix_type;
12:59コピーの仕方がまずくて、↑の処理を行った瞬間に、ポインタが循環参照になって型情報が壊れる13:00どこかしらでシャローコピーしてるのが悪さしてそうなんよなあ (edited)
-mikiken 06/22/2023 4:11 PMpointer_type
をparse_suffix_type()
で再び参照しているのがまずそう16:11そこで、以下のようにpointer_type
を(ディープ)コピーしてから用いるようにした16:12 Type *prefix_type = new_type(TYPE_NULL);
- *prefix_type = *pointer_type;
16:12ネストした型がパースできるようになった16:13それによって、ライフゲームのサンプルが動くようになった16:13
-mikiken 07/02/2023 11:43 PMさすがに構造体を足したい23:43struct
というキーワードを足した23:44方針としては、各要素の型が異なる配列みたいな感じで扱う感じになりそう?23:45別の表現をすれば、複数の型を1つの型としてまとめて扱えるようにするという感じ23:45とりあえず、TokenKind
にTK_STRUCT
を追加23:48(多次元)配列と違うのは、各要素(すなわちメンバ)に名前がついており、各要素の型(データサイズ)が異なる点23:49それを踏まえると、Member
みたいな構造体を用意する必要がありそう23:52Member
という構造体に必要そうなメンバは、
-・型情報
-・メンバの名前
-・(先頭のアドレスからの)オフセット23:57ある構造体に対して、複数のメンバの情報を持つとき、Linked Listで実装してもいいが、正直適当なデカさの配列で持っておいてもいい気はする23:57まあLinked Listでいくか
-
-mikiken 07/10/2023 4:50 PM関数に構造体の宣言の情報を記録しておく必要がある?
-mikiken 07/17/2023 10:37 PMとりあえず、Struct *struct_list
みたいなやつを生やして対応22:37そのうちローカル変数の処理とかと一本化したい
-mikiken 07/25/2023 11:55 PMint main() {
+ *inner_type = *suffix_type;
+
+
+
+
+
+
+ 12:59
+
+
+ コピーの仕方がまずくて、↑の処理を行った瞬間に、ポインタが循環参照になって型情報が壊れる
+
+
+
+
+
+
+ 13:00
+
+
+ どこかしらでシャローコピーしてるのが悪さしてそうなんよなあ (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 06/22/2023 4:11 PM
+ pointer_type
をparse_suffix_type()
で再び参照しているのがまずそう
+
+
+
+
+
+
+ 16:11
+
+
+ そこで、以下のようにpointer_type
を(ディープ)コピーしてから用いるようにした
+
+
+
+
+
+
+ 16:12
+
+
+ Type *prefix_type = new_type(TYPE_NULL);
+ *prefix_type = *pointer_type;
+
+
+
+
+
+
+ 16:12
+
+
+ ネストした型がパースできるようになった
+
+
+
+
+
+
+ 16:12
+
+
+
+
+
+
+
+ 16:13
+
+
+ それによって、ライフゲームのサンプルが動くようになった
+
+
+
+
+
+
+ 16:13
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 07/02/2023 11:43 PM
+ さすがに構造体を足したい
+
+
+
+
+
+
+ 23:43
+
+
+ struct
というキーワードを足した
+
+
+
+
+
+
+ 23:44
+
+
+ 方針としては、各要素の型が異なる配列みたいな感じで扱う感じになりそう?
+
+
+
+
+
+
+ 23:45
+
+
+ 別の表現をすれば、複数の型を1つの型としてまとめて扱えるようにするという感じ
+
+
+
+
+
+
+ 23:45
+
+
+ とりあえず、TokenKind
にTK_STRUCT
を追加
+
+
+
+
+
+
+ 23:48
+
+
+ (多次元)配列と違うのは、各要素(すなわちメンバ)に名前がついており、各要素の型(データサイズ)が異なる点
+
+
+
+
+
+
+ 23:49
+
+
+ それを踏まえると、Member
みたいな構造体を用意する必要がありそう
+
+
+
+
+
+
+ 23:52
+
+
+ Member
という構造体に必要そうなメンバは、
+ ・型情報
+ ・メンバの名前
+ ・(先頭のアドレスからの)オフセット
+
+
+
+
+
+
+ 23:57
+
+
+ ある構造体に対して、複数のメンバの情報を持つとき、Linked Listで実装してもいいが、正直適当なデカさの配列で持っておいてもいい気はする
+
+
+
+
+
+
+ 23:57
+
+
+ まあLinked Listでいくか
+
+
+
+
+
+
+
+
+
+ mikiken 07/10/2023 2:18 PM
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 07/10/2023 4:50 PM
+ 関数に構造体の宣言の情報を記録しておく必要がある?
+
+
+
+
+
+
+
+
+
+ mikiken 07/17/2023 10:37 PM
+ とりあえず、Struct *struct_list
みたいなやつを生やして対応
+
+
+
+
+
+
+ 22:37
+
+
+ そのうちローカル変数の処理とかと一本化したい
+
+
+
+
+
+
+
+
+
+ mikiken 07/25/2023 11:55 PM
+ int main() {
struct position {
int x;
int y;
};
struct position p;
-}
-mikiken 07/27/2023 1:08 AM構造体のメンバのアライメントに気をつけないといけないみたいな話あった気がしてきた
-例えばこういうの: https://godbolt.org/z/vbb3nf7fE (edited)int main () {
- struct foo{
- int x;
- char y;
- };
- struct foo p;
- p.x = 3;
- p.y = 5;
- printf("sizeof : %lu\n", sizeof(struct foo));
- printf("_Alignof : %lu\n", _Alignof(struct foo));
- return 0;
-}
-mikiken 09/03/2023 5:43 PM久々にやっていく17:43何も覚えていないので、一旦全部取り消して一から構造体を実装していく (edited)17:44まず、構造体定義を表す構造体を作成した
-struct StructDef {
+}
+
+
+
+
+
+
+
+
+
+ mikiken 07/27/2023 1:08 AM
+ 構造体のメンバのアライメントに気をつけないといけないみたいな話あった気がしてきた
+ 例えばこういうの: https://godbolt.org/z/vbb3nf7fE (edited)
+
+
+
+
+
+
+
+ int main () {
+ struct foo{
+ int x;
+ char y;
+ };
+ struct foo p;
+ p.x = 3;
+ p.y = 5;
+ printf("sizeof : %lu\n", sizeof(struct foo));
+ printf("_Alignof : %lu\n", _Alignof(struct foo));
+ return 0;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mikiken 09/03/2023 5:43 PM
+ 久々にやっていく
+
+
+
+
+
+
+ 17:43
+
+
+ 何も覚えていないので、一旦全部取り消して一から構造体を実装していく (edited)
+
+
+
+
+
+
+ 17:44
+
+
+ まず、構造体定義を表す構造体を作成した
+ struct StructDef {
Token *tag;
Member members;
-};
17:44この構造体は、構造体のタグ(struct position
のposition
の部分)と、構造体のメンバのリストを持つ17:45構造体メンバのリストは、次のような構造体で表される
-struct Member {
+};
+
+
+
+
+
+
+ 17:44
+
+
+ この構造体は、構造体のタグ(struct position
のposition
の部分)と、構造体のメンバのリストを持つ
+
+
+
+
+
+
+ 17:45
+
+
+ 構造体メンバのリストは、次のような構造体で表される
+ struct Member {
Member *next;
Type *type;
-};
17:45次のメンバへのポインタと、メンバ自体の型情報を持つ17:46Type
という構造体の中に、変数名などの情報も含まれているので、struct Member
のメンバとしてはこの2種類で足りるはず17:47そして、Type
という構造体に、今書いたStructDef
へのポインタを追加した17:47 // 構造体型
- StructDef *struct_def;
17:48構造体の定義リスト自体は、トップレベルに書く場合、関数の中で書く場合など、スコープごとに持っておく必要がある17:49その構造体の定義リストの中の、構造体定義を上記のstruct_def
が指し示す17:50なお、メンバのリストは、struct StructDef
自体がその実体を持つ17:50とりあえず、まずはトップレベルと関数のリストの中に、構造体定義のリストを追加する必要がある (edited) 1
-mikiken 09/03/2023 10:15 PM構造体定義と、構造体変数の定義を同時に行う記法は現時点ではサポートしない
-mikiken 09/05/2023 11:21 PMタグなし構造体って書けるんや(無知)23:21使わん気がするのでサポートしない23:26構造体できたら、全体的にリファクタリングしたいな23:27さすがに適当な順番でいろんな機能を生やしているのでコードがぐちゃぐちゃになってる
-mikiken 09/05/2023 11:35 PM変数宣言など、「何もしない」ことを表すNodeKindがあってもいいかも23:41雑な改修の結果、とりあえず構造体定義はパースできるようになったと思われる23:41なおsamantic_analysis.c
ではセグフォしてる23:41これは、NodeをNULLのまま返してるので switch(node->kind)
がNULLになってるから (edited)23:45こういうのもできるらしい
-struct position {
+};
+
+
+
+
+
+
+ 17:45
+
+
+ 次のメンバへのポインタと、メンバ自体の型情報を持つ
+
+
+
+
+
+
+ 17:46
+
+
+ Type
という構造体の中に、変数名などの情報も含まれているので、struct Member
のメンバとしてはこの2種類で足りるはず
+
+
+
+
+
+
+ 17:47
+
+
+ そして、Type
という構造体に、今書いたStructDef
へのポインタを追加した
+
+
+
+
+
+
+ 17:47
+
+
+ // 構造体型
+ StructDef *struct_def;
+
+
+
+
+
+
+ 17:48
+
+
+ 構造体の定義リスト自体は、トップレベルに書く場合、関数の中で書く場合など、スコープごとに持っておく必要がある
+
+
+
+
+
+
+ 17:49
+
+
+ その構造体の定義リストの中の、構造体定義を上記のstruct_def
が指し示す
+
+
+
+
+
+
+ 17:50
+
+
+ なお、メンバのリストは、struct StructDef
自体がその実体を持つ
+
+
+
+
+
+
+ 17:50
+
+
+ とりあえず、まずはトップレベルと関数のリストの中に、構造体定義のリストを追加する必要がある (edited)
+
+ 1
+
+
+
+
+
+
+
+
+
+
+ mikiken 09/03/2023 10:15 PM
+ 構造体定義と、構造体変数の定義を同時に行う記法は現時点ではサポートしない
+
+
+
+
+
+
+
+
+
+ mikiken 09/05/2023 11:21 PM
+ タグなし構造体って書けるんや(無知)
+
+
+
+
+
+
+ 23:21
+
+
+ 使わん気がするのでサポートしない
+
+
+
+
+
+
+ 23:26
+
+
+ 構造体できたら、全体的にリファクタリングしたいな
+
+
+
+
+
+
+ 23:27
+
+
+ さすがに適当な順番でいろんな機能を生やしているのでコードがぐちゃぐちゃになってる
+
+
+
+
+
+
+
+
+
+ mikiken 09/05/2023 11:35 PM
+ 変数宣言など、「何もしない」ことを表すNodeKindがあってもいいかも
+
+
+
+
+
+
+ 23:41
+
+
+ 雑な改修の結果、とりあえず構造体定義はパースできるようになったと思われる
+
+
+
+
+
+
+ 23:41
+
+
+ なおsamantic_analysis.c
ではセグフォしてる
+
+
+
+
+
+
+ 23:41
+
+
+ これは、NodeをNULLのまま返してるので switch(node->kind)
がNULLになってるから (edited)
+
+
+
+
+
+
+ 23:45
+
+
+ こういうのもできるらしい
+ struct position {
int x;
int y;
-} pos[30];
23:46まあとりあえずは、こいつを通せるようにやっていく
-int main() {
+} pos[30];
+
+
+
+
+
+
+ 23:46
+
+
+ まあとりあえずは、こいつを通せるようにやっていく
+ int main() {
struct position {
int x;
int y;
@@ -1639,14 +8166,104 @@
p.y = 3;
return p.x + p.y;
-}
-mikiken 09/10/2023 12:51 AMstruct Member
がoffsetを持つようにした
-offsetの計算の際に、メンバのアライメントを考えないといけないはず
-でも、これはローカル変数のoffsetの計算と同じようにできるはずなので、既に作成しているはず
-結局、構造体は、変数のリストをネストさせたものみたいに理解すれば良さそう(な気がしている) (edited)00:51TK_DOT
をトークナイズできるようにした00:51次のステップは構造体変数を定義できるようにすること00:52その次に.
演算子の実装
-mikiken 09/15/2023 2:49 PMまず、構造体変数を定義できるようにしていく14:50新たな構造体の定義時に、def_list
への追加を忘れてたので修正14:53変数のリストに、構造体変数を追加できるようにする
-mikiken 09/16/2023 4:14 PM構造体変数のオフセットを計算できるようにする
-mikiken 09/23/2023 11:33 AM while (true) {
+}
+
+
+
+
+
+
+
+
+
+ mikiken 09/10/2023 12:51 AM
+ struct Member
がoffsetを持つようにした
+ offsetの計算の際に、メンバのアライメントを考えないといけないはず
+ でも、これはローカル変数のoffsetの計算と同じようにできるはずなので、既に作成しているはず
+ 結局、構造体は、変数のリストをネストさせたものみたいに理解すれば良さそう(な気がしている) (edited)
+
+
+
+
+
+
+ 00:51
+
+
+ TK_DOT
をトークナイズできるようにした
+
+
+
+
+
+
+ 00:51
+
+
+ 次のステップは構造体変数を定義できるようにすること
+
+
+
+
+
+
+ 00:52
+
+
+ その次に.
演算子の実装
+
+
+
+
+
+
+
+
+
+ mikiken 09/15/2023 2:49 PM
+ まず、構造体変数を定義できるようにしていく
+
+
+
+
+
+
+ 14:50
+
+
+ 新たな構造体の定義時に、def_list
への追加を忘れてたので修正
+
+
+
+
+
+
+ 14:53
+
+
+ 変数のリストに、構造体変数を追加できるようにする
+
+
+
+
+
+
+
+
+
+ mikiken 09/16/2023 4:14 PM
+ 構造体変数のオフセットを計算できるようにする
+
+
+
+
+
+
+
+
+
+ mikiken 09/23/2023 11:33 AM
+ while (true) {
if (consume(tok, TK_LEFT_BRACKET)) {
node = new_binary_node(ND_DEREF, new_binary_node(ND_ADD, node, expr(func, tok)), NULL);
expect(tok, TK_RIGHT_BRACKET);
@@ -1663,13 +8280,113 @@
}
return node;
}
-現時点だと、ローカル変数はとnodeはoffsetの情報のみでマッピングしてるので、parseする段階でnodeに型情報を入れておく必要がある
-mikiken 09/24/2023 12:07 PM型情報足したら、多分parseは通るようになって、codegenでアドレスが生成できないとか言われてる
-良さそう12:09gen_addr()
において、.
が来た場合は、rbp - 構造体変数のoffset - メンバのoffset のアドレスを生成すればいけそう12:09これは、p.x = 2;
みたいに左辺値として.
演算子が使われた場合の話12:10一方で、return p.x;
みたいな感じで、右辺値として使われた場合のコードもgen_expr()
とかに足す必要があるはず
-mikiken 09/25/2023 1:15 AMメモ
-・トップレベルに構造体を定義できるようにする
-・構造体をネストできるようにする (edited)
-mikiken 11/05/2023 10:20 PMまた1ヶ月くらい放置した22:20何やってたっけ22:20構造体を実装してて、.
演算子が一応追加されてたはず22:21ただ一部のテストがまだバグってたはず22:21int main() {
+ 現時点だと、ローカル変数はとnodeはoffsetの情報のみでマッピングしてるので、parseする段階でnodeに型情報を入れておく必要がある
+
+
+
+
+
+
+
+
+
+ mikiken 09/24/2023 12:07 PM
+ 型情報足したら、多分parseは通るようになって、codegenでアドレスが生成できないとか言われてる
+ 良さそう
+
+
+
+
+
+
+ 12:09
+
+
+ gen_addr()
において、.
が来た場合は、rbp - 構造体変数のoffset - メンバのoffset のアドレスを生成すればいけそう
+
+
+
+
+
+
+ 12:09
+
+
+ これは、p.x = 2;
みたいに左辺値として.
演算子が使われた場合の話
+
+
+
+
+
+
+ 12:10
+
+
+ 一方で、return p.x;
みたいな感じで、右辺値として使われた場合のコードもgen_expr()
とかに足す必要があるはず
+
+
+
+
+
+
+
+
+
+ mikiken 09/25/2023 1:15 AM
+ メモ
+ ・トップレベルに構造体を定義できるようにする
+ ・構造体をネストできるようにする (edited)
+
+
+
+
+
+
+
+
+
+ mikiken 11/05/2023 10:20 PM
+ また1ヶ月くらい放置した
+
+
+
+
+
+
+ 22:20
+
+
+ 何やってたっけ
+
+
+
+
+
+
+ 22:20
+
+
+ 構造体を実装してて、.
演算子が一応追加されてたはず
+
+
+
+
+
+
+ 22:21
+
+
+ ただ一部のテストがまだバグってたはず
+
+
+
+
+
+
+ 22:21
+
+
+ int main() {
struct position {
int x;
int y;
@@ -1681,10 +8398,60 @@
p.ptr = &hoge;
return *p.ptr; // => 7
-}
22:21これはちゃんとコンパイルできる22:22struct position
のメンバにint z
を追加すると、returnされる値がバグる
-mikiken 11/05/2023 11:30 PMアセンブリ読んだ感じだとoffsetの計算が間違っていそう23:30というか、挙動からエスパーしても、こういうパターンは大体offsetの計算間違えてる(偏見)
-mikiken 11/06/2023 9:10 AM出力されるアセンブリを手で書き換えたらうまく動いた
-.intel_syntax noprefix
+}
+
+
+
+
+
+
+ 22:21
+
+
+ これはちゃんとコンパイルできる
+
+
+
+
+
+
+ 22:22
+
+
+ struct position
のメンバにint z
を追加すると、returnされる値がバグる
+
+
+
+
+
+
+
+
+
+ mikiken 11/05/2023 11:30 PM
+ アセンブリ読んだ感じだとoffsetの計算が間違っていそう
+
+
+
+
+
+
+ 23:30
+
+
+ というか、挙動からエスパーしても、こういうパターンは大体offsetの計算間違えてる(偏見)
+
+
+
+
+
+
+
+
+
+ mikiken 11/06/2023 9:10 AM
+ 出力されるアセンブリを手で書き換えたらうまく動いた
+ .intel_syntax noprefix
.data
@@ -1729,8 +8496,28 @@
.L.return.main:
mov rsp, rbp
pop rbp
- ret
09:11配列のアドレス出力するのと同様の計算を行う必要がありそう
-mikiken 11/06/2023 9:33 AM(← higer address ↑)
+ ret
+
+
+
+
+
+
+ 09:11
+
+
+ 配列のアドレス出力するのと同様の計算を行う必要がありそう
+
+
+
+
+
+
+
+
+
+ mikiken 11/06/2023 9:33 AM
+ (← higer address ↑)
0 4 8
+----------------+----------------+
|/// (int x) ////|/// (int y) ////|
@@ -1739,8 +8526,76 @@
+----------------+----------------|
|////////// (int *ptr) ///////////|
+----------------+----------------|
-(lower address)
(edited)
-09:45先頭のアドレスが下位アドレスにあって、そこにoffset * sizeof(int)
分のアドレスを加算することで、各要素のアドレスを算出してる09:50やはり、先に書いたメンバが下位アドレスから上位アドレスに向かって順次配置されてそう09:56たぶん今の実装だと、構造体変数全体の大きさをrbpから引いて、さらにoffsetの値をそこから引くようになってるけど、これはどうみてもおかしいので直す必要がありそう09:56さらに先に書いたメンバが上位アドレス側に配置されているので、これも修正する必要がある
+(lower address) (edited)
+
offset * sizeof(int)
分のアドレスを加算することで、各要素のアドレスを算出してる