x64の呼出し規約とva_argとprintfと
今日やったことの報告です。
発端はこのへんです。
なっかなか気持ちわるいですね〜https://t.co/gGA7sk5nEF pic.twitter.com/dXwKIYtzQS
— Joe (@xuzijian629) 2017年3月6日
はてなブログに投稿しました
— гасин (@_gacin) 2017年3月21日
doubleの入出力で誤差を生みたくない話 - gasin’s blog https://t.co/TBZe1youpQ #はてなブログ
追記をした
そこから色々遊んでたら、このCのコードを書くところに行き着きました。
大学の端末(Macintosh)でやりました。
$ clang --version Apple LLVM version 8.0.0 (clang-800.0.42.1) Target: x86_64-apple-darwin15.6.0 Thread model: posix $ gcc-5 --version gcc-5 (Homebrew gcc 5.3.0) 5.3.0
#include<stdio.h> #include<alloca.h> #include<string.h> double func(double a, double b, double c, double d, double e, double f) { return 0.25; } void alc() { int *p = alloca(100000); memset(p, 0xab, 100000); } int main(void) { func(0.5, 1.0, 1.5, 2.0, 2.5, 3.0); printf("%f %f %f %f %f %f\n", 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); func(0.5, 1.0, 1.5, 2.0, 2.5, 3.0); printf("%f %f %f %f %f %f\n", 5); puts("- - - - - - - - - -"); func(0.5, 1.0, 1.5, 2.0, 2.5, 3.0); printf("%f %f %f %f %f %f\n", 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); alc(); func(0.5, 1.0, 1.5, 2.0, 2.5, 3.0); printf("%f %f %f %f %f %f\n", 5); puts("- - - - - - - - - -"); func(0.5, 1.0, 1.5, 2.0, 2.5, 3.0); printf("%f %f %f %f %f %f\n", 4.0, 5.0, 6.0, 7.0, 8.0, 9.0); func(0.5, 1.0, 1.5, 2.0, 2.5, 3.0); printf("%f %f %f %f %f %f\n", 10.0); return 0; }
$ clang test.c -w -o test-clang $ ./test-clang 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 - - - - - - - - - - 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 -0.000000 -0.000000 -0.000000 -0.000000 -0.000000 -0.000000 - - - - - - - - - - 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 10.000000 10.000000 1.500000 2.000000 2.500000 3.000000 $ gcc-5 test.c -w -o test-gcc $ ./test-gcc 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 - - - - - - - - - - 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 -0.000000 -0.000000 -0.000000 -0.000000 -0.000000 -0.000000 - - - - - - - - - - 4.000000 5.000000 6.000000 7.000000 8.000000 9.000000 10.000000 1.000000 1.500000 2.000000 2.500000 3.000000
以上です。
ところで、これをideoneとかで試そうとすると、最適化がかかって、面白みが半減します。
よかったら、考えたり、手を動かしてみてください。
注目ポイントです。
- (なぜ第2引数の5は特に出力されていないのか)
— こおしいず (@cookies146) 2017年3月21日
- 引数のためのレジスタはfunc呼び出し時に書き換わっているはずなのに、なぜ前のprintfの引数が残っているのか
- 引数の数は十分少ないのに、なぜ引数がスタックに影響されるのか
- なぜ急にfuncへの引数が顔を出したのか