Tantangan Inline Assembly pada Glasgow Haskell Compiler (GHC)
Penggunaan inline assembly atau C FFI primitif di GHC umumnya diterapkan pada komputasi performa tinggi seperti kriptografi atau manipulasi vektor SIMD. Karena GHC tidak memiliki parser inline assembly bawaan di level sintaksis Haskell murni, pola standar yang umum digunakan adalah menyisipkan assembly via C wrapper atau menggunakan backend LLVM (-fllvm) yang dipadukan dengan modul C/Cmm.
Masalah utama muncul saat kode dijalankan lintas arsitektur. Instruksi instruksi register spesifik pada x86_64 tidak valid untuk aarch64, dan kesalahan deklarasi clobbered registers dapat merusak runtime system (RTS) GHC secara senyap (silent corruption). Membangun continuous integration (CI) pipeline yang memvalidasi target arsitektur dan memeriksa integritas clobber list sebelum kompilasi penuh menjadi kebutuhan kritis.
Strategi Linting: Mendeteksi Masalah Register Clobber
Sebelum menjalankan kompilasi biner GHC yang memakan resource, linting statis berbasis skrip dapat mendeteksi pelanggaran arsitektur dan deklarasi register assembly GCC/Clang-style yang umum dipakai pada FFI wrapper.
Contoh skrip linter Bash berikut memvalidasi keberadaan penanda arsitektur serta memeriksa apakah register kritis seperti stack pointer (rsp/sp) atau register reservasi LLVM diubah tanpa deklarasi clobber yang benar.
#!/usr/bin/env bash
set -euo pipefail
TARGET_ARCH="${1:-x86_64}"
SRC_DIR="${2:-cbits}"
echo "[LINT] Memeriksa inline assembly untuk target: ${TARGET_ARCH} di folder ${SRC_DIR}..."
EXIT_CODE=0
# Validasi guard macro arsitektur
for file in $(find "${SRC_DIR}" -type f -name "*.c" -o -name "*.h"); do
if grep -q "__asm__" "$file"; then
if [ "${TARGET_ARCH}" = "x86_64" ] && ! grep -q -E "(__x86_64__|__amd64__)" "$file"; then
echo "[ERROR] $file: Ditemukan inline asm tanpa guard __x86_64__"
EXIT_CODE=1
fi
if [ "${TARGET_ARCH}" = "aarch64" ] && ! grep -q -E "(__aarch64__|__arm64__)" "$file"; then
echo "[ERROR] $file: Ditemukan inline asm tanpa guard __aarch64__"
EXIT_CODE=1
fi
fi
done
# Validasi clobber register berbahaya (modifikasi rsp/sp langsung dilarang)
for file in $(find "${SRC_DIR}" -type f -name "*.c"); do
if grep -Pzo '(?s)__asm__.*?\([\s\S]*?:[\s\S]*?:[\s\S]*?:[\s\S]*?"(rsp|esp|sp)"' "$file" > /dev/null; then
echo "[ERROR] $file: Terdeteksi clobber langsung pada stack pointer! Gunakan frame pointer aman."
EXIT_CODE=1
fi
done
if [ $EXIT_CODE -eq 0 ]; then
echo "[LINT] Validasi assembly berhasil tanpa pelanggaran fatal."
fi
exit $EXIT_CODE
Konfigurasi GitHub Actions Matrix Build
Untuk memvalidasi target x86_64 dan aarch64 secara paralel tanpa membuang waktu build kompilasi GHC, konfigurasi GitHub Actions membutuhkan runner native (Ubuntu standard untuk x86_64 dan Ubuntu ARM runner untuk aarch64) serta caching dependencies via actions/cache.
Workflow berikut menjalankan linting kustom sebelum memicu cabal build dengan flag --ghc-options="-fllvm".
name: GHC Inline Assembly Validation
on:
push:
branches: [ main ]
pull_request:
jobs:
validate-asm:
name: Build & Test (${{ matrix.arch }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
os: ubuntu-22.04
llvm_version: "15"
- arch: aarch64
os: ubuntu-24.04-arm
llvm_version: "15"
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Install Toolchain (LLVM & Clang)
run: |
sudo apt-get update
sudo apt-get install -y llvm-${{ matrix.llvm_version }} clang-${{ matrix.llvm_version }}
echo "/usr/lib/llvm-${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
- name: Setup Haskell GHC
uses: haskell-actions/setup@v2
with:
ghc-version: '9.6.4'
cabal-version: '3.10.2.0'
- name: Run Assembly Linter
run: |
chmod +x ./scripts/lint-asm.sh
./scripts/lint-asm.sh ${{ matrix.arch }} cbits
- name: Cache Cabal Store
uses: actions/cache@v4
with:
path: |
~/.cabal/store
dist-newstyle
key: cabal-${{ matrix.arch }}-${{ hashFiles('**/*.cabal', 'cabal.project') }}
restore-keys: |
cabal-${{ matrix.arch }}-
- name: Build with GHC LLVM Backend
run: |
cabal update
cabal build --ghc-options="-fllvm -optlo-O3 -optlc-O3"
- name: Run Test Suite
run: |
cabal test --test-show-details=streaming
Optimasi Caching dan Pertimbangan Teknis
Saat menggunakan LLVM backend di GHC, pastikan versi LLVM kompatibel secara presisi dengan rilis GHC yang digunakan. GHC 9.6 secara resmi mendukung LLVM versi 13 hingga 15. Penggunaan versi LLVM di luar batas yang didukung akan memicu error kompilasi pada pass opt atau llc.
- Cache Key Granularity: Pisahkan cache Cabal berdasarkan
matrix.arch. Binary artifacts antara x86_64 dan aarch64 tidak kompatibel; menyatukan cache key akan menyebabkan cache thrashing. - Clobber List Fallback: Register
memoryclobber wajib ditambahkan pada setiap block inline assembly yang memodifikasi pointer buffer Haskell agar compiler tidak melakukan reordering akses memori. - QEMU vs Native Runners: Hindari emulasi QEMU untuk arsitektur AArch64 bila runner native tersedia. GHC kompilasi dengan backend LLVM membutuhkan konsumsi memori dan CPU signifikan yang sering kali berujung pada timeout di lingkungan emulasi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!