はじめに
Go言語はシンプルな文法と高い開発効率、強力な並行処理モデルによってサーバーサイドを中心に広く普及しています。しかし、その手軽さを支えている「重厚なGC(ガベージコレクション)」や「ランタイムスケジューラ」の存在ゆえに、C言語向けの極小な共有ライブラリ(.so/.dll)を作ることや、外部ランタイムを抱え込まない軽量なWebAssembly(Wasm)バイナリを出力することは極めて困難でした。
そこで、「Goの書き味と人間工学(Ergonomics)」をそのままに、GCやランタイムオーバーヘッドを完全に排除し、C-ABI完全互換とLLVM最適化の恩恵をフルに受けるシステムプログラミング言語「Hike」を開発しました。
- GitHubリポジトリ: kanryu/hike-lang
Hikeとは?
Hikeは、Go言語風の明瞭な構文を持ち、LLVM IRを経由してネイティブバイナリやC-ABI準拠の共有ライブラリを生成するシステムプログラミング言語です。
[ .hike ソースコード ]
│
▼ (hikec: Go製フロントエンド & 意味解析)
[ AST / 単相化ジェネリクス ]
├───► C/C++ ヘッダ自動生成 (.h)
│
▼ (LLVM IR Codegen)
[ 純粋な LLVM IR (.ll) ]
│
▼ (Clang / LLVM -O3 最適化)
┌──────────────────────────────────────┐
│ │
▼ ▼
[ ネイティブ実行ファイル ] [ C-ABI共有ライブラリ ]
(.exe / ELF / Mach-O) (.dll / .so) ──► C++, Python, Rustから利用
[ WebAssembly ] (.wasm)
主な特徴
-
Go風のシンプルな構文: 多値返却、スライス、構造体、型推論(
:=)をサポート。 -
ゼロランタイム・ゼロGC: GC停止やバックグラウンドスケジューラなし。C言語と同等のメモリレイアウトと実行速度を実現。
-
C-ABI完全準拠 & C/C++ヘッダ自動生成: POD構造体やポインタ渡し関数を定義するだけで、即座に呼び出し可能な共有ライブラリと対応する
.hファイルを出力。 -
単相化ジェネリクス(Zero-Cost Monomorphization): コンパイル時特殊化により、動的ディスパッチのオーバーヘッドなし。
-
脱出解析付きファーストクラス関数・クロージャ: スコープを抜ける変数のみを自動でヒープ昇格(
malloc)させ、安全なクロージャを実現。 -
自律したモジュール管理(
hike.mod): C/C++のような複雑なCMake設定不要で、インポートパス解決やディレクトリマッピングが可能。 -
外部依存不要のスタンドアロンWasm出力: EmscriptenやWASI-SDKを使わず、標準のClangのみで極小
.wasmを出力。 -
VS Code / DWARFデバッグ対応:
-gオプションでブレークポイントやステップ実行、変数ウォッチに対応。
コード例で見るHikeの機能
1. 基本文法とC-ABI共有ライブラリの自動エクスポート
C言語・C++から呼び出せる共有ライブラリを作る場合、Hike側で関数を記述してビルドするだけで、C/C++用ヘッダ(.h)が自動生成されます。
mathlib.hike
package main
type Matrix2x2 struct {
M00 float64
M01 float64
M10 float64
M11 float64
}
// C-ABIエクスポート関数
func HikeMatrixDeterminant(m *Matrix2x2) float64 {
return (m.M00 * m.M11) - (m.M01 * m.M10)
}
コンパイルコマンド
# LLVM IRとCヘッダを同時に出力
hikec emit-ir -header mathlib.h -o mathlib.ll mathlib.hike
# Clangで共有ライブラリをビルド
clang -shared -O3 mathlib.ll -o mathlib.dll
自動生成される mathlib.h
#ifndef HIKE_MATHLIB_H
#define HIKE_MATHLIB_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Matrix2x2 {
double M00;
double M01;
double M10;
double M11;
} Matrix2x2;
HIKE_API double HikeMatrixDeterminant(Matrix2x2* m);
#ifdef __cplusplus
}
#endif
#endif
C++やPython(ctypes)から、何の手間もなくそのまま呼び出すことができます。
2. ファーストクラス関数と脱出解析付きクロージャ
関数を変数に代入したり、高階関数へ渡したり、外側のスコープの変数をキャプチャするクロージャを記述できます。脱出する変数のみが自動的にヒープへ昇格します。
package main
// クロージャを返すファクトリ関数('base'は脱出解析により自動でヒープ昇格)
func makeAdder(base int) func(int) int {
return func(n int) int {
return base + n
}
}
func main() int {
add100 := makeAdder(100)
result := add100(42) // => 142
// 参照キャプチャによる状態変更
counter := 0
increment := func() int {
counter = counter + 1
return counter
}
increment()
increment()
finalCount := increment() // counter == 3, finalCount == 3
return 0
}
内部的には { i8* fn_ptr, i8* env_ptr } という統一Fat Pointer ABIを採用しており、動的ディスパッチのコストを極限まで抑えています。
3. モジュール管理(hike.mod)と標準JSONサポート
C/C++には標準のパッケージ解決やJSONパーサーが存在せず、サードパーティ製ライブラリの導入やビルド設定に苦労しがちです。Hikeではモジュール管理とJSON/TOML操作が標準で組み込まれています。
hike.mod
module my-app
hike 0.1.0
# 外部・ローカルモジュールのマッピング
replace std/encoding/json => ../../std/encoding/json
main.hike
package main
import (
"std/encoding/json"
)
func printf(format string, ...) int
func main() int {
// 1. JSONファイルの読み込みとDOMパース
content := json.ReadFile("data.json")
doc := json.Parse(content)
nameVal := doc.Get("name")
if nameVal != nil {
printf("Project: %s\n", nameVal.AsString())
}
// 2. DOMの変更とノード追加
doc.Set("modified_by", json.NewString("hikec"))
// 3. 文字列化して保存
outStr := json.Stringify(doc)
json.WriteFile("output.json", outStr)
return 0
}
4. 外部SDK不要のWebAssembly(Wasm)ビルド
重厚なWASI-SDKやEmscriptenをセットアップすることなく、Clangのみで極小のスタンドアロン .wasm を出力できます。
# 1. wasm32向けLLVM IRを出力
hikec -target wasm32 -o main.ll main.hike
# 2. Clangで.wasmへコンパイル
clang --target=wasm32-unknown-unknown -O2 -nostdlib -Wl,--no-entry -Wl,--export-all -Wl,--allow-undefined main.ll -o app.wasm
# 3. Node.jsやブラウザで即座に実行
node run_wasm.js
開発環境(VS Code DWARFデバッグ)
-g フラグをつけてビルドすることでDWARFデバッグ情報が埋め込まれ、VS Codeの標準C/C++拡張機能(GDB/LLDB)を使って、.hike ソースコード上で直接ブレークポイントの停止、ステップイン/ステップオーバー、ローカル変数の確認が可能です。
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Build Hike Debug Executable",
"type": "shell",
"command": "hikec ${file} -g -o main.ll && clang -g -O0 main.ll -o app.exe"
}
]
}
なぜ「LLVM IRの出力」に徹するのか?
Hikeコンパイラは、あえてバイナリリンクやオブジェクト生成を抱え込まず、「単一のクリーンなLLVM IR(.ll)を出力する」という単機能性に徹しています。
コンパイラがバイナリ生成までブラックボックス化してしまうと、Windowsリソース(アイコンやマニフェスト)の埋め込み、特殊なリンカスクリプトの適用、セキュリティフラグの制御といった「現場で必要になる泥臭いカスタマイズ」の自由度が奪われてしまいます。
中間表現をLLVM IRというオープンな標準規格に絞ることで、後続のビルド・最適化・リソースバインドはすべてC/C++の成熟したツールチェーンに委ねることができます。「Goの書き心地で記述し、C/C++の資産とパイプラインで仕上げる」という柔軟性こそが、Hikeの最もコアな設計哲学です。
まとめとロードマップ
Hikeは、「Go言語の書きやすさ」と「C言語の極小フットプリント・共有ライブラリ互換性」の良いとこ取りを目指したシステムプログラミング言語です。
今後のロードマップ
-
動的インターフェース(
vtableディスパッチ)の正式サポート -
アリーナアロケータ等のメモリ管理機構の標準化
-
パッケージレジストリ・リモート依存解決
-
Hike自身によるコンパイラのセルフホスティング
プロジェクトはMITライセンスでGitHubにて公開しています。
少しでも興味を持っていただけた方は、ぜひリポジトリのスター(⭐)やIssue・PRでのフィードバックをよろしくお願いします!