秋分の日を過ぎると、日中の日射しには厳しさが残るものの、朝晩は肌寒いくらいになってきました。発熱量が大きくて暑い時期には敬遠していた開発用のマシンに電源が入ることも多くなり、Plamo-5.2へ向けたパッケージ作りの作業に励む日々が続いています。
Plamo-5.2は、今年の5月下旬にリリースしたPlamo-5.1に続くPlamo-5系統の2度目のメンテナンスリリースで、5.1では拾えなかったり、5.1以後にアップデートされたソフトウェアに対応して、年内には公開したいと考えています。
現在、Plamo-5.2用のツリーでは仮想化関係のパッケージの更新が盛んで、仮想化回りの機能はPlamo-5.2の特長のひとつになりそうです。しかし仮想化回りについてはまた別の機会にとりあげることにして、しばらくはPlamo-5.1では拾い損なったX Window System とその基盤のひとつとなっている3Dグラフィックス用のMesaLib 、さらにそれに必要となるLLVM について紹介してみましょう。
X Window SystemとMesaLib
X Window Systemは、マサチューセッツ工科大学(MIT)の学内コンピュータ環境をMITとIBM、DECが共同で研究開発した「アテナプロジェクト (Project Athena) 」の一部として生まれたウィンドウシステムです。そのような出自から、最初のX Window SystemはMITが中心となったMIT X Consortium が、アテナプロジェクトの終了後は非営利な業界団体であるX Consortium が引き継いで開発を続けてきました。その後、The Open Group やX.Org 、XFree86 といったさまざまな組織を経て、現在ではX.Org Foundation が管理主体となりつつ、誰でも自由に開発に参加できる「バザールモデル」の形式で開発が進んでいます。
図1 X.Org Foundationの公式サイト
このような開発スタイルの変化を反映して、X Window Systemのソースコードは、全てのソフトウェアをひとつのディレクトリ以下に収めていたモノリシック構造 から、それぞれのライブラリやコマンドを独立したソフトウェアとして自由にビルド、インストールできるモジュール構造 に変更されました。そのため、各ソフトウェアの開発テンポも従来に比べて自由度が増し、X Window Systemとしての公式なリリースは定期的に行われるものの、それぞれのソフトウェアはそれとは独立にバージョンアップ を進めています。
その結果、Plamo-5.1で採用しているX11R77 は、X Window Systemとしてはいまだに最新版ではあるものの、Xサーバや各ビデオカード用のドライバ、ライブラリなど構成しているパーツの中にはずいぶんバージョンが進んだものがあり、X11R77当時のバージョンは少々見劣りするようになってしまいました。
この問題には、Plamo-5.1をまとめていた際にも気がついていて、主要なパーツ(特にXサーバとドライバ類)だけでも新しいバージョンに追従しようかと考えましたが、その際に問題になったのが3Dグラフィックス機能を担当しているMesaLib でした。
MesaLibはOpenGL と呼ばれる3Dグラフィックスの仕様をOSSとして実装したライブラリで、X Window Systemも3Dグラフィックスの処理はMesaLibを利用しています。
図2 Mesa3D projectの公式サイト
元々、MesaLibはOpenGLが定義している3Dグラフィックス用の機能をソフトウェア的に実現 しようとしたプロジェクトで、CPUで生成した画像情報をグラフィックスカードに送ってディスプレイに表示していました。しかし、グラフィックスカードに搭載されているGPU ( Graphics Processing Unit)の進化が進み、3Dグラフィックス機能の多くがハードウェア的に実現できるようになるにつれ、MesaLibも処理の多くをGPUにゆだねる ように設計が変更されました。
厳密に言うと、プログラマブル・シェーダー等の機能をもったGPUを操作するために、従来のMesa3Dに代わるGallium3Dと言うフレームワークが開発され、最近のMesaLibはこのGallium3Dフレームワークを使うようになりました。
GPUは"Graphics Processing Unit"の名称通り、コンピュータの画面処理、特に3Dグラフィックスの処理を効率よく実現できるように設計されたマイクロプロセッサ です。
3Dグラフィックスでは、オブジェクトに光がどのように当たってどのような質感や陰影が生じるのかを、オブジェクトの構成単位ごとに繰り返し計算する必要があります。そのような計算はいわゆる「並列化」することで効率が改善するので、AMD(ATI)のRadeon やNVIDIAのGeForce といった最近のGPUは並列処理をきわめて高速に実行できるような設計になっています。
これらのGPUはマイクロプロセッサ なので、操作するためにはGPUが理解するコマンドを送ってやる必要があります。ところが、GPUはIntelやAMDのCPUとは全く異なる設計になっているため、GPUのコマンドはCPUのコマンドと根本的に異なります。そのため、MesaLibではOpenGLの機能を実現するために必要な操作を、GPUが理解するコマンドに変換した上でグラフィックスカードに送る必要があります。
グラフィックス用に特化した設計になっているため、GPUのコマンドはCPUのコマンドほど複雑ではないものの、ある言語(OpenGL)で書かれたコードをマイクロプロセッサ(GPU)が理解するコマンドに変換する、という作業は、ドライバやライブラリではなく、本来はコンパイラがするべき仕事 です。
そこで最近のMesaLibでは、「 LLVM (Low Level Virtual Machine) 」と呼ばれるソフトウェアを利用して、GPUを操作するコマンドを生成するようにしています。つまり、X Window Systemのパーツ(Xサーバ)をビルドするためには、新しいMesaLib(Gallium3D)が必要で、新しいMesaLibをビルドするためにはLLVMを用意する必要があるわけです。
LLVM
LLVMは、元々はイリノイ大学で開発された「コンパイラ基盤 (Compiler Infrastructure) 」です。「 コンパイラ基盤」というのは聞きなれない言葉ですが、特定の言語やCPUに依存せず、さまざまなコンパイラで汎用的に使えるコンピュータ用の中間表現や最適化処理に関する機能をこのように呼んでいるようです。
図3 LLVMの公式サイト
最初期のコンパイラは、ある言語で記述されたソースコードを特定のCPU用のアセンブリコードに直接変換するソフトウェアでした。しかし、さまざまに提案される言語や次々に開発されるCPUそれぞれに専用のコンパイラを開発するのは大変です。
そこで各言語とCPUを直接対応づけるのではなく、言語やCPUに依存しないコンピュータ専用の中間表現 を介して、両者を多対多で結びつけよう、という考え方が生まれました。このようにしておけば、新しい言語を追加するにはその言語を中間表現に変換する処理 を書けばいいわけですし、新しいCPUに対応するのも中間表現からそのCPU用のコマンドを生成する処理 を書くだけで済みます。最適化等の処理はそれぞれの言語やCPUごとではなく、この中間表現に対して行なうようにすれば、言語やCPUに関わらずにその恩恵を受けることができます。
このような設計の魁(さきがけ)となったのが、GNUプロジェクトが開発したGCC (GNU Compiler Collection)です。GCCでは、コンピュータ専用の中間表現としてRTL (Register Transfer Language)を採用し、CやC++、FORTRAN、あるいはJavaやAdaといった言語をいったんRTL形式に変換してから最適化処理を行い、その結果をさまざまなCPU用のアセンブリコードに変換する、という設計になっています。このような設計のおかげで、GCCは60種類を越えるCPUに対応したコンパイラとして、長くOSSのデフォルトコンパイラの地位を占めてきました。
しかしながら、最初のバージョンが1987年に公開されて以来、長年に渡って多くの開発者がさまざまな機能を追加してきたGCCは、開発やメンテナンスも簡単ではありません。
LLVM はそのような現状を踏まえて生まれたソフトウェアです。「 低水準仮想マシン(Low Level Virtual Machine) 」の名前が示すように、LLVMの中心部分は特定のアーキテクチャに依存しない仮想的なCPUとこの仮想CPU用に生成された中間表現を最適化する機能です。これらの機能はLLVM以外の環境からも利用しやすいように、それぞれが独立したライブラリ として提供されています。
LLVMでは、元々、各言語を解析するフロントエンド部はGCCを流用することにして、GCCが生成した中間表現を磨きあげる(最適化する)ことを目的としていました。しかし最近では、CやC++、Objective-Cのソースコードを解析するためのClang と呼ばれる独自のフロントエンド部が開発され、GCCとは独立したC/C++/Objective-Cコンパイラとして利用できるようになっています。
Clangは、Objective-Cを主要開発言語としているApple社が、GCCのObjective-Cへの対応が進展しないことに業を煮やして開発したC99/C++/Objective-C用のフロントエンドです。ClangをLLVMと組み合わせることで、OSSの世界では他に選択肢の無かったGCCに代わるコンパイラ環境が実現できるようになりました。
ClangとLLVMは、GPLよりも緩やかな「イリノイ大学オープンソースライセンス」( 内容は新BSDライセンスと同等)で配布されているので、FreeBSDのようにGPLをよしとしないプロジェクトは積極的にClang/LLVMを採用しています。
LLVMは中間表現の最適化処理などの機能を独立した共有ライブラリとして提供しており、LLVM用のフロントエンド部を書けば、後の処理はそれら共有ライブラリを利用することが可能です。Gallium3DはLLVMのこのような特徴を利用して、自身はOpenGLの機能をLLVM用の中間表現に変換する処理を行い、その後の処理はLLVMのライブラリを利用するようになっています。
このように書いてくれば、それぞれの段階はそれなりの必然性を持っているように思われるでしょう。しかし、こういう構造になった結果、X Window Systemを更新するためにはMesaLibを新しくする必要があり、MesaLibを新しくするためにはきちんと動作するLLVMの環境を用意する必要があるわけで、それぞれをパッケージ化しなければいけないディスリビュータの立場からすると安易に手を出すことがためらわれます。そのため、Plamo-5.0から5.1へのアップデートではX Window Systemの更新を見送り、今回改めてLLVMのパッケージ化から挑戦したのでありました。
Plamo-5.1用LLVM
さて、ずいぶん前置きが長くなりましたが、X Window Systemの更新を目指して、まずは64ビット環境用にLLVMをパッケージ化する作業に着手しました。
LLVMではビルド環境構築用に、伝統的なconfigureスクリプトとより新しいcmakeの2つのツールが用意されています。当初はBLFS(Beyond Linux From Scratch)のビルド方法等を参考にして、configureスクリプトを用いたビルドスクリプトを書いてみたものの、configure時に--libdirオプション を指定しても、生成したライブラリ類が64ビット環境用のディレクトリ(/usr/lib64/)ではなく/usr/lib/にインストールされてしまいます。
cmakeには-DLLVM_LIBDIR_SUFFIX という指定があるようなのでこちらも試してみましたが、やはり共有ライブラリが/usr/lib64/には行きません。
さてどうしたものか…、と他のディストリビューションを調べてみると、LLVMのソースコードのあちこちに共有ライブラリの位置を/usr/lib/に決め打ちしているところがあり、それらを適宜修正する必要があるようです。そこで、それらの修正処理をPlamoBuildスクリプトに取り込んでみました。
1 #!/bin/sh
2 ##############################################################
3 url='http://llvm.org/releases/3.3/llvm-3.3.src.tar.gz
4 http://llvm.org/releases/3.3/cfe-3.3.src.tar.gz
5 http://llvm.org/releases/3.3/compiler-rt-3.3.src.tar.gz'
6 pkgbase=llvm
7 vers=3.3
8 arch=x86_64
9 # arch=i586
10 build=P1
....
153 # Fix installation directories, ./configure doesn't set them right
154 if [ "$arch" = "x86_64" ]; then
155 sed -e 's:\$(PROJ_prefix)/docs/llvm:$(PROJ_prefix)/share/doc/llvm-3.3:' \
156 -e 's:\$(PROJ_prefix)/lib:$(PROJ_prefix)/lib64:' \
157 -i Makefile.config.in
158 sed '/ActiveLibDir = ActivePrefix/s:lib:lib64:' -i tools/llvm-config/llvm-config.cpp
159 sed -i 's:LLVM_LIBDIR="${prefix}/lib":LLVM_LIBDIR="${prefix}/lib64":' autoconf/configure.ac configure
160 sed -i s,/lib/,/lib64/,g tools/clang/lib/Driver/Tools.cpp \
161 tools/clang/test/Preprocessor/iwithprefix.c
162 rm tools/clang/test/Driver/x86_features.c
163 rm tools/clang/test/Driver/linux-ld.c
164 fi
165 # Fix insecure rpath (http://bugs.archlinux.org/task/14017)
166 sed -i 's:$(RPATH) -Wl,$(\(ToolDir\|LibDir\|ExmplDir\))::g' Makefile.rules
167
....
186 cmake -G "Unix Makefiles" \
187 -DBUILD_SHARED_LIBS=ON \
188 -DCMAKE_BUILD_TYPE=Release \
189 -DCMAKE_INSTALL_PREFIX=/usr \
190 -DLLVM_LIBDIR_SUFFIX=${suffix} \
191 -DCLANG_RESOURCE_DIR=../${libdir}/clang/3.3 \
192 -DLLVM_REQUIRES_RTTI=ON \
193 -DLLVM_ENABLE_TIMESTAMPS=OFF \
194 -DLLVM_ENABLE_ASSERTIONS=OFF \
195 -DLLVM_ENABLE_PIC=ON \
196 -DLLVM_BINUTILS_INCDIR=/usr/include \
197 -DLLVM_TARGETS_TO_BUILD=all \
198 -DLLVM_HOST_TRIPLE=$HOST_TRIPLE \
199 -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=R600 \
200 ..
単純を旨とするPlamoBuildスクリプトとしては、ずいぶん面倒な処理が必要となりましたが、このような試行錯誤の結果、何とか64ビット版のLLVMとC/C++用フロントエンドであるClangを動かすことができるようになりました。
$ clang --help
OVERVIEW: clang LLVM compiler
USAGE: clang-3 [options] <inputs>
OPTIONS:
-### Print the commands to run for this compilation
-E Only run the preprocessor
-F <value> Add directory to framework include search path
-H Show header includes and nesting depth
-I <value> Add directory to include search path
...
これらを使ってC++のサンプルコードをコンパイルしてみました。
$ clang++ -v sample.c++
clang version 3.3 (tags/RELEASE_33/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
"/usr/bin/clang-3.3" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all
-disable-free -disable-llvm-verifier -main-file-name sample.c++ -mrelocation-model static
-mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables
....
#include <...> search starts here:
/usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../include/c++/4.7.3
/usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../include/c++/4.7.3/x86_64-pc-linux-gnu
/usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../include/c++/4.7.3/backward
/usr/local/include
/usr/bin/../lib64/clang/3.3/include
/usr/include
End of search list.
"/usr/bin/ld" --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out
/usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/crt1.o
/usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/crti.o
/usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/crtbegin.o -L/usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3
...
$ ./a.out
Hello World by C++ !
さて、LLVMの準備が整ったので、いよいよ新しいMesaLibのビルドにとりかかれるわけですが、すいぶん長くなってしまったので、続きは次回に譲りましょう。