編纂者:hsjoihs
2022年セキュリティ・キャンプL3(Cコンパイラゼミ)は基本的に Discord で行われた。したがって、テキストチャットとボイスチャットが混在する形態となったが、講師である hsjoihs がゼミ中の会話を全て手動でテキスト起こしした。
OS自作ゼミ・Cコンパイラゼミは、もとから自分が興味を持っていて、かつDiscordでやってることが見えやすかったのでよかったです。特にCコンパイラゼミは会話がまるごとテキストに起こされてチャンネルに流されていたので、マジで良かったです。これ絶対大変だと思うんですけど、めちゃくちゃ助かりました。 セキュリティ・キャンプを終えて(感想とか) - わたすけのへや
セキュリティ・キャンプCコンパイラゼミの創設者である@rui314さんの以下の思想に基づき、Cコンパイラゼミの会話のうち、発言者からの公開許可を頂けた発言を一般公開する。会話のテキスト起こしも Discord に最初からテキストとして書かれたものも区別せずに表示している。
セキュキャンは参加者はお金を払わないし、物理的制約で選考というものがある中で通るか否かいうのは時の運によるものが多いので(年齢制限もあるし)、行ったかどうかであまり差がつくというのは望ましくないと思う。なので可能な限り教材をやれば誰でもできるというようにしたいとは思います。
— Rui Ueyama (@rui314) April 15, 2019
セキュキャンに来なくて大丈夫なようにコースの詳細な資料を全公開するとなぜか逆に応募が増えるという。でもインターネットってそういうものかも。
— Rui Ueyama (@rui314) May 28, 2019
なお、ここで言及されている「コースの詳細な資料」とは「低レイヤを知りたい人のためのCコンパイラ作成入門」のことである。今年の C コンパイラゼミでは、これに加えて前橋和弥「新・標準プログラマーズライブラリ C言語 ポインタ完全制覇」も用いた。
assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
「試しにunary-expressionを読んでみて、その直後にassignment-operatorが来ていなかった場合はその情報を破棄し、conditional-expressionとして読む」と実装しなければならない。しかし、現状ではparseしたら同時にコード生成もしてしまう。
make test1 => 第1世代に対するテスト
make test2 => 第2世代 ...
make test3 => 第3世代 ...
make testall => test1, test2, test3を実行
make diff => selfhost/gen2/*.sとselfhost/gen3/*.sを比較する。
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
.globl init_array8
init_array8:
push rbp
mov rbp, rsp
sub rsp, 20
mov rax, 0
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
mov rax, 1
push rax
pop rdi
pop rax
mov [rax], edi
push rdi
pop rax
mov rax, 1
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
mov rax, 2
push rax
pop rdi
pop rax
mov [rax], edi
push rdi
pop rax
push rax
pop rax
mov rax, 0
push rax
pop rax
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rax
mov eax, [rax]
cdqe
push rax
mov rax, 4
push rax
pop rax
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rax
mov eax, [rax]
cdqe
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rax
push rax
pop rdi
movsx rax, edi
jmp .L.return.init_array8
pop rax
push rax
pop rax
.L.return.init_array8:
mov rsp, rbp
pop rbp
ret
.globl init_array8
init_array8:
push rbp
mov rbp, rsp
sub rsp, 20
mov rax, 0
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
mov rax, 1
push rax
pop rdi
pop rax
mov [rax], edi
push rdi
pop rax
mov rax, 1
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
mov rax, 2
push rax
pop rdi
pop rax
mov [rax], edi
push rdi
pop rax
mov rax, 2
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
mov rax, 3
push rax
pop rdi
pop rax
mov [rax], edi
push rdi
pop rax
mov rax, 3
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
mov rax, 4
push rax
pop rdi
pop rax
mov [rax], edi
push rdi
pop rax
mov rax, 4
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
mov rax, 5
push rax
pop rdi
pop rax
mov [rax], edi
push rdi
pop rax
push rax
pop rax
mov rax, 0
push rax
pop rax
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rax
mov eax, [rax]
cdqe
push rax
mov rax, 4
push rax
pop rax
push rax
mov rax, 4
push rax
pop rdi
pop rax
imul rax, rdi
push rax
mov rax, rbp
sub rax, 20
push rax
pop rax
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rax
mov eax, [rax]
cdqe
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rax
push rax
pop rdi
movsx rax, edi
jmp .L.return.init_array8
pop rax
push rax
pop rax
.L.return.init_array8:
mov rsp, rbp
pop rbp
ret
test9:
.long 1
.long 2
.long 3
test9:
.long 1
.long 2
| children_cap | test9 | init_array8 |
|--------------|-------|-------------|
| 1 | fail | fail |
| 2 | fail | fail |
| 3 | ok | fail |
| 4 | ok | fail |
| 5 | ok | ok |
int test9[] = {1, 2, 3};
int init_array8() {
int a[] = {1, 2, 3, 4, 5};
return a[0] + a[4];
}
initialize_array initialize2 initialize_array initialize2 initialize
test_gcc_and_clang.sh: kcc1_gcc start compileing tests/init2.c before: 0x56156ec602d0, 2 after : 0x56156ec60450, 4 before: 0x56156ec60450, 4 after : 0x56156ec60620, 8 final len: 5 init->children: 0x56156ec60620 init->len: 5 init->children: (nil) init->children: (nil) init->children: (nil) init->children: (nil) init->children: (nil) before: 0x56156ec630d0, 2 after : 0x56156ec63250, 4 final len: 3 init->children: 0x56156ec63250 init->len: 3 init->children: (nil) init->children: (nil) init->children: (nil) test_gcc_and_clang.sh: kcc1_clang start compileing tests/init2.c before: 0x607000000020, 2 after : 0x60e000000040, 4 before: 0x60e000000040, 4 after : 0x612000000040, 8 final len: 5 init->children: 0x612000000040 init->len: 5 init->children: (nil) init->children: (nil) init->children: 0xbebebebebebebebe init->len: -1094795586 init->children: 0xbebebebebebebebe init->len: -1094795586 init->children: 0xbebebebebebebebe init->len: -1094795586 before: 0x607000000090, 2 after : 0x60e000000120, 4 final len: 3 init->children: 0x60e000000120 init->len: 3 init->children: (nil) init->children: (nil) init->children: 0xbebebebebebebebe init->len: -1094795586
① このコンパイラでは、メモリ確保は memory_alloc という関数を使って行っているが、こいつは裏で calloc を呼び出しているので、必ずゼロ埋めされたメモリ領域が返ってくる ② しかし、メモリ再確保は libc の realloc をそのまま呼んでおり、こいつはメモリをゼロ埋めするとは限らないし、なんなら clang のサニタイザは意図的に 0xBEBEBEBE で埋めた領域を返してくる ③ で、未初期化のフィールドを読んでるので、ヌルポインタが入ってるところが期待されるべき場所で 0xBEBEBEBE を読んで、コンパイラがバグっている ④ 解決策としては、以下の二つのどちらか(あるいは両方)が考えられる。 A.「このコンパイラでは、ヒープ領域のうち代入してないフィールドは常に 0 かヌルポインタであることを仮定する」という暗黙の規約を守るために、realloc(ソースコード中で 2 回登場)した領域を必ずゼロクリアする B. ヒープ上に確保した構造体のありとあらゆるフィールドに 0 を入れて回る
2018年8月31日 (Day 50) セルフホストに向けて 構造体を関数に渡すのは放棄しよう。スタック使いたくないし。 ということで構造体を関数で渡している部分をポインタ渡しにするようにしたので、 codegen_expression.c をセルフコンパイルできた。 グローバル変数へのアクセスを全部GOTPCREL経由にしたところ、stderrがちゃんと使えるようになった。よって std.c と error.c をセルフコンパイル。 「今構造体周りってどこまでできてたっけ?」と思ったので実験をしている。a.b.cとか普通にできるのか。
# uname -a
Linux uint 5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
# gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# uname -a
Linux LAPTOP-BKHPSENK 4.19.128-microsoft-standard #1 SMP Tue Jun 23 12:58:10 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
# gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ uname -a
Linux KARINTO 4.19.128-microsoft-standard #1 SMP Tue Jun 23 12:58:10 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
AddressSanitizer:DEADLYSIGNAL
=================================================================
==27109==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x0001089add29 bp 0x7ffee7250dd0 sp 0x7ffee7250da8 T0)
==27109==The signal is caused by a READ memory access.
==27109==Hint: this fault was caused by a dereference of a high value address (see register values below). Dissassemble the provided pc to learn which register was used.
#0 0x1089add29 in .Lbegin8+0x55 (prpr:x86_64+0x100001d29)
#1 0x1089aea01 in .Lswitch1+0x16 (prpr:x86_64+0x100002a01)
#2 0x1089ae908 in .Lend35+0x1d (prpr:x86_64+0x100002908)
#3 0x1089aeaac in .Lswitch6+0x41 (prpr:x86_64+0x100002aac)
#4 0x1089ae391 in .Lend26+0x7f (prpr:x86_64+0x100002391)
#5 0x1089ae4e9 in include+0x14a (prpr:x86_64+0x1000024e9)
#6 0x1089aea21 in .Lswitch2+0x16 (prpr:x86_64+0x100002a21)
#7 0x1089aeef0 in main+0x3a0 (prpr:x86_64+0x100002ef0)
#8 0x7fff71a72cc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)
==27109==Register values:
rax = 0x02ffffff00000002 rbx = 0x00007ffee7253640 rcx = 0x0000000117b3de6c rdx = 0x0000000000000000
rdi = 0x0000000000000000 rsi = 0x00000000000120a8 rbp = 0x00007ffee7250dd0 rsp = 0x00007ffee7250da8
r8 = 0x00000000000130a8 r9 = 0x0000000000000000 r10 = 0x00007ffee7250dc4 r11 = 0x00007fff9859abf0
r12 = 0x0000000000000000 r13 = 0x0000000000000000 r14 = 0x0000000000000000 r15 = 0x0000000000000000
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (prpr:x86_64+0x100001d29) in .Lbegin8+0x55
==27109==ABORTING
$ gdb prpr/prpr
@(gdb) run (適当なcファイル)
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7f0af0b in ?? () from /usr/lib/libc.so.6
@(gdb) backtrace
#0 0x00007ffff7f0af0b in ?? () from /usr/lib/libc.so.6
#1 0x0000555555555636 in read_file (name=0x5555d2a0 <error: Cannot access memory at address 0x5555d2a0>)
at fileutil.c:16
#2 0x00005555555563ba in main (argc=2, argv=0x7fffffffe6f8) at main.c:33
このように、Cは、現場の人間が、自分たちの用途のために、必要に応じて作成した言語です。その後もCは(中略)かなり行き当たりばったりに機能拡張を繰り返してきました。
Cは、プログラマーは全知全能であるという理念の元に設計されています。Cの設計で優先されたのは、 ・いかにコンパイラを簡単に実装できるか(Cを使う人が、如何に簡単にプログラムを実装できるか、ではない) ・いかに高速な実行コードを吐くソースが書けるか(いかにコンパイラで最適化して、高速な実行コードを吐くか、ではない)
hsjoihs 試しに実測してみました。 https://en.wikipedia.org/wiki/Serif の本文で 4分半ぐらいですかね 16:04 えーと 2800 単語を 270 秒で読んでるので、1 秒に 10 単語強ですかねぇ 16:10 https://irisreading.com/what-is-the-average-reading-speed/ これの本文が2分半。1525 単語あるっぽいので、やっぱり 1 秒に 10 単語強ですね
struct Expr {
enum ExprCategory category;
union {
struct {
enum UnaryOp op;
struct Expr* ptr1;
} unary;
struct {
enum BinaryOp op;
struct Expr* ptr1;
struct Expr* ptr2;
} binary;
};
};
switch(expr.category) {
case UNARY_EXPR: // expr.unary.opとか
case BINARY_EXPR: // expr.binary.opとか
}
// expr.category == UNARY_EXPRのときにexpr.binaryにアクセスできてしまう
case IF_STATEMENT: {
int label1 = get_new_label_name(ptr_prs);
int label2 = get_new_label_name(ptr_prs);
const struct Expr expr = ref_sta->expr1;
print_expression(ptr_prs, &expr);
gen_if_zero_jmp_nbyte(
size_of_basic(&expr.details.type, "condition of `if`"), label1, 0);
gen_discard();
print_statement(ptr_prs, ref_sta->inner_statement);
gen_jump(label2, "if statement");
gen_label(label1);
gen_discard();
gen_label(label2);
return;
}
When compiling in ISO C mode by GCC or Clang (e.g. with option -std=c11), __asm__ must be used instead of asm.
7.1.3 Reserved identifiers 1 Each header declares or defines all identifiers listed in its associated subclause, and optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers. All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use. All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces. Each macro name in any of the following subclauses (including the future library directions) is reserved for use as specified if any of its associated headers is included; unless explicitly stated otherwise (see 7.1.4). All identifiers with external linkage in any of the following subclauses (including the future library directions) and errno are always reserved for use as identifiers with external linkage.184) Each identifier with file scope listed in any of the following subclauses (including the future library directions) is reserved for use as a macro name and as an identifier with file scope in the same name space if any of its associated headers is included.
J.5 Common extensions 1 The following extensions are widely used in many systems, but are not portable to all implementations. The inclusion of any extension that may cause a strictly conforming program to become invalid renders an implementation nonconforming. Examples of such extensions are new keywords, extra library functions declared in standard headers, or predefined macros with names that do not begin with an underscore.
A preprocessing directive of the form # include <h-char-sequence> new-line searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the < and > delimiters, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.
#if VERSION == 1
#define INCFILE "vers1.h"
#elif VERSION == 2
#define INCFILE "vers2.h" // and so on
#else
#define INCFILE "versN.h"
#endif
#include INCFILE
init.map_or(Ok(None), |expr| self.down_expr(expr, lvar_map).map(|expr| Some(expr)))?
./test.sh
a=3; if(a==1) return 1; if(a==2) return 2; if(a==3) return 3; => 3 expected, but got 179
make: *** [Makefile:11: test] Error 1
.intel_syntax noprefix
.globl main
main:
push rbp
mov rbp, rsp
sub rsp, 8
mov rax, rbp
sub rax, 8
push rax
push 3
pop rdi
pop rax
mov [rax], rdi
push rdi
pop rax
mov rax, rbp
sub rax, 8
push rax
pop rax
mov rax, [rax]
push rax
push 1
pop rdi
pop rax
cmp rax, rdi
sete al
movzb rax, al
push rax
pop rax
cmp rax, 0
je .L.end.0
push 1
pop rax
mov rsp, rbp
pop rbp
ret
.L.end.0:
pop rax
mov rax, rbp
sub rax, 8
push rax
pop rax
mov rax, [rax]
push rax
push 2
pop rdi
pop rax
cmp rax, rdi
sete al
movzb rax, al
push rax
pop rax
cmp rax, 0
je .L.end.1
push 2
pop rax
mov rsp, rbp
pop rbp
ret
.L.end.1:
pop rax
mov rax, rbp
sub rax, 8
push rax
pop rax
mov rax, [rax]
push rax
push 3
pop rdi
pop rax
cmp rax, rdi
sete al
movzb rax, al
push rax
pop rax
cmp rax, 0
je .L.end.2
push 3
pop rax
mov rsp, rbp
pop rbp
ret
.L.end.2:
pop rax
mov rsp, rbp
pop rbp
ret
スカラとは、char, int, double, 列挙型などの算術型、およびポインタを指します。(中略) 初期の C では、一度に扱えるのはスカラだけでした。 (中略) 初期の C では、一度にできることといえば、スカラという「小さな」型を、右から左に動かしたり、スカラ同士で演算したり、スカラ同士で比較したりすることだけでした。 C はそういう言語です。入出力はおろか、配列や構造体すら、言語自体の機能でまとめて扱うことを放棄した言語なのです。 ただし、ANSI C では、以下の点で、集成体型をまとめて扱うことが可能になっています。 ・構造体の一括代入 ・構造体を関数の引数として渡す ・構造体を関数の戻り値として返す ・auto 変数の初期化
C では、すべての変数は、一般的に使う前に、普通は関数の始めの実行可能分の前のところで宣言しておかなければならない。
p.297 compound-statement: { declaration-list_opt statement-list_opt }
On the MS/DOS 16 bit compilers, you had different "modes", and data pointers weren't necessarily the same size as function pointers. But at least on the ones I used, all data pointers (including void*) always had the same size. (Of course, you couldn't convert a function pointer to void*, since void* might be smaller. But according to the standard, you can't do that today, either.)
You don't even have to have a Harvard architecture to have code and data pointers using different address spaces - the old DOS "Small" memory model did this (near pointers with CS != DS)
For those who remember MS-DOS, Windows 3.1 and older the answer is quite easy. All of these used to support several different memory models, with varying combinations of characteristics for code and data pointers. So for instance for the Compact model (small code, large data): sizeof(void *) > sizeof(void(*)()) and conversely in the Medium model (large code, small data): sizeof(void *) < sizeof(void(*)()) In this case you didn't have separate storage for code and date but still couldn't convert between the two pointers (short of using non-standard __near and __far modifiers).
use serde::{Deserialize, Serialize};
use serde_json::Result;
#[derive(Serialize, Deserialize)]
struct Person {
name: String,
age: u8,
phones: Vec<String>,
}
fn typed_example() -> Result<()> {
// Some JSON input data as a &str. Maybe this comes from the user.
let data = r#"
{
"name": "John Doe",
"age": 43,
"phones": [
"+44 1234567",
"+44 2345678"
]
}"#;
// Parse the string of data into a Person object. This is exactly the
// same function as the one that produced serde_json::Value above, but
// now we are asking it for a Person as output.
let p: Person = serde_json::from_str(data)?;
// Do things just like with any other Rust data structure.
println!("Please call {} at the number {}", p.name, p.phones[0]);
Ok(())
}
global:
.long 0
main:
mov rax, global
mov rdi, 5
mov DWORD PTR [rax], edi
The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an expression that designates a bit-field member. The _Alignof operator shall not be applied to a function type or an incomplete type.
if [ "`cat tmp/err | wc -l | tr -d ¥" ¥"`" != "0" ]; then
echo "--- report ---------------"
cat tmp/err | sort
echo "test failed : `wc -l tmp/err | tr -d " tmp/er"` errors"
exit 1
fi
int exit(int status);
int test30()
{
char char_var = -1;
assert(30, -1, char_var);
int int_var = 4;
exit(int_var + char_var);
assert(30, 3, int_var + char_var);
assert(30, 3, char_var + int_var);
return 0;
}
int assert(int index, int expected, int got);
int main()
{
char char_var = -1;
int int_var = 4;
assert(30, -1, char_var);
assert(30, 4, int_var);
assert(30, 3, int_var + char_var);
assert(30, 3, char_var + int_var);
return 0;
}
#include <stdio.h>
int assert(int index, int expected, int got)
{
printf("got: %d\n", got);
if (expected != got)
{
assertion_failed(index, expected, got);
}
else
{
passed(index);
}
return 0;
}
.intel_syntax noprefix
.global main
.text
.global main
main:
push rbp
mov rbp, rsp
sub rsp, 5
mov rax, rbp
sub rax, 1
push rax
push 0
push 1
pop rdi
pop rax
sub rax, rdi
push rax
pop rdi
pop rax
mov BYTE PTR [rax], dil
push rdi
pop rax
mov rax, rbp
sub rax, 5
push rax
push 4
pop rdi
pop rax
mov DWORD PTR [rax], edi
push rdi
pop rax
push 30
push 0
push 1
pop rdi
pop rax
sub rax, rdi
push rax
mov rax, rbp
sub rax, 1
push rax
pop rax
mov al, BYTE PTR [rax]
push rax
pop rax
movsx eax, al
push rax
pop rdx
pop rsi
pop rdi
sub rsp, 8
call assert
add rsp, 8
push rax
pop rax
push 30
push 4
mov rax, rbp
sub rax, 5
push rax
pop rax
mov eax, DWORD PTR [rax]
push rax
pop rdx
pop rsi
pop rdi
sub rsp, 8
call assert
add rsp, 8
push rax
pop rax
push 30
push 3
mov rax, rbp
sub rax, 5
push rax
pop rax
mov eax, DWORD PTR [rax]
push rax
mov rax, rbp
sub rax, 1
push rax
pop rax
mov al, BYTE PTR [rax]
push rax
pop rax
movsx eax, al
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rdx
pop rsi
pop rdi
sub rsp, 8
call assert
add rsp, 8
push rax
pop rax
push 30
push 3
mov rax, rbp
sub rax, 1
push rax
pop rax
mov al, BYTE PTR [rax]
push rax
mov rax, rbp
sub rax, 5
push rax
pop rax
mov eax, DWORD PTR [rax]
push rax
pop rdi
pop rax
add rax, rdi
push rax
pop rdx
pop rsi
pop rdi
sub rsp, 8
call assert
add rsp, 8
push rax
pop rax
push 0
pop rax
jmp .Lmain_ret
.Lmain_ret:
mov rsp, rbp
pop rbp
ret
#include <stdio.h>
void f(int n) {
puts("\xA" "B");
puts("\xAB");
}
.LC0:
.string "\nB"
.LC1:
.string "\253"
f:
subq $8, %rsp
movl $.LC0, %edi
call puts
movl $.LC1, %edi
addq $8, %rsp
jmp puts
5.1.1.2 Translation phases 1 The precedence among the syntax rules of translation is specified by the following phases.6) 1. Physical source file multibyte characters are mapped, in an implementation- defined manner, to the source character set (introducing new-line characters for end-of-line indicators) if necessary. Trigraph sequences are replaced by corresponding single-character internal representations. 2. Each instance of a backslash character () immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. Only the last backslash on any physical source line shall be eligible for being part of such a splice. A source file that is not empty shall end in a new-line character, which shall not be immediately preceded by a backslash character before any such splicing takes place. 3. The source file is decomposed into preprocessing tokens7) and sequences of white-space characters (including comments). A source file shall not end in a partial preprocessing token or in a partial comment. Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is implementation-defined. 4. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. If a character sequence that matches the syntax of a universal character name is produced by token concatenation (6.10.3.3), the behavior is undefined. A #include preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted. 5. Each source character set member and escape sequence in character constants and string literals is converted to the corresponding member of the execution character set; if there is no corresponding member, it is converted to an implementation- defined member other than the null (wide) character.8) 6. Adjacent string literal tokens are concatenated. 7. White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. The resulting tokens are syntactically and semantically analyzed and translated as a translation unit. 8. All external object and function references are resolved. Library components are linked to satisfy external references to functions and objects not defined in the current translation. All such translator output is collected into a program image which contains information needed for execution in its execution environment.
.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.
time : 194 sec
--- report ---------------
9CC COMPILE KO => arg11.c
9CC COMPILE KO => arg2.c
9CC COMPILE KO => arg3.c
9CC COMPILE KO => arg4.c
9CC COMPILE KO => arg5.c
9CC COMPILE KO => arg6.c
9CC COMPILE KO => ret_struct0.c
9CC COMPILE KO => ret_struct1.c
9CC COMPILE KO => ret_struct2.c
9CC COMPILE KO => ret_struct3.c
9CC COMPILE KO => ret_struct4.c
9CC COMPILE KO => ret_struct5.c
9CC COMPILE KO => varg.c
9CC COMPILE KO => varg3.c
9CC KO => arg7.c
9CC KO => arg8.c
OUTPUT KO varg2.c > ./tmp/act_varg2.c.txt ./tmp/exp_varg2.c.txt
STATUS KO global7.c > act:139 exp:0
test failed : 18 errors
typedef struct s_node
{
t_nodekind kind;
struct s_node *lhs;
struct s_node *rhs;
t_type *type;
t_lvar *lvar; // local var
struct s_node *lvar_assign; // type ident = expr;
t_str_elem *def_str;
struct s_defvar
{
char *name;
int name_len;
t_type *type;
// for global variable
bool is_extern;
bool is_static;
struct s_node *assign;
} *var_global;
struct s_node *global_assign_next;
// num
int val;
// else of if
struct s_node *elsif;
struct s_node *els;
struct s_node *for_expr[3]; // for (0; 1; 2)
// call
struct s_deffunc
{
char *name;
int name_len;
t_type *type_return;
int argcount;
char *argument_names[20];
int argument_name_lens[20];
t_type *argument_types[20];
bool is_static;
bool is_prototype;
bool is_zero_argument; // func(void)
bool is_variable_argument; // func(, ...)
t_lvar *locals;
struct s_node *stmt;
} *funcdef;
int funccall_argcount;
struct s_node *funccall_args[20];
struct s_deffunc*funccall_caller;
t_member *elem;
// valとlabelでswicth-case
t_switchcase *case_label;
bool switch_has_default; // TODO labelstackに移動する
t_lvar *switch_save; // switch (lvar)
t_labelstack *block_sbdata;
// analyze
bool is_analyzed;
char *analyze_source;
char *analyze_var_name;
int analyze_var_name_len;
char *analyze_funccall_name;
int analyze_funccall_name_len;
char *analyze_member_name;
int analyze_member_name_len;
} t_node;
struct Expr typecheck_expression(struct AnalyzerState *ptr_ps,
const struct UntypedExpr *ref_uexpr)
struct AnalyzerState {
struct ScopeChain scope_chain;
struct Map2 * /*<Type>*/ global_vars_type_map;
struct Map2 * /*<Type>*/ func_info_map;
struct Type func_ret_type;
int newest_offset;
struct Map2 * /*<StructOrUnionInternalCompleteInfo>*/
global_struct_or_union_tag_map;
struct Map2 * /*<struct Vector<EnumeratorAndValue>>*/ global_enum_tag_map;
struct Vector /*<EnumeratorAndValue>*/ global_enumerator_list;
const char *current_function_name;
};
9cc/9cc> make test ARCH=x8664
9CC COMPILE KO => void1.c
9CC COMPILE KO => void2.c
9CC COMPILE KO => while1.c
9CC COMPILE KO => while2.c
test failed : 287 errors
make: *** [Makefile:15: test] Error 1
int main()
{
return sizeof("123456789") / sizeof(char);
}
#ifdef __STDC__
#include <stdarg.h>
typedef struct __FILE FILE;
int fprintf(FILE *restrict, const char *restrict, ...);
int printf(const char *restrict, ...);
int sprintf(char *restrict s, const char *restrict format, ...);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
#endif
#ifndef __STDC__
int fprintf();
int printf();
int sprintf();
int vfprintf(struct __FILE *stream, const char *format,
struct va_list_tag arg[1]);
int vprintf(const char *format, struct va_list_tag arg[1]);
#endif
#ifdef __STDC__
#define size_t2 unsigned long
#endif
#ifndef __STDC__
#define size_t2 int
#endif
void *calloc(size_t2 nmemb, size_t2 size);
void *realloc(void *ptr, size_t2 size);
mikiken@DESKTOP-CM4259U:~/c-compiler$ make
gcc -Wall -Wextra -DOVERRIDE_STD -g std.c codegen.c alignment.c parse_analyze_toplevel.c parse_analyze_statement.c codegen_expression.c main.c vector.c typecheck_expression.c parse_expression.c error.c type.c parse_type.c map.c print_x86_64.c print_x86_64_unofficial.c lexer.c codegen_switch.c file_io.c cpp.c -DLINUX -o out/compiler.out
typecheck_expression.c: In function ‘is_strictly_equal’:
typecheck_expression.c:159:2: warning: #warning should typecheck [-Wcpp]
159 | #warning should typecheck
| ^~~~~~~
typecheck_expression.c: In function ‘is_compatible’:
typecheck_expression.c:244:2: warning: #warning should typecheck [-Wcpp]
244 | #warning should typecheck
| ^~~~~~~
cpp.c: In function ‘replace_recursively’:
cpp.c:338:2: warning: #warning only one token [-Wcpp]
338 | #warning only one token
| ^~~~~~~
cp -p out/compiler.out out/compiler_1stgen.out
main()
{
assert(0, 3, test0());
assert(0, 2, test0_2());
assert(0, 10, test0_3());
assert(1, 9, test1());
assert(2, 5, test2(5));
assert(3, 5, test3(5));
assert(4, 2, test4(3));
assert(4, 3, test4(4));
assert(5, 10, test5(5));
assert(6, 2, test6(2));
assert(7, 0, test7(1));
assert(7, 0, test7(91));
assert(7, 1, test7(109));
print_ok();
return 0;
}
test0() { return 3; }
test0_2() { return g(2); }
test0_3() { return h(5); }
g(int n) { return n; }
h(int n)
{
if (n > 5)
{
return 5;
}
else
{
return 10;
}
}
test1() { return f(5); }
f(int n)
{
int i;
i = 3;
if (n > 4)
{
return f(n - 1) + 2;
}
return i + n;
}
test2(int i)
{
if (i == 0)
{
return 0;
}
if (i == 1)
{
return 1;
}
return i;
}
test3(int i)
{
if (i == 0)
{
return 0;
}
if (i == 1)
{
return 1;
}
return test3(i - 1) + 1;
}
test4(int i)
{
if (i == 0)
{
return 0;
}
if (i == 1)
{
return 1;
}
return test4(i - 1) + test4(i - 2);
}
test5(int i) { return add(i, 1000000) + sub(i, 1000000); }
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
test6(int i)
{
if (i == 0)
{
return 1;
}
return i * test6(i - 1);
}
test7(int n)
{
if (n == 1)
{
return 0;
}
int result;
result = 1;
int i;
for (i = 2; i < n; i = i + 1)
{
if (n % i == 0)
{
result = 0;
}
}
return result;
}
kanapo@pon:~/seccamp/kcc$ make test2
sh tests/test-control.sh kcc2
test-control.sh: kcc2 start compileing tests/align.c
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
./kcc2: Symbol `fprintf' causes overflow in R_X86_64_PC32 relocation
Any number of derived types can be constructed from the object and function types, as follows: An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. The element type shall be complete whenever the array type is specified. Array types are characterized by their element type and by the number of elements in the array. An array type is said to be derived from its element type, and if its element type is T , the array type is sometimes called ''array of T ''. The construction of an array type from an element type is called ''array type derivation''.
C11: Types are partitioned into object types (types that describe objects) and function types (types that describe functions). At various points within a translation unit an object type may be incomplete (lacking sufficient information to determine the size of objects of that type) or complete (having sufficient information).
int main() {
int a;
{int *k;}
{int *i; {int k;}}
int c[2];
}
#ifdef __STDC__
#include <stdarg.h>
typedef struct __FILE FILE;
int fprintf(FILE *restrict, const char *restrict, ...);
int printf(const char *restrict, ...);
int sprintf(char *restrict s, const char *restrict format, ...);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
#endif
#ifndef __STDC__
int fprintf();
int printf();
int sprintf();
int vfprintf(struct __FILE *stream, const char *format,
struct va_list_tag arg[1]);
int vprintf(const char *format, struct va_list_tag arg[1]);
#endif
// This switch-cases is an interesting example of magical aspects
// of self-hosting compilers. Here, we teach the compiler about
// escaped sequences using escaped sequences themselves.
// This is a tautology. The information about their real character
// codes is not present in the source code but propagated from
// a compiler compiling the source code.
// See "Reflections on Trusting Trust" by Ken Thompson for more info.
// http://cm.bell-labs.com/who/ken/trust.html
switch (c) {
case '\'': case '"': case '?': case '\\':
return c;
case 'a': return '\a';
case 'b': return '\b';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'v': return '\v';
case 'e': return '\033'; // '\e' is GNU extension
case 'x': return read_hex_char();
case 'u': return read_universal_char(4);
case 'U': return read_universal_char(8);
case '0' ... '7': return read_octal_char(c);
}
;;;;;;
int main() { return 0;;;;;; }
CLANG_WARN=-Wall -Wextra -Wimplicit-fallthrough -Weverything -Wno-documentation -Wno-padded -Wno-missing-prototypes -Wno-switch-enum -Wno-sign-conversion -Wno-format-nonliteral
warn:
make format
clang $(CLANG_WARN) -DOVERRIDE_STD $(SRC) $(OSFLAG) -o out/compiler.out
clang $(CLANG_WARN) misc/assembly_sandbox.c print_x86_64.c print_x86_64_unofficial.c $(OSFLAG) -o out/assembly_sandbox.out
clang -Wall -Wextra -Wimplicit-fallthrough $(OSFLAG) $(TYPECHECKSRC) -o out/typeparse_check.out
int add(int a, int b) { return a + b; }
int main() {
int c = add(2, 3);
return 0;
}
(gdb) layout asm
set disassemble-next-line on
show disassemble-next-line
valgrind
valgrind --leak-check=no myprog arg1 arg2
int neighbour_count[20][20];
int grid[20][20];
int grid_binaries[20] = {
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000111111111100000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00000000000000000000,
0b00111100000000000000,
0b01000100000000000000,
0b00000100000000000000,
0b01001000000000000000,
0b00000000000000000000,
0b00000000000000000000};
init_grid(grid, grid_binaries);
int i;
int j;
int steps;
run_test 379 "int main() { char a; return sizeof a; }" 1
run_test 380 "int main() { char a; return sizeof +a; }" 4
run_test 377 'int main() { int a[2][3]; return sizeof a; }' 24
run_test 378 'int main() { int a[2][3]; return sizeof (a+0); }' 8
run_test 379 "int main() { return sizeof 'C'; }" 4
run_test 380 "int main() { char a; return sizeof a; }" 1
run_test 381 "int main() { char a; return sizeof +a; }" 4
#include <stdio.h>
int main() {
sizeof(
struct A {
int a;
});
struct A s = {1};
printf("%d\n", s.a);
}
4 NOTE 3 Expressions that are not evaluated do not access objects.
Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator.
7.31 Future library directions 7.31.13 String handling <string.h> 1 Function names that begin with str, mem, or wcs and a lowercase letter may be added to the declarations in the <string.h> header.
enum structure { // reserved
isomorphic, // reserved
nonisomorphic
};
void memorize_secret( // reserved
const char *string // reserved
);
struct toxicology { // reserved
enum condition {
cnd_clean, // reserved
cnd_dirty // reserved
} cnd;
};
#define ENTOMOLOGY 1 // reserved
#define SIGNIFICANT_RESULTS 1 // reserved
#define TIME_TO_EAT 1 // reserved
#define ATOMIC_WEIGHT .000001f // reserved
#define INTERESTING_VALUE_MIN 0 // reserved
k;
double sin(), cos();
main() {
float A = 0, B = 0, i, j, z[1760];
char b[1760];
printf("\x1b[2J");
for (;;) {
memset(b, 32, 1760);
memset(z, 0, 7040);
for (j = 0; 6.28 > j; j += 0.07)
for (i = 0; 6.28 > i; i += 0.02) {
float c = sin(i), d = cos(j), e = sin(A), f = sin(j), g = cos(A),
h = d + 2, D = 1 / (c * h * e + f * g + 5), l = cos(i),
m = cos(B), n = sin(B), t = c * h * g - f * e;
int x = 40 + 30 * D * (l * h * m - t * n),
y = 12 + 15 * D * (l * h * n + t * m), o = x + 80 * y,
N = 8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n);
if (22 > y && y > 0 && x > 0 && 80 > x && D > z[o]) {
z[o] = D;
b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];
}
}
printf("\x1b[H");
for (k = 0; 1761 > k; k++)
putchar(k % 80 ? b[k] : 10);
A += 0.04;
B += 0.02;
}
}
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define FloatTimes10000 int
FloatTimes10000 mul(FloatTimes10000 a, FloatTimes10000 b) {
return a * b / 10000;
}
int k;
void update2(FloatTimes10000 *cos, FloatTimes10000 *sin, FloatTimes10000 cos_delta, FloatTimes10000 sin_delta) {
FloatTimes10000 new_cos = mul(*cos, cos_delta) - mul(*sin, sin_delta);
FloatTimes10000 new_sin = mul(*sin, cos_delta) + mul(*cos, sin_delta);
*cos = new_cos;
*sin = new_sin;
}
int main() {
FloatTimes10000 zTimes10000[1760];
char b[1760];
printf("\e[2J");
FloatTimes10000 cosATimes10000 = 10000; FloatTimes10000 sinATimes10000 = 0;
FloatTimes10000 cosBTimes10000 = 10000; FloatTimes10000 sinBTimes10000 = 0;
for (;;update2(&cosATimes10000, &sinATimes10000, 9992, 400), update2(&cosBTimes10000, &sinBTimes10000, 9998, 200), usleep(50000)) {
memset(b, 32, 1760 * sizeof(char));
memset(zTimes10000, 0, 1760 * sizeof(FloatTimes10000));
FloatTimes10000 sinthetaTimes10000 = 0; FloatTimes10000 costhetaTimes10000 = 10000;
for (int theta_times_14 = 0; theta_times_14 < 88; theta_times_14++, update2(&costhetaTimes10000, &sinthetaTimes10000, 9974 + theta_times_14 % 2, 714)){
FloatTimes10000 sinphiTimes10000 = 0; FloatTimes10000 cosphiTimes10000 = 10000;
for (int phi_times_50 = 0; phi_times_50 < 314; phi_times_50++, update2(&cosphiTimes10000, &sinphiTimes10000, 9998, 200)) {
FloatTimes10000 hTimes10000 = costhetaTimes10000 + 20000;
FloatTimes10000 DTimes10000 = 100000000 / (
mul(mul(sinphiTimes10000, hTimes10000), sinATimes10000)
+ mul(sinthetaTimes10000, cosATimes10000)
+ 50000);
FloatTimes10000 tTimes10000 = mul(mul(sinphiTimes10000, cosATimes10000), hTimes10000) - mul(sinthetaTimes10000, sinATimes10000);
int x = 40 + 30 * mul(DTimes10000, mul(mul(cosphiTimes10000, cosBTimes10000), hTimes10000) - mul(tTimes10000 , sinBTimes10000)) / 10000,
y = 12 + 15 * mul(DTimes10000, mul(mul(cosphiTimes10000, sinBTimes10000), hTimes10000) + mul(tTimes10000 , cosBTimes10000)) / 10000,
o = x + 80 * y,
N = 8 * (
mul(mul(sinthetaTimes10000, sinATimes10000) - mul(mul(sinphiTimes10000, cosATimes10000), costhetaTimes10000), cosBTimes10000)
- mul(mul(sinphiTimes10000, sinATimes10000), costhetaTimes10000)
- mul(sinthetaTimes10000, cosATimes10000)
- mul(mul(cosphiTimes10000, sinBTimes10000), costhetaTimes10000)
) / 10000;
if (22 > y && y > 0 && x > 0 && 80 > x && DTimes10000 > zTimes10000[o]) {
zTimes10000[o] = DTimes10000;
b[o] = ".,-~:;=!*#$@"[N >= 1 ? N : 0];
}
}
}
printf("\e[H");
for (k = 0; 1761 > k; k++)
putchar(k % 80 ? b[k] : 10);
}
}
#include<string.h>
#include<stdio.h>
#include<unistd.h>
int m(int a,int b){return a*b/10000;}void a(int*c,int*s,int d,int t){int k=m(*c,d)-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l;}
int main(){int z[1760];char b[1760];printf("\e[2J");int q=10000;int r=0;int u=10000;int v=0;for(;;a(&q,&r,9992,400),a(&u,&v,9998,200),usleep(50000)){memset(b,32,1760);memset(z,0,1760*sizeof(int));int l=0;int p=10000;for(int i=0;i<88;i++,a(&p,&l,9974+i%2,714)){int w=0;int e=10000;for(int j=0;j<314;j++,a(&e,&w,9998,200)){int f=p+20000;int g=100000000/(m(m(w,f),r)+m(l,q)+50000);int t=m(m(w,q),f)-m(l,r);int x=40+30*m(g,m(m(e,u),f)-m(t,v))/10000,y=12+15*m(g,m(m(e,v),f)+m(t,u))/10000,o=x+80*y,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))/10000;if(22>y&&y>0&&x>0&&80>x&&g>z[o]){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0];}}}printf("\e[H");for(int k=0;k<1761;k++)putchar(k%80?b[k]:10);}}
void *memset();int printf();int usleep();int putchar();
int m(int a,int b){return (a*b+5000)/10000;}void a(int*c,int*s,int d,int t){int k=m(*c,d)-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l;}
int main(){int z[1760];char b[1760];printf("\e[2J");int q=10000;int r=0;int u=10000;int v=0;for(;;a(&q,&r,9992,400),a(&u,&v,9998,200),usleep(50000)){memset(b,32,1760);memset(z,0,1760*sizeof(int));int l=0;int p=10000;for(int i=0;i<88;i++,a(&p,&l,9974+i%2,714)){int w=0;int e=10000;for(int j=0;j<314;j++,a(&e,&w,9998,200)){int f=p+20000;int g=100000000/(m(m(w,f),r)+m(l,q)+50000);int t=m(m(w,q),f)-m(l,r);int x=40+30*m(g,m(m(e,u),f)-m(t,v))/10000;int y=12+15*m(g,m(m(e,v),f)+m(t,u))/10000;int o=x+80*y;int 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))/10000;if(22>y&&y>0&&x>0&&80>x&&g>z[o]){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0];}}}printf("\e[H");for(int k=0;k<1761;k++)putchar(k%80?b[k]:10);}return 0;}
int putchar();void*
memset();int m(int a,int b)
{return (a*b+5000)/10000;}void a(
int*c,int*s,int d,int t){int k=m(*c,d
)-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l
;}int usleep();int printf();int main(){int z[
1760];char b[1760];printf("\e[2J");int s=10000;
int q=s;int r=0;int u=s;int v=0;for(;;a(&q,&r,s-8
,400),a(&u,&v,s-2,200)){memset(b,32,1760);memset(z,
0,7040);int l=0;int p=s;for(int i=0;i<88;++
i,a(&p,&l,9974+i %2,714)){int w=0;int
e=s;for(int j=0; j<314;j++,a(&e,&w,s-
2,200)){int f=p +2*s;int g=s*s/(m(m
(w,f),r)+m(l,q)+ 5*s);int t=m(m(w,q),
f)-m(l,r);int x= 40+30*m(g,m(m(e,u),f
)-m(t,v))/s;int y=12+15*m(g,m(m(e,v
),f)+m(t,u))/s; int o=x+80*y;int 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;if(y>0&&g>z[o]&&22
>y&&x>0&&80>x){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0]
;}}}printf("\e[H");for(int k=0;k<1761;k++)putchar
(k%80?b[k]:10);/*printf("Originally written by"
" @a1k0n. Rewritten by @hsjoihs so that it c"
"ompiles without any floating-point arit"
"hmetics.foobarbazloremipsumfo\n");*/
usleep(5*s);}return 0;}/*foobarba
brfnuwdoigjkrtfdgjvkmlsfdgr
donutdonutdonutdon*/
int putchar();void*
memset();int m(int a,int b)
{return (a*b+5000)/10000;}void a(
int*c,int*s,int d,int t){int k=m(*c,d
)-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l
;}int usleep();int printf();int main(){int z[
1760];char b[1760];printf("\e[2J");int s=10000;
int q=s;int r=0;int u=s;int v=0;for(;;a(&q,&r,s-8
,400),a(&u,&v,s-2,200)){memset(b,32,1760);memset(z,
0,7040);int l=0;int p =s;for(int i=0;i<88;i
++,a(&p,&l,9974+i% 2,714)){int w=0;int
e=s;for(int j=0;j< 314;j++,a(&e,&w,s-
2,200)){int f=p+2 *s;int g=s*s/(m(m
(w,f),r)+m(l,q)+5* s);int t=m(m(w,q),
f)-m(l,r);int x=40 +30*m(g,m(m(e,u),f
)-m(t,v))/s;int y =12+15*m(g,m(m(e,
v),f)+m(t,u))/s;int o=x+80*y;int 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 ;if(y>0&&g>z[o]&&y<22
&&x>0&&80>x){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0]+0
;}}}printf("\e[H");for(int k=0;k<1761;k++)putchar
(k%80?b[k]:10);printf("Author: @a1k0n. Rewritt"
"en by @hsjoihs so that it works without flo"
);https://github.com/hsjoihs/c-compiler/
printf("ating types.\nNote that roun"
"ding errors gradually reduce th"
"e donut's size.\n");usleep
(50000);}return 0;}
k;double sin()
,cos();main(){float A=
0,B=0,i,j,z[1760];char b[
1760];printf("\x1b[2J");for(;;
){memset(b,32,1760);memset(z,0,7040)
;for(j=0;6.28>j;j+=0.07)for(i=0;6.28
>i;i+=0.02){float c=sin(i),d=cos(j),e=
sin(A),f=sin(j),g=cos(A),h=d+2,D=1/(c*
h*e+f*g+5),l=cos (i),m=cos(B),n=s\
in(B),t=c*h*g-f* e;int x=40+30*D*
(l*h*m-t*n),y= 12+15*D*(l*h*n
+t*m),o=x+80*y, N=8*((f*e-c*d*g
)*m-c*d*e-f*g-l *d*n);if(22>y&&
y>0&&x>0&&80>x&&D>z[o]){z[o]=D;;;b[o]=
".,-~:;=!*#$@"[N>0?N:0];}}/*#****!!-*/
printf("\x1b[H");for(k=0;1761>k;k++)
putchar(k%80?b[k]:10);A+=0.04;B+=
0.02;}}/*****####*******!!=;:~
~::==!!!**********!!!==::-
.,~~;;;========;;;:~-.
..,--------,*/
#ifdef __STDC__
#include <stdio.h>
#include <stdlib.h>
#else
int printf();
#endif
#ifdef __STDC__
#include <stdarg.h>
typedef struct __FILE FILE;
int fprintf(FILE *restrict, const char *restrict, ...);
int printf(const char *restrict, ...);
int sprintf(char *restrict s, const char *restrict format, ...);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vprintf(const char *format, va_list arg);
#endif
#ifndef __STDC__
int fprintf();
int printf();
int sprintf();
int vfprintf(struct __FILE *stream, const char *format,
struct va_list_tag arg[1]);
int vprintf(const char *format, struct va_list_tag arg[1]);
#endif
int putchar();void*
memset();int m(int a,int b)
{return (a*b+5000)/10000;}void a(
int*c,int*s,int d,int t){int k=m(*c,d
)-m(*s,t);int l=m(*s,d)+m(*c,t);*c=k;*s=l
;}int usleep();int printf();int main(){int z[
1760];char b[1760];printf("\e[2J");int s=10000;
int q=s;int r=0;int u=s;int v=0;for(;;a(&q,&r,s-8
,400),a(&u,&v,s-2,200)){memset(b,32,1760);memset(z,
0,7040);int l=0;int p =s;for(int i=0;i<88;i
++,a(&p,&l,9974+i% 2,714)){int w=0;int
e=s;for(int j=0;j< 314;j++,a(&e,&w,s-
2,200)){int f=p+2 *s;int g=s*s/(m(m
(w,f),r)+m(l,q)+5* s);int t=m(m(w,q),
f)-m(l,r);int x=40 +30*m(g,m(m(e,u),f
)-m(t,v))/s;int y =12+15*m(g,m(m(e,
v),f)+m(t,u))/s;int o=x+80*y;int 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 ;if(y>0&&g>z[o]&&y<22
&&x>0&&80>x){z[o]=g;b[o]=".,-~:;=!*#$@"[N>=1?N:0]+0
;}}}printf("\e[H");for(int k=0;k<1761;k++)putchar
(k%80?b[k]:10);printf("Author: @a1k0n. Rewritt"
"en by @hsjoihs so that it works without flo"
);https://github.com/hsjoihs/c-compiler/
printf("ating types.\nNote that roun"
"ding errors gradually reduce th"
"e donut's size.\n");usleep
(50000);}return 0;}