2010年2月13日土曜日

gccが誤ったコードを生成する(gcc 4.4.0)

最近Emacsが正しくビルドされないことがあり、少し原因を調べてみました。

gccに「-march=native -O3」オプションを渡して Emacs 23.1 をビルドすると make bootstrap が最後まで通りません。ビルドを通すためには、最適化レベルを落とすか、「-fno-tree-vectorize」オプションを加える必要があるようです(*)
*このオプションは@yunhさんから教えていただきました。

1.現象
make bootstapの最中に以下のコマンドが実行されたところでエラーダイアログが表示されます。
"./oo-spd/i386/temacs.exe" -batch -l loadup bootstrap




以下の環境でビルドしています。
OS:Windows 7 64bit
CPU: Core i7-860
ビルド環境:MinGW 5.1.6, MSYS 1.0.11, gcc 4.4.0 (mingw32)

2.解析(何がおきているのか?)
コマンドラインから上記のコマンドをたたくとエラーが再現できるので、gdbで様子を見てみました。gdbはMinGWのサイトからダウンロードした gdb-7.0.50.20100202-mingw32-bin.tar.gz を使っています。

C:\work\emacs-23.1\nt>mingw32-make.exe ARCH_CFLAGS="-c -mno-cygwin -march=native -O3" bootstrap

(中略)

echo oo-spd/i386/w32font.o oo-spd/i386/w32uniscribe.o >> oo-spd/i386/buildobj.lst
mingw32-make.exe[2]: Leaving directory `D:/temp/work/emacs-23.1/src'
"./oo-spd/i386/temacs.exe" -batch -l loadup bootstrap
mingw32-make.exe[1]: *** [bootstrap-emacs] Error 255
mingw32-make.exe[1]: Leaving directory `D:/temp/work/emacs-23.1/src'
mingw32-make.exe: *** [bootstrap-gmake] Error 2

エラーが発生したコマンドをgdb上で走らせてみます。
C:\work\emacs-23.1\nt>cd ..\src

C:\work\emacs-23.1\src>gdb .\oo-spd\i386\temacs.exe
GNU gdb (GDB) 7.0.50.20100202
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "mingw32".
For bug reporting instructions, please see:
...
Reading symbols from C:\work\emacs-23.1\src/.\oo-spd\i386\temacs.exe...done.
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) [answered Y; input not from terminal]
Environment variable "DISPLAY" not defined.
Environment variable "TERM" not defined.
Temporary breakpoint 1 at 0x10af525
(gdb) run -batch -l loadup bootstrap
Starting program: C:\work\emacs-23.1\src/.\oo-spd\i386\temacs.exe -batch -l loadup bootstrap
[New Thread 5208.0xa08]
Error: dll starting at 0x77630000 not found.
Error: dll starting at 0x76bf0000 not found.
Error: dll starting at 0x77630000 not found.
Error: dll starting at 0x77530000 not found.

Program received signal SIGSEGV, Segmentation fault.
0x010c6b52 in init_charset_once ()
(gdb) bt
#0  0x010c6b52 in init_charset_once ()
#1  0x01002ebf in main ()

Segmentation faultで落ちました。バックトレースすると main() から呼ばれた init_charset_once() の中で落ちているようです。
次に落ちている箇所のアセンブラコードとレジスタの状態を見てみることにします。
(gdb) disass
Dump of assembler code for function init_charset_once:
   0x010c6b30 <+0>:     push   %ebp
   0x010c6b31 <+1>:     mov    $0x16eedf8,%eax
   0x010c6b36 <+6>:     mov    %esp,%ebp
   0x010c6b38 <+8>:     push   %edi
   0x010c6b39 <+9>:     push   %esi
   0x010c6b3a <+10>:    push   %ebx
   0x010c6b3b <+11>:    sub    $0xc,%esp
   0x010c6b3e <+14>:    movdqa 0x16a4c30,%xmm0
   0x010c6b46 <+22>:    lea    0x400(%eax),%ecx
   0x010c6b4c <+28>:    lea    0x200(%eax),%edx
=> 0x010c6b52 <+34>:    movdqa %xmm0,(%eax)
   0x010c6b56 <+38>:    add    $0x10,%eax
   0x010c6b59 <+41>:    cmp    %edx,%eax

(中略)

   0x010c6f29 <+1017>:  lea    0x0(%esi,%eiz,1),%esi
End of assembler dump.

(gdb) info registers
eax            0x16eedf8        24047096
ecx            0x16ef1f8        24048120
edx            0x16eeff8        24047608
ebx            0x5      5
esp            0x88fe90 0x88fe90
ebp            0x88fea8 0x88fea8
esi            0x0      0
edi            0x1      1
eip            0x10c6b52        0x10c6b52
eflags         0x10206  [ PF IF RF ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x53     83
gs             0x2b     43

(gdb)

 落ちた箇所は「0x010c6b52 <+34>:    movdqa %xmm0,(%eax)」で、このときの eaxレジスタの中身は「0x16eedf8(24047096)」です。

3.問題点
このmovdqa命令ですが、調べたところ128bit単位でmovするSSE2命令らしく、使用条件として「転送元/先のアドレスが16byte(128bit)でアラインメント」されている必要があります。

今回の場合は 0x16eedf8(24047096) なので16で割ると...割り切れません。正しくアラインメントされたアドレスを使っていないようです。。。

問題が発生したオプションは「-march=native -O3」でした。僕の環境では -march=native は以下の様に解釈されているようです。(CPUはCore i7-860)
-march=core2 -mcx16 -msahf --param l1-cache-size=32 --param l1-cache-line-size=64 --param l2-cache-size=256 -mtune=core2

#ここから色々推測が入ります
最適化レベルを-O2から-O3にすると、暗黙的に-ftree-vectorizeが指定されるようです。どうもこのオプションが有効になることで(-march=core2なので)SSEを使った最適化がされるようですが、アラインメントをミスったコードを生成してしまっているようです。このオプションが悪いわけではなく、gccが正しくアラインメントされたメモリアドレスを扱うコードを生成するべきでしょう。gccのバグっぽいです。

4.回避策
core2に特化したコード生成を行う場合で-O3以上の最適化レベルを指定する場合は「-fno-tree-vectorize」オプションを加えて逃げるというworkaroundをとるしかない(と思います)。新しいgccではもしかするとバグが修正されているかもしれません。


最後に、本件を調査する際に参考になったリンクをあげておきます。

最近はgdbを使ったり、SSE命令など調べたことがなかったので勉強になりました:)

2010年2月9日火曜日

Emacs Scratchバッファの自動保存とリストア

1年ほど前にどこかのサイトで発見して、ありがたく使わせていただいているのが「Scratchバッファの自動保存とリストア」をするためElisp。Emacs終了時に自動的にScratchバッファの内容を保存して、次回起動時に復元してくれる。
;;; auto save and restore scratch buffer
(defun save-scratch-data ()
  (let ((str (progn
               (set-buffer (get-buffer "*scratch*"))
               (buffer-substring-no-properties
                (point-min) (point-max))))
        (file "~/.scratch"))
    (if (get-file-buffer (expand-file-name file))
        (setq buf (get-file-buffer (expand-file-name file)))
      (setq buf (find-file-noselect file)))
    (set-buffer buf)
    (erase-buffer)
    (insert str)
    (save-buffer)
    (kill-buffer buf)))

(defadvice save-buffers-kill-emacs
  (before save-scratch-buffer activate)
  (save-scratch-data))

(defun read-scratch-data ()
  (let ((file "~/.scratch"))
    (when (file-exists-p file)
      (set-buffer (get-buffer "*scratch*"))
      (erase-buffer)
      (insert-file-contents file))
    ))

(read-scratch-data)
これを .emacs に記述して長い間使ってる。かなり便利で、即席のメモ帳として使える。どこのサイトでオリジナルを見つけたのか今となってはわからないのだが、、、ありがとうございます>作者の方。

#僕はEmacs Lisp初心者で勉強中。

2010年1月28日木曜日

Emacs (NTEmacs) 23.1 フォント設定

昨年MeadowからEmacs 23.1に移行して唯一つまずいたのがフォント設定。ぜんぜん意図したとおりの設定ができないんだなこれが。フォントの設定は素人なので、深く追求もできないし。

ASCIIは Consolas フォント を使い、日本語は メイリオ フォント を使いたかった。
しかし「欧文フォントと日本語のフォントでは高さが違う」問題を解決できずに結局メイリオを断念することにした。以下のサイトを見れば解決できそうではあるが、.emacsファイル 以外をいじるのは僕のポリシー?(*)に反するので当面様子をみることにする。
*Emacsの設定やソースをいじるのはOK、Emacs以外の環境設定で回避するのはNG
比較的簡単な解決方法は、Consolasとメイリオフォントを合成したフォントを作ることだと思う。どうぞ、どなたかお試しくだされ。

結局僕がいきついたところは、日本語に MSゴシック を使うことだった。これがまた簡単な設定でいける。Consolasにはアンチエイリアスがかかって、MSゴシックにはかかっていないようだが、慣れれば違和感ない。

 ;;; 標準フォント
(set-default-font "Consolas 11")

(set-fontset-font (frame-parameter nil 'font)
                  'japanese-jisx0208
                  '("MS ゴシック" . "unicode-bmp")
                  )

(set-fontset-font (frame-parameter nil 'font)
                  'katakana-jisx0201
                  '("MS ゴシック" . "unicode-bmp")
                  )

この設定を .emacs に書けば以下の用に表示される。
(見やすくするために背景色などかえています)





もっとEmacs(NTEmacs)ユーザ増えてくれないかなぁと思って比較的簡単だと思われるフォント設定をさらしてみた。

Emacs (NTEmacs) 23.1 on Windows 7 でタスクバーにEmacsアイコンを複数表示させない方法

文章を書き慣れていない=文章力が無いようで、昨年書いた記事(つーか、メモ)は微妙だった気がする。タイトルも曖昧で、内容もだらだら感がある。まぁ手順を書いている文章の場合はしかたない場合もあるけれど。今年はそこを直していければと思うこの頃。

Emacs (NTEmacs) 23.1 をWindows 7上で少し快適に使う - Part.1
Emacs (NTEmacs) 23.1 をWindows 7上で少し快適に使う - Part.2」で説明したかったことは、「Windows 7 でタスクバーにEmacsアイコンを複数表示させない方法」だった。また、技術的な観点で言うと「ショートカットファイルに “GNU.Emacs” AppID を埋め込む方法」とも言えるだろう。

いやいや後者をもっと大きな観点で言えば 「ショートカットファイルに任意の AppID を埋め込む方法」か。

文章わかりにくいな~と気になっていたので少し補足を書いてみた。後者の言い方は@yunhさんが言ってたんだけどね。そこで、俺のタイトル&文章わかりにくいな。。。と思った次第であります。
わかりやすい文章を書くって難しいね。