Belajar Hermes JS Engine - Hermes Compiler Internals
Episode 16 of 23

Belajar Hermes JS Engine - Hermes Compiler Internals

Mengupas pipeline internal hermesc: frontend, IR, optimizer, dan code generation, bagaimana bytecode dihasilkan lalu diserialisasi, serta custom build flags dan optimization tiers yang bisa dikendalikan.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Pendahuluan

Episode 15 mengajarkan pola kode yang ramah interpreter. Sekarang kita balik arah: bukan bagaimana kode dieksekusi, melainkan bagaimana kode itu dikompilasi. hermesc adalah nama yang sudah sering kalian ketik, tetapi apa yang sebenarnya terjadi di dalamnya? Episode 16 membuka kap mesin compiler.

Roadmap episode ini: pipeline hermesc dari frontend sampai code generation, bagaimana bytecode dihasilkan dan diserialisasi, lalu custom build flags dan optimization tiers.

Gambaran Umum Pipeline hermesc

hermesc bekerja dalam rangkaian tahap yang berurutan. Setiap tahap mengubah representasi kode menjadi bentuk yang lebih dekat ke mesin:

pipeline-hermesc.txt
JavaScript source
  -> parser -> AST
  -> semantik (validasi scope & aturan bahasa)
  -> HBC lowering -> IR
  -> optimizer passes
  -> bytecode writer
  -> serialisasi -> file .hbc

Setiap tahap bisa dibongkar untuk dipelajari — inilah kenapa compiler Hermes terasa ramah untuk didalami, dibanding compiler runtime yang sangat besar. Kita telusuri satu per satu.

Tidak ada JIT yang menyelip di tengah pipeline ini — hasilnya selalu bytecode yang deterministik. Artinya, dua kompilasi dari source dan flag yang sama menghasilkan bytecode yang identik. Sifat inilah yang membuat build reproducible, sebagaimana dibahas di episode 11, menjadi masuk akal di dunia nyata.

Frontend: Parsing dan Validasi

Langkah pertama mengubah teks JavaScript menjadi pohon sintaks abstrak, alias AST. Parser Hermes menerjemahkan setiap token — function, panah, async, const — menjadi node AST. Setelah parsing, compiler melakukan validasi semantik: mengecek scope, penulisan mode strict, dan aturan yang tidak bisa ditangkap oleh sintaks belaka.

Kalian bisa melihat hasil parsing dengan flag -dump-ast:

dump-ast.sh
hermesc -dump-ast -out /dev/null app.js

Outputnya adalah representasi pohon dari kode kalian. Berguna ketika penasaran bagaimana konstruk sintaks tertentu dipahami compiler — misalnya bagaimana async/await diturunkan menjadi state machine.

Parser Hermes menerapkan aturan yang sama dengan standar ECMAScript, jadi perilaku parsing tidak menyimpang dari engine lain. Yang perlu diperhatikan justru lapisan di bawahnya, tempat perbedaan optimasi mulai terasa.

Kalau parser menemukan sintaks yang tidak sah, kompilasi berhenti di tahap ini — tidak ada bytecode yang dihasilkan. Error di tahap ini paling mudah dibaca karena langsung menunjuk ke baris source asli.

Intermediate Representation dan Optimizer

AST masih terlalu tinggi untuk dikompilasi langsung. Hermes menurunkan kode ke bentuk Intermediate Representation (IR) yang lebih rendah — kira-kira seperti kode tiga alamat di compiler klasik. Pada level inilah optimizer bekerja. Lihat hasilnya dengan -dump-ir:

dump-ir.sh
hermesc -dump-ir -O -out /dev/null app.js

Optimizer menjalankan rangkaian pass: menghapus kode mati, melipat konstanta, dan membuang ekspresi yang tidak punya efek. Flag -O mengaktifkan tiers optimisasi — semakin tinggi level, semakin agresif pass yang dijalankan, dan biasanya semakin lama waktu kompilasi. Trade-off inilah yang harus dipikirkan ketika kalian memilih opsi build di CI.

Menariknya, IR juga menjadi tempat Hermes melakukan optimasi yang berkaitan dengan karakter interpreter, misalnya menata ulang urutan instruksi agar akses register lebih ramah. Bagian inilah yang membuat hermesc berbeda dari sekadar transpiler biasa.

Code Generation dan Serialisasi Bytecode

Dari IR yang sudah dioptimalkan, code generator menuliskan bytecode Hermes: urutan opcode yang akan dieksekusi interpreter. Setiap fungsi dikompilasi menjadi segmen instruksi dengan tabel informasi — jumlah argumen, jumlah register, lokasi variabel. Lalu serialisasi menggabungkan semua segmen menjadi satu file .hbc dengan:

  • Header dan magic number sebagai penanda format.
  • Nomor versi bytecode, yang mengikat file ke versi engine, sebagaimana dibahas di episode 13.
  • Tabel string untuk literal, tabel fungsi, dan metadata sumber.

Struktur tersimpan bisa diperiksa dengan hbcdump. Ringkasannya ditampilkan lewat hbcdump -summary app.hbc:

inspeksi-bytecode.sh
hbcdump -summary app.hbc

Flag -c menampilkan disassembly opcode demi opcode. Menonton bagaimana sebuah fungsi kecil diterjemahkan menjadi instruksi adalah cara tercepat memahami mengapa bentuk kode tertentu lebih cepat di interpreter.

Selain disassembly, hbcdump bisa menampilkan tabel string dan statistik fungsi. Membaca ketiganya bersama memberi gambaran utuh: instruksi apa yang dipakai, literal apa yang disimpan, dan seberapa besar kontribusi tiap fungsi terhadap ukuran bundle.

Info

Karena serialisasi menghasilkan format biner yang terdokumentasi, bytecode Hermes adalah artefak yang bisa diaudit — tetapi ingat, bukan enkripsi, sebagaimana ditegaskan di episode 12 dan 13.

Custom Build Flags dan Optimization Tiers

hermesc memberi kontrol yang cukup rinci lewat flag. Beberapa yang sering dipakai:

  • -O sampai -O3: tingkat optimisasi; tanpa flag, compiler berjalan tanpa pass optimizer.
  • -emit-binary: menulis bytecode, bukan sekadar memvalidasi sintaks.
  • -output-source-map: menghasilkan mapping bytecode ke source, seperti episode 10.
  • -dump-ast dan -dump-ir: membongkar representasi perantara untuk debugging compiler.

Semua flag ini bisa digabung dalam satu pemanggilan, dan urutan penulisannya tidak memengaruhi hasil. Yang menentukan adalah versi hermesc dan set flag yang dipakai — karena itu, simpan kombinasi pilihan kalian sebagai konfigurasi yang di-commit.

optimization-tiers.sh
hermesc -O -emit-binary -out app.hbc app.js
hermesc -O2 -emit-binary -out app.hbc app.js

Praktik yang disarankan: tetap gunakan -O atau lebih tinggi untuk release, seragamkan lewat konfigurasi yang di-commit, sebagaimana episode 11, dan dokumentasikan tier yang dipakai supaya bytecode antar build bisa dibandingkan secara adil. Kalau ada dugaan bug compiler, pasangan -dump-ir dan hbcdump -c adalah alat forensik pertama kalian.

Perlu dicatat: tier yang lebih tinggi tidak selalu berarti bytecode lebih kecil. Beberapa pass justru mengorbankan ukuran demi kecepatan eksekusi, dan sebaliknya. Ukur keduanya pada aplikasi nyata sebelum memutuskan tier mana yang dijadikan standar tim.

Penutup

Kalian sekarang melihat hermesc dari dalam: AST dari parser, IR tempat optimizer bekerja, code generation yang menulis opcode, dan serialisasi yang menghasilkan file .hbc dengan header serta tabel yang terstruktur. Dengan pemahaman flags dan tiers, kalian tidak lagi memperlakukan compiler sebagai kotak hitam.

Inti yang harus dibawa pulang:

  • Pipeline hermesc terdiri dari parser, validasi semantik, lowering ke IR, optimizer, dan bytecode writer.
  • -dump-ast dan -dump-ir membongkar representasi perantara untuk keperluan analisis.
  • Serialisasi .hbc menyimpan header, nomor versi, tabel string, dan tabel fungsi yang terkunci ke versi engine.
  • hbcdump adalah alat inspeksi bytecode: summary untuk ringkasan, flag -c untuk disassembly.
  • Pilih optimization tier secara sadar dan seragamkan lewat konfigurasi yang di-commit.

Di episode 17 kita menyelam lebih dalam ke memori: Advanced Memory & GC Tuning — benchmarking heap behavior dan GC pause, konfigurasi GC untuk low-memory devices, serta profiling memory leaks dan fragmentation. Sampai jumpa!

Belajar Hermes JS Engine - Hermes Compiler Internals | Belajar Hermes JS Engine