nimとzigはじめました
nim はじめました
教訓
LLM は マイナー言語や最新バージョンなどは把握していないため、LLMに聞くと高確率でハルシネーションしたり、実装してエラー→旧バージョンではこうだった的な嘘を教えてくれます。
環境は intel macOS 15.6.1です。
nim で gzip されたxmlファイルを解凍してパースする
xml を行う方法は 二種類あるらしいが簡単な方を選んだ。
import os, strutils, strformat, zippy, xmlparser, xmltree, options when isMainModule: if paramCount() < 1: quit("Usage: nim2 <gzipFile> [csvOutputFile]") let gzipFile = paramStr(1) let csvFile = if paramCount() >= 2: some(paramStr(2)) else: none(string) # GZIPを解凍 let compressed = readFile(gzipFile) let decompressed = uncompress(compressed) # XMLを解析 let xmlDoc = parseXml(decompressed) # CSVのヘッダ var rows: seq[string] = @["xmlFile,fileName,contentId"] # 必要な要素を探索(例: <file-name> 要素から 値 と contentId を取る) for node in xmlDoc.findAll("file-name"): let fileName = node.innerText().strip() let contentId = node.attr("contentId") rows.add(&"{gzipFile},{fileName},{contentId}") # 出力処理 if csvFile.isSome: writeFile(csvFile.get(), rows.join("\n")) else: echo rows.join("\n")
これでコンパイルも実行もできた。やったね
nim c nim2.nim # nim という実行ファイルの生成 nim c -r nim2.nim /path/to/NOTICE.xml.gz # 実行ファイルを生成して、引数を与えて実行
クロスコンパイルできる?
nimコンパイラはC言語のソースを生成しているのでC言語のソースから linux用のバイナリを生成するクロスコンパイルすれば、nim もクロスコンパイルできる。 ということは二つの方法がある。
ここで zig cc を使うとクロスコンパイルが簡単にできるという情報があったので試してみた。 もちろん、 glibc のバージョンが違って動かないという問題を回避するために musl libc を使おう。
一気にクロスコンパイルで失敗
いろいろ調べた結果これでいけそうなんだがうまくいかない。
CC="zig cc" nim c --cc:clang --os:linux --cpu:amd64 --passC:"-target x86_64-linux-musl" --passL:"-target x86_64-linux-musl -static" nim2.nim
これを実行すると、 musl-linux環境の stddef.h を探しにいくのだけれでども、intel MacOS の方を見てしまう。この問題の解決にこうすればいいとLLMはいうのだが、
--passC:"-target x86_64-linux-musl --sysroot $(zig libc-path)" \
zig libc-path なんてオプションには対応していなくてエラーがでた。
というところで撤退
じゃあ分割してコンパイル
つぎのコマンドで nimcache: で指定したディレクトリにC 言語に書き出すまで行える。
nim c --cc:clang --os:linux --cpu:amd64 --compileOnly \ --nimcache:build/c nim2.nim
それからこれでクロスコンパイルします、としたいのですが。
zig cc -target x86_64-linux-musl -static \ -o nim2 \ build/c/nim2.nim.c \ $(find build/c -name '*.c' ! -name 'nim2.nim.c')
nim が提供する nimbase.h のパスを教える必要があります。
nim dump
すると、${HOME}/.choosenim/toolchains/nim-${NIMVERSION}/lib のようなディレクトリが表示されますこのディレクトリに nimbase.h があります。 このディレクトリを I オプションで指定します。
zig cc -target x86_64-linux-musl \
-I${HOME}/.choosenim/toolchains/nim-2.2.4/lib \
build/c/nim2.nim.c \
-o nim2
SIMD命令SSSE3 対策
これで成功すると思ったのですが、次のようなエラーがでました。
'_mm_maddubs_epi16' requires target feature 'ssse3', but would be inlined into function 'adler32_ssse3__pkgZzippyZadler3295simd_u84' that is compiled without support for 'ssse3'ect 219 | nimln_(92); mad1_1 = _mm_maddubs_epi16(bytes1_1, tap1_1); |
これは ssse3 というSIMD 命令 (SSSE3) が必要なのに、ツールチェーン(llvm or zig)がデフォルトでサポートしていないようです。これは、zippy ライブラリ(Nim の gzip/zip ライブラリ)が要求しています。
解決方法としては、二つあります。一つは Zig 側に SSSE3 を有効化する です。もう一つは SIMD サポートを無効化したC言語ソースファイルを nim で生成する の二つです。今回は後者を選びました。
Zig 側に SSSE3 を有効化する
最近のx86_64のCPUでは ssse3 のSIMD命令はサポートされています。そのため、実際には有効にしても問題ないはずです。その場合、-mssse3 オプションで ssse3 を有効にすることもできますし、アーキテクチャを指定してあげる方法もあるらしいです。
zig cc -target x86_64-linux-musl \
-I${HOME}/.choosenim/toolchains/nim-2.2.4/lib \
-o nim2l \
-march=x86_64 -mssse3 \
build/c/nim2.nim.c $(find build/c -name '*.c' ! -name 'nim2.nim.c')
SIMD サポートを無効化したC言語ソースファイルを nim で生成する
SIMD サポートを無効化したC言語ソースファイルを nim で生成するには、-d:noSimd
というオプションで SIMD サポートを無効にできることが多いのです。ですが、zippy は -d:zippyNoSimd というオプションで無効にするようです。
nim c --cc:clang --os:linux --cpu:amd64 --compileOnly \
-d:zippyNoSimd --nimcache:build/c nim2.nim
zig cc -target x86_64-linux-musl \
-I${HOME}/.choosenim/toolchains/nim-2.2.4/lib \
-o nim2 \
build/c/nim2.nim.c $(find build/c -name '*.c' ! -name 'nim2.nim.c')
キャッシュチェックエラー対策
これで SIMD サポートを無効化できたのですが次に以下のようなエラーがでました。
build/c/nim2.nim.c:1:1: error: CacheCheckFailed
これは、Zig の クロスコンパイル時キャッシュ検証エラーだそうです。
# nim の不要なソースコードを先に削除
rm -rf build/c
# nim で zippy で SIMD命令を無効にしてCソースコードを生成
nim c --cc:clang --os:linux --cpu:amd64 --compileOnly \
-d:zippyNoSimd --nimcache:build/c nim2.nim
# zig のキャッシュを削除
rm -rf ~/.cache/zig
# zig でキャッシュを無効化して各ファイルを分割コンパイル
for f in $(find build/c -name '*.c'); do
zig cc -fno-cache -target x86_64-linux-musl -static \
-I${HOME}/.choosenim/toolchains/nim-2.2.4/lib -c $f -o ${f%.c}.o
done
# zig で全部をリンク
zig cc -target x86_64-linux-musl -static \
-o nim2l $(find build/c -name '*.o')
これでなんとかnimとzigでクロスコンパイルできました。