Schema zu WebAssembly kompilieren – Eli Benderskys Website

Schema zu WebAssembly kompilieren – Eli Benderskys Website


Eines meiner ältesten Open-Source-Projekte – Bob – hat vor ein paar Monaten seinen 15. Geburtstag gefeiert. Bob ist eine Suite von Implementierungen der Programmiersprache Scheme in Python, einschließlich eines Interpreters, eines Compilers und einer VM. Damals habe ich mich mit CPython-Interna befasst und war sehr neugierig, wie CPython-ähnliche Bytecode-VMs funktionieren. Bob führte ein Experiment durch, um dies herauszufinden, indem er eines von Grund auf für das R5RS-Schema implementierte.

Einige Monate später fügte ich Bob eine C++-VM hinzu, als Übung, um zu lernen, wie solche VMs in einer Low-Level-Sprache implementiert werden, ohne die gesamte Laufzeitunterstützung, die Python bietet; vor allem ohne den eingebauten GC. Die C++-VM in Bob implementiert ihren eigenen Mark-and-Sweep-GC.

Nach vielen ruhigen Jahren (mit nur ein paar kosmetischen Änderungen, Portierungen auf GitHub, Updates auf Python 3 usw.) verspürte ich kurz vor den Feiertagen den Drang, wieder an Bob zu arbeiten. Konkret habe ich beschlossen, der Suite einen weiteren Compiler hinzuzufügen – diesen von Scheme direkt zu WebAssembly.

Die Ziele dieser Bemühungen waren zweierlei:

  1. Experimentieren Sie mit der Herabstufung einer echten Hochsprache wie Scheme auf WebAssembly. Experimente wie das jüngste Let’s Build a Compiler kompilieren Spielzeugsprachen auf C-Ebene (keine Laufzeit). Scheme verfügt über integrierte Datenstrukturen, lexikalische Abschlüsse, Speicherbereinigung usw. Es ist viel anspruchsvoller.
  2. Sammeln Sie praktische Erfahrungen mit der WASM GC-Erweiterung. Ich habe mehrere Beispiele für die Verwendung von WASM GC im Wasm-Wat-Samples-Repository, aber ich wollte es unbedingt für etwas „Echtes“ ausprobieren.

Nun, es ist jetzt erledigt; Hier ist ein aktualisierter Schaltplan des Bob-Projekts:

Schema zu WebAssembly kompilieren – Eli Benderskys Website

Der neue Teil ist der vertikale Pfad ganz rechts. Eine WasmCompiler-Klasse reduziert geparste Scheme-Ausdrücke bis hin zum WebAssembly-Text, der dann in eine Binärdatei kompiliert und mit Standard-WASM-Tools ausgeführt werden kann.

Höhepunkte

Der interessanteste Aspekt dieses Projekts war die Arbeit mit WASM GC zur Darstellung von Scheme-Objekten. Solange wir alle Werte richtig einpacken/verpacken
refs, die zugrunde liegende WASM-Ausführungsumgebung kümmert sich um die Speicherverwaltung.

Für Bob werden einige wichtige Scheme-Objekte wie folgt dargestellt:

;; PAIR holds the car and cdr of a cons cell.
(type $PAIR (struct (field (mut (ref null eq))) (field (mut (ref null eq)))))

;; BOOL represents a Scheme boolean. zero -> false, nonzero -> true.
(type $BOOL (struct (field i32)))

;; SYMBOL represents a Scheme symbol. It holds an offset in linear memory
;; and the length of the symbol name.
(type $SYMBOL (struct (field i32) (field i32)))

$PAAR ist von besonderem Interesse, da es beliebige Objekte in seinen Feldern enthalten kann; (ref null eq) bedeutet „ein nullbarer Verweis auf etwas, das Identität hat“. ref.test kann verwendet werden, um für eine bestimmte Referenz den Laufzeittyp des Werts zu überprüfen, auf den sie verweist.

Sie fragen sich vielleicht: Was ist mit numerischen Werten? Hier hat WASM einen Trick – den i31
Der Typ kann verwendet werden, um einen Verweis auf eine Ganzzahl darzustellen, ohne ihn jedoch tatsächlich einzurahmen (ein Bit wird verwendet, um ein solches Objekt von einem echten Verweis zu unterscheiden). Wir benötigen also keinen separaten Typ, um Verweise auf Zahlen zu speichern.

Auch die $SYMBOL Typ sieht ungewöhnlich aus – wie wird er mit zwei Zahlen dargestellt? Der Schlüssel zum Rätsel liegt darin, dass WASM keine integrierte Unterstützung für Strings bietet; Sie sollten manuell mithilfe von Offsets zum linearen Speicher implementiert werden. Der Bob WASM-Compiler gibt die Zeichenfolgenwerte aller gefundenen Symbole in den linearen Speicher aus und verfolgt dabei den Offset und die Länge jedes einzelnen Symbols. Das sind die beiden eingegebenen Zahlen $SYMBOL. Dies ermöglicht auch die relativ einfache Implementierung der String-Interning-Funktion von Scheme; Mehrere Instanzen desselben Symbols werden nur einmal zugewiesen.

Betrachten Sie diesen trivialen Scheme-Ausschnitt:

Der Compiler gibt die Symbole „foo“ und „bar“ wie folgt in den linearen Speicher aus:

(data (i32.const 2048) "foo")
(data (i32.const 2051) "bar")

Und wenn wir im Rest des ausgegebenen Codes nach einer dieser Adressen suchen, finden wir:

(struct.new $SYMBOL (i32.const 2051) (i32.const 3))

Als Teil des Codes zum Erstellen der Konstante Nachteile Liste, die das Argument darstellt schreiben; Adresse 2051 und Länge 3: Das ist das Symbol Bar.

Apropos schreibendie Implementierung dieses Builtins war sehr interessant. Aus Kompatibilitätsgründen mit den anderen Bob-Implementierungen in meinem Repository: schreiben
muss in der Lage sein, rekursive Darstellungen beliebiger Scheme-Werte zu drucken, einschließlich Listen, Symbolen usw.

Anfangs zögerte ich, all diese Funktionen manuell in WASM-Text zu implementieren, aber alle Alternativen stießen auf Herausforderungen:

  1. Dies auf den Host zu verlagern ist schwierig, da die Hostumgebung keinen Zugriff auf WASM GC-Referenzen hat – diese sind völlig undurchsichtig.
  2. Die Implementierung in einer anderen Sprache (vielleicht C?) und die Reduzierung auf WASM ist aus einem ähnlichen Grund ebenfalls eine Herausforderung – die andere Sprache verfügt wahrscheinlich nicht über eine gute Darstellung von WASM-GC-Objekten.

Also habe ich in den sauren Apfel gebissen und – mit etwas KI-Hilfe für die mühsamen Teile – einfach eine Implementierung von geschrieben schreiben direkt im WASM-Text; es war nicht wirklich so schlimm. Ich importiere nur zwei Funktionen vom Host:

(import "env" "write_char" (func $write_char (param i32)))
(import "env" "write_i32" (func $write_i32 (param i32)))

Obwohl es nicht schwierig ist, Ganzzahlen direkt aus WASM auszugeben, dachte ich, dass dieses Projekt bereits über genügend Code verfügt und dass hier etwas Host-Hilfe willkommen wäre. Im Übrigen nur die unterste Stufe write_char verwendet wird. So werden beispielsweise boolesche Werte in der kanonischen Scheme-Notation ausgegeben (#T Und #F):

(func $emit_bool (param $b (ref $BOOL))
    (call $emit (i32.const 35)) ;; '#'
    (if (i32.eqz (struct.get $BOOL 0 (local.get $b)))
        (then (call $emit (i32.const 102))) ;; 'f'
        (else (call $emit (i32.const 116))) ;; 't'
    )
)

Abschluss

Das war ein wirklich unterhaltsames Projekt und ich habe einiges über die realistische Codeausgabe an WASM gelernt. Schauen Sie sich gerne den Quellcode von WasmCompiler an – er ist sehr gut dokumentiert. Obwohl es insgesamt etwas mehr als 1000 LOC sind, handelt es sich bei mehr als der Hälfte davon tatsächlich um WASM-Textschnipsel, die die integrierten Typen und Funktionen implementieren, die für eine grundlegende Scheme-Implementierung erforderlich sind.




Source link

Postagens Similares

  • میکوس گولڈن گیٹ آئیکن کا موازنہ – بنیادی ایپل گائے

    WWDC ہمیشہ نئے مواد، تفصیلات، اور پلیٹ فارم پر وسیع تبدیلیاں لاتا ہے۔ میک او ایس گولڈن گیٹ بیٹا انسٹال کرنے کے بعد میں نے پہلی چیزوں میں سے ایک جو آئیکن کا اپ ڈیٹ کردہ ڈیزائن تھا۔ رنگ زیادہ بولڈ ہیں، کئی آئیکنز کو ایڈجسٹ کیا گیا ہے، اور Liquid Glass اثر میں ریفریکشن…

  • Ментон Ріс – Переоцінка Twitter/X API

    Місяць тому я вголос задавався питанням, чи настав час додати перехресні публікації з Twitter до Micro.blog. Після довгих роздумів і відгуків користувачів я вважаю, що так. Мета завжди полягала в тому, щоб допомогти людям спочатку публікувати публікації на їхньому власному домені, незалежно від того, де вони також вирішили поділитися своїми публікаціями. Хоча я сподівався, що…

  • 關於我的十個毫無意義的事實

    📅 2025 年 10 月 16 日 | ⏱️ ~3 分鐘閱讀 我最近在一些博客上看到了這種情況,所以想添加我自己的版本,因為我是一個自戀者。 🙃 皮特·摩爾昨天做了他的版本,大衛在四月份就做了他的版本。事實上,我當時就已經在草稿了,但一直沒有時間完成它(總有一些更有趣的東西可以寫)。 好吧,我現在沒有什麼更有趣的東西可寫,所以皮特的帖子促使我完成它。所以這裡是 關於我的十個毫無意義的事實…… 你用牙線嗎? 有點。我最討厭的就是食物卡在牙齒裡。所以我每次吃飯的時候都會用牙籤把它們清理乾淨。 🤢 茶、咖啡還是水? 全部 3. 我主要喝水和咖啡,但周末確實喜歡喝一杯茶和早餐。 卡駱馳!我愛卡駱馳!但我不會在戶外穿它們——對我來說,它們更像是適合在家裡穿的舒適拖鞋。當我出門時,通常是運動鞋或步行鞋。通常是後者,因為我隨時都會選擇舒適而不是時尚。我個人最喜歡的是梅雷爾和哥倫比亞。 最喜歡的甜點? 任何檸檬味的東西。通常是檸檬毛毛雨,或檸檬芝士蛋糕(雖然不是美國的🇬🇧)。 你起床後做的第一件事是? 我總是在床邊放一品脫水。所以我第一件事 總是 要做的就是喝點飲料清新口氣,然後去洗手間把前一天晚上喝的水處理掉。 你想堅持的年齡? 大概28歲左右。我認為 20 多歲是健康、可支配收入和責任水平之間的良好平衡點。 你擁有多少頂帽子? 我其實不知道。 8 也許?我有幾頂冬帽、一頂鴨舌帽、一些夏帽,還有我在軍隊時戴的舊貝雷帽。 描述一下你拍的最後一張照片? 我正在銷售的其中一款手錶的照片。我真的不拍很多照片。當我這樣做時,它們主要是我的寵物、我的孩子或我的摩托車。 最糟糕的電視劇? 不知道。我對電視和電影的門檻很低。我通常可以在幾乎所有我看的東西中找到我喜歡的東西。我看過的最糟糕的電影是《狗人》;絕對是一堆熱氣騰騰的狗屎(雙關語)。 💩 小時候,你對成年的渴望是什麼? 說實話,我沒有任何嚴肅的願望。作為一個孩子,我太忙了,無暇擔心成人的事情。我曾經一度想成為一名醫生,但後來我意識到我不喜歡血液,而且我不夠聰明。 就是這樣 就是這樣,這些就是 關於我的十個毫無意義的事實。也許你覺得這很有趣並且了解了一些關於我的事情? 如果您想參加,這裡有復制/粘貼格式的問題,可以轉儲到您自己的博客文章中…… 你用牙線嗎? 茶、咖啡還是水? 鞋類偏好? 最喜歡的甜點? 你起床後做的第一件事是? 你想堅持的年齡?…

  • My Coffee Ridden Framework 13 Güncellemesi

    23 Nisan 2026 Yaklaşık bir hafta önce, Framework 13’ümü çalışırken üzerine bir fincan dolusu kahve dökerek nasıl öldürebileceğimden bahsetmiştim. Son gönderide dizüstü bilgisayarı nasıl söktüğümü ve daha kapsamlı bir şekilde temizleyebilmek için bir miktar izopropil alkolün (IPA) teslim edilmesini beklediğimi açıklamıştım. Sevgili okuyucu, IPA ortaya çıktı, elimden geldiğince temizledim ve kuruması için 24 saat beklettim….

  • ماذا يحدث عندما يتوقف الذكاء الاصطناعي عن كونه رخيصًا بشكل مصطنع

    عصر الدعم انتهى. وهنا ما يأتي بعد ذلك. 28 مارس 2026  تقطيع منطق كريسيبوس… لقد كنت أفكر فيما سيحدث عندما يتوقف دعم تكاليف استدلال الذكاء الاصطناعي. كل المختبرات الكبرى تخسر المال بسبب الاستدلال في الوقت الحالي، وهذا سوف يتغير. لا أعرف بالضبط كيف تسير الأمور، ولكن هذا هو ما يدور في ذهني. جيد بما…

  • New Safari extension: ChangeTheHeaders to customize HTTP request headers

    New Safari extension: ChangeTheHeaders to customize HTTP request headers Underpass App Company News March 27, 2025By Jeff Johnson of Underpass App Company I’m happy to announce that I’ve just released a new app! ChangeTheHeaders is a Safari extension for iOS, iPadOS, macOS, and visionOS that allows you to customize HTTP request headers such as Accept,…

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *