ذات مرة ، من أجل الضحك ، قررت إثبات إمكانية عكس العملية وتعلم كيفية إنشاء JavaScript (أو بالأحرى ، Asm.js) من رمز الجهاز. تم اختيار QEMU للتجربة ، في وقت لاحق تم كتابة مقال عن هبر. في التعليقات ، نصحني بإعادة صياغة المشروع على WebAssembly ، ولم أشعر بنفسي بترك المشروع شبه مكتمل بنفسي ... استمر العمل ، ولكن ببطء شديد ، والآن ، في هذه المقالة ، ظهر تعليق حول الموضوع "إذن كيف انتهى؟". على إجابتي التفصيلية ، سمعت "إنها تشد على مقال". حسنا ، إذا قمت بالسحب ، سيكون هناك مقال. ربما شخص ما سوف يأتي في متناول اليدين. من خلاله ، يتعرّف القارئ على بعض الحقائق المتعلقة بالجهاز والتي تعمل على إنشاء كود QEMU ، وكذلك كيفية كتابة برنامج التحويل البرمجي Just-in-Time لتطبيق الويب.
المهام
نظرًا لأنني تعلمت بالفعل كيفية "نقل" QEMU إلى JavaScript ، فقد تقرر هذه المرة القيام بذلك بحكمة وعدم تكرار الأخطاء القديمة.
عدد مرات الخطأ: فرع من نقطة الإصدار
كان خطأي الأول هو تقسيم روايتي من الإصدار الأولي 2.4.1. ثم بدت لي فكرة جيدة: إذا كان إصدار النقطة موجودًا ، فمن المحتمل أن يكون أكثر استقرارًا من 2.4 بسيط ، وحتى أكثر من ذلك بكثير من الفروع master
. وبما أنني كنت أخطط لإضافة قدر لا بأس به من الأخطاء ، فلست بحاجة إلى الغرباء على الإطلاق. لذلك ربما حدث ما حدث. ولكن هذا هو الحظ السيئ: QEMU لا تقف صامدة ، وفي مرحلة ما أعلنوا حتى تحسين رمز المئة الذي تم إنشاؤه بحلول عام 10. "نعم ، أنا الآن أتجمد" وانقطع . هنا يجب أن نجري استنباطًا: نظرًا لطبيعة QEMU.js ذات الترابط المفرد وحقيقة أن QEMU الأصلي لا يعني عدم وجود تعدد مؤشرات ترابط (أي ، من الأهمية بمكان أن تكون قادرة على تشغيل العديد من مسارات التعليمات البرمجية غير ذات الصلة في نفس الوقت ، بدلاً من "تكرار كل النوى") كان على "الخروج" لتكون قادرة على الاتصال من الخارج. هذا خلق بعض المشاكل دمج الطبيعية. ومع ذلك ، فإن حقيقة أن بعض التغييرات من الفرع master
، والتي حاولت دمج الكود الخاص بي بها ، تم اختيار الكرز أيضًا في إصدار نقطة (وبالتالي ، في فرعي) ، على الأرجح ، لن تضيف مزيدًا من الراحة.
بشكل عام ، قررت أنه على أي حال النموذج الأولي المنطقي طرد تفكيك للأجزاء وبناء نسخة جديدة من نقطة الصفر على أساس شيء أعذب والآن من master
.
الخطأ الثاني: منهجية TLP
في الواقع ، هذا ليس خطأ ، بشكل عام - إنه مجرد ميزة لإنشاء مشروع في ظروف سوء الفهم التام لـ "أين وكيف تتحرك؟" ، وبشكل عام "هل سنصل إلى هناك؟". في ظل هذه الظروف ، كانت البرمجة خيارًا مبررًا ، لكن بالطبع ، لم أكن أرغب مطلقًا في تكرار ذلك بلا ضرورة. هذه المرة أردت أن أفعلها بحكمة: ارتكاب الذرات ، وتغيير الكود المتعمد (وليس "توحيد الأحرف العشوائية معًا حتى تجميعها (مع التحذيرات)" ، كما قال لينوس تورفالدس ذات مرة عن شخص ما ، إذا كنت تعتقد أن ويكيتنيك) ، إلخ.
الخطأ الثالث: عدم معرفة فورد لتسلق الماء
لم أتخلص من ذلك تمامًا بعد ، لكنني الآن قررت عدم اتباع مسار أقل مقاومة على الإطلاق ، وأقوم بذلك "بطريقة بالغة" ، أي كتابة مقالة TCG الخاصة بي من الصفر حتى لا أقول لاحقًا ، "نعم ، إنها بالطبع ، ببطء ، لكن لا يمكنني التحكم في كل شيء - يتم كتابة TCI على هذا النحو ... ". بالإضافة إلى ذلك ، بدا هذا في البداية كحل واضح ، لأنني كنت أنتج كودًا ثنائيًا . كما يقول المثل ، "لقد جمعت Ghent ، لكن ليس هذا واحد": الكود ، بالطبع ، ثنائي ، لكن لا يمكن نقل التحكم إليه تمامًا مثل ذلك - يجب دفعه صراحةً إلى المتصفح لتجميعه ، مما ينتج عنه كائن معين من عالم JS ، والذي لا تزال بحاجة إلى حفظ مكان ما. ومع ذلك ، على طبيعي تصميمات RISC ، كما أفهمها ، الموقف المعتاد هو الحاجة إلى مسح ذاكرة التخزين المؤقت للتعليمات بشكل صريح للرمز الذي تم تجديده - إذا لم يكن هذا هو ما نحتاجه ، فهو قريب على الأقل. بالإضافة إلى ذلك ، من محاولتي الأخيرة ، علمت أن عنصر التحكم لا يبدو أنه تم نقله إلى منتصف كتلة الترجمة ، وبالتالي ، نحن لسنا بحاجة إلى تفسير الشفرة الجانبية من أي إزاحة ، ويمكننا ببساطة توليدها حسب الوظيفة على السل.
جاء وركل
على الرغم من أنني بدأت في إعادة كتابة الكود مرة أخرى في يوليو ، إلا أن Pendel السحرية تسللت دون أن يلاحظها أحد: عادةً ما تأتي الرسائل من GitHub كإخطارات بالردود على المشكلات وطلبات السحب ، ثم فجأة ، يقول Binaryen كواجهة خلفية qemu في السياق ، "هنا لقد فعل شيئًا كهذا ، ربما سيقول شيئًا ". كان حول استخدام مكتبة Binaryen المتعلقة بـ Emscripten لإنشاء WASM JIT. حسنًا ، قلت إن لديك ترخيص Apache 2.0 هناك ، ويتم توزيع QEMU ككل تحت GPLv2 ، وأنها غير متوافقة للغاية. اتضح فجأة أنه يمكن تصحيح الترخيص بطريقة أو بأخرى (لا أعرف: ربما التغيير ، ربما الترخيص المزدوج ، ربما شيء آخر ...). هذا ، بالطبع ، جعلني سعيدًا ، لأنني نظرت بالفعل إلى تنسيق WebAssembly الثنائي عدة مرات بحلول ذلك الوقت ، وكنت حزينًا بطريقة ما وغير مفهومة بالنسبة لي. كان هناك مكتبة هنا ستلتهم الكتل الأساسية باستخدام الرسم البياني الانتقالي ، وتصدر الرمز الفرعي ، بل وتطلقها في المترجم إذا لزم الأمر.
ثم كان هناك أيضًا خطاب في القائمة البريدية لـ QEMU ، لكن هذا على الأرجح هو السؤال ، "من الذي يحتاجها على الإطلاق؟" وفجأة ، كان ضرورياً. على الأقل ، يمكنك تجميع حالات الاستخدام هذه معًا إذا كانت تعمل بشكل أكثر ذكاءً أو أقل:
- إطلاق أي شيء التدريس دون أي تثبيت على الإطلاق
- المحاكاة الافتراضية على iOS ، حيث وفقًا للشائعات ، فإن التطبيق الوحيد الذي لديه الحق في توليد الشفرة أثناء التنقل هو محرك JS (هل هذا صحيح؟)
- عرض مصغر لنظام التشغيل - قرص واحد ، مدمج ، جميع أنواع البرامج الثابتة ، إلخ ...
ميزات وقت تشغيل المتصفح
كما قلت ، فإن QEMU مرتبط بعدة مؤشرات ترابط ، لكنه ليس في المتصفح. حسنًا ، هذا ليس مثل ... في البداية لم يكن هناك أي شيء على الإطلاق ، ثم WebWorkers ظهر - كما أفهمها ، هذا هو multithreading استنادًا إلى رسالة تمر دون متغيرات متبادلة متبادلة. بطبيعة الحال ، هذا يخلق مشاكل كبيرة عند ترقية التعليمات البرمجية الموجودة على أساس نموذج الذاكرة المشتركة. ثم ، تحت ضغط من الجمهور ، تم تنفيذه تحت اسم SharedArrayBuffers
. قاموا بتقديمه تدريجياً ، واحتفلوا بإطلاقه في متصفحات مختلفة ، ثم احتفلوا بالعام الجديد ، ثم الانهيار ... وبعد ذلك توصلوا إلى استنتاج مفاده أن وقحا ، لا تقاس الوقت ، ولكن بمساعدة الذاكرة المشتركة وتدفق يزيد العداد ، لا يزال دقيقًا . حتى أنهم إيقاف تشغيل multithreading مع الذاكرة المشتركة. يبدو أنهم أعادوا تشغيله لاحقًا ، ولكن ، كما أصبح واضحًا من التجربة الأولى ، هناك حياة بدونها ، وإذا كان الأمر كذلك ، فسنحاول القيام بذلك دون الاعتماد على تعدد العمليات.
الميزة الثانية هي استحالة التلاعب بمستوى منخفض باستخدام المكدس: لا يمكنك فقط حفظ السياق الحالي والتحول إلى سياق جديد باستخدام مكدس جديد. تتم إدارة مكدس الاستدعاءات بواسطة الجهاز الظاهري JS. يبدو ، ما هي المشكلة ، لأننا ما زلنا قررنا إدارة التدفقات السابقة يدويًا بالكامل؟ والحقيقة هي أن كتلة المدخلات والمخرجات في QEMU يتم تنفيذها من خلال coroutines ، وهنا التلاعب في المستوى المنخفض من المفيد سيكون لنا. لحسن الحظ ، يحتوي Emscipten بالفعل على آلية لعمليات غير متزامنة ، حتى اثنتين: Asyncify و Emterpreter . الأول يعمل من خلال النفخ الكبير لرمز JavaScript الذي تم إنشاؤه ولم يعد مدعومًا. والثاني هو "الطريقة الصحيحة" الحالية ويعمل من خلال توليد كود الشفرة لمترجمها الخاص. إنه يعمل ، بالطبع ، ببطء ، لكنه لا يضخم الشفرة. صحيح ، يجب أن يعزى دعم coroutine لهذه الآلية من تلقاء نفسها (كانت هناك بالفعل coroutines مكتوبة تحت Asyncify وكان هناك تنفيذ تقريبا نفس API ل Emterpreter ، كان عليك فقط الاتصال بهم).
في الوقت الحالي ، لم أتمكن حتى الآن من تقسيم الشفرة إلى تجميع في WASM وتفسيرها باستخدام Emterpreter ، لذلك لا تعمل أجهزة الحظر بعد (انظر السلسلة التالية ، كما يقولون ...). وهذا هو ، في النهاية ، يجب أن تحصل على شيء مضحك ذو طبقات:
- تفسير كتلة I / O. حسنًا ، ماذا ، هل كنت حقًا تتوقع NVMe مقلدًا بأداء أصلي؟ :)
- كود QEMU الرئيسي المترجمة بشكل ثابت (مترجم ، أجهزة مقلدة أخرى ، إلخ.)
- WASM المترجمة بشكل ديناميكي رمز الضيف
ميزات مصادر QEMU
كما ربما تكون قد خمنت بالفعل ، فإن رمز المضاهاة لبنى الضيوف ورمز إنشاء تعليمات الجهاز المضيف من QEMU منفصلان. في الواقع ، هناك حتى اصعب قليلا:
- هناك هياكل الضيف
- هناك مسرعات ، وهي KVM لمحاكاة الأجهزة الافتراضية على نظام Linux (لأنظمة الضيف والمضيف المتوافقة) ، TCG لتوليد كود JIT في أي مكان. بدءًا من QEMU 2.9 ، ظهر دعم لمعايير الأجهزة الافتراضية HAXM على Windows ( التفاصيل )
- إذا تم استخدام TCG ، وليس ظاهرية الأجهزة ، عندها يكون هناك دعم منفصل لإنشاء الشفرة لكل هندسة مضيفة ، وكذلك لمترجم عالمي
- ... وحولها - الأجهزة الطرفية التي تمت محاكاتها ، واجهة المستخدم ، الترحيل ، إعادة تشغيل السجلات ، إلخ.
بالمناسبة ، هل تعلم: تستطيع QEMU محاكاة ليس فقط الكمبيوتر بالكامل ، ولكن أيضًا المعالج لعملية مستخدم منفصلة في kernel المضيف ، والذي يتم استخدامه ، على سبيل المثال ، بواسطة fuzzer AFL لأدوات الثنائيات. ربما شخص ما يريد أن ينقل وضع تشغيل QEMU هذا إلى JS؟ ؛)
مثل معظم البرامج المجانية الطويلة الأمد ، تم تصميم QEMU من خلال مكالمة configure
make
. لنفترض أنك قررت إضافة شيء ما: خلفية TCG ، تنفيذ سلسلة رسائل ، شيء آخر. لا تتسرع في الفرحة / تكون مرعوبة (تسطير حسب الضرورة) احتمال التواصل مع Autoconf - في الواقع ، يبدو أن configure
في QEMU مكتوب ذاتيًا ولا يوجد شيء يمكن أن تنشئه.
WebAssembly
فما هو هذا الشيء - WebAssembly (الملقب WASM)؟ هذا بديل لـ Asm.js ، والآن لم يعد يتظاهر بأنه رمز JavaScript صالح. على العكس من ذلك ، فهو ثنائي محسّن ومُحسّن ، وحتى مجرد كتابة عدد صحيح فيه ليس بسيطًا جدًا: يتم تخزينه بتنسيق LEB128 من أجل الاكتناز .
ربما تكون قد سمعت عن خوارزمية إعادة الانتقال لـ Asm.js - استعادة إرشادات التحكم في تدفق التنفيذ "عالية المستوى" (أي ، إذا ، إذا ، آخر ، حلقات ، وما إلى ذلك) التي يتم من خلالها ضبط محركات JS من LLVM IR ذات المستوى المنخفض ، أقرب إلى رمز الجهاز الذي ينفذه المعالج. بطبيعة الحال ، فإن التمثيل المتوسط لل QEMU هو أقرب إلى الثاني. يبدو أنه هنا هو ، bytecode ، نهاية العذاب ... ثم الكتل ، if-then-else والحلقات! ..
وهذا سبب آخر يجعل Binaryen مفيدًا: إنه ، بالطبع ، يمكنه قبول كتل عالية المستوى قريبة مما سيتم تخزينه في WASM. ولكنه يمكن أيضًا أن ينتج رمزًا من الرسم البياني للكتل الأساسية والانتقالات بينها. حسنًا ، لقد قلت بالفعل أنه يخفي تنسيق تخزين WebAssembly خلف واجهة برمجة تطبيقات C / C ++ المريحة.
TCG (مولد الرمز الصغير)
كانت TCG في الأصل خلفية للمترجم C. وبعد ذلك ، على ما يبدو ، لم تستطع المنافسة مع دول مجلس التعاون الخليجي ، ولكن في النهاية وجدت مكانها في QEMU كآلية لتوليد الكود لمنصة المضيف. هناك أيضًا واجهة TCG الخلفية التي تنشئ بعض الرموز التجريدية ، والتي يتم تنفيذها على الفور من قبل المترجم الفوري ، لكنني قررت أن أغادر هذه المرة هذه المرة. ومع ذلك ، فإن حقيقة أن QEMU لديها بالفعل القدرة على تمكين الانتقال إلى السل الذي تم إنشاؤه عبر وظيفة tcg_qemu_tb_exec
كانت مفيدة للغاية بالنسبة لي.
لإضافة خلفية TCG جديدة إلى QEMU ، تحتاج إلى إنشاء دليل فرعي tcg/< >
(في هذه الحالة ، tcg/binaryen
) ، وهناك ملفان به: tcg-target.h
و tcg-target.inc.c
وتسجيل كل شيء هذا هو configure
. يمكنك وضع ملفات أخرى هناك ، ولكن ، كما يمكنك تخمين أسماء هذين ، سيتم تضمين كلاهما في مكان ما: واحد كملف رأس منتظم (سيتم تضمينه في tcg/tcg.h
، وسيكون أحد الملفات بالفعل في ملفات أخرى في tcg
، accel
وليس فقط) ، والآخر فقط tcg/tcg.c
شفرة في tcg/tcg.c
، ولكن لديه إمكانية الوصول إلى وظائفه الثابتة.
بعد أن قررت أن أقضي وقتًا طويلاً في الإجراءات التفصيلية ، وكيفية عملها ، قمت ببساطة بنسخ "الهياكل العظمية" لهذين الملفين من تطبيق آخر للجهة الخلفية ، مع الإشارة بصراحة إلى ذلك في رأس الترخيص.
يحتوي tcg-target.h
بشكل أساسي على إعدادات في شكل #define
s:
- كم عدد السجلات ومدى اتساعها في البنية المستهدفة (لدينا - بقدر ما نريد ، هناك الكثير - السؤال هو أكثر من ما سيولده المتصفح في كود أكثر كفاءة على بنية "هدف تام" ...)
- محاذاة تعليمات المضيف: في x86 ، وفي TCI ، لا تتم محاذاة التعليمات على الإطلاق ، لكنني سأضع التعليمات البرمجية في المخزن المؤقت وليس الإرشادات على الإطلاق ، لكن مؤشرات إلى بنيات مكتبة Binaryen ، لذلك أقول: 4 بايت
- ما هي الإرشادات الاختيارية التي يمكن أن تولدها الواجهة الخلفية - قم بتشغيل كل شيء نجده في Binaryen ، والسماح للمسرّع بتقسيم الباقي إلى تعليمات أبسط
- أي الحجم التقريبي لذاكرة التخزين المؤقت TLB مطلوب بواسطة الواجهة الخلفية. الحقيقة هي أن كل شيء في QEMU أمر خطير: على الرغم من وجود وظائف المساعد التي تقوم بتحميل / تخزين مع الأخذ في الاعتبار MMU الضيف (وأين بدون ذلك الآن؟) ، فإنها حفظ ذاكرة التخزين المؤقت الترجمة الخاصة بهم في شكل بنية ، والمعالجة مريحة لتضمين مباشرة إلى كتل الترجمة. والسؤال هو ، ما هو الأوفست في هذا الهيكل الذي تتم معالجته بكفاءة أكثر من خلال سلسلة صغيرة وسريعة من الأوامر
- هنا يمكنك تحريف الغرض من سجل واحد أو اثنين من السجلات المحجوزة ، وتمكين استدعاء السل من خلال وظيفة ، واختياريا وصف بضع وظائف
inline
صغيرة مثل flush_icache_range
(ولكن هذه ليست حالتنا)
tcg-target.inc.c
أكبر بكثير ويحتوي على العديد من الوظائف المطلوبة:
- التهيئة ، مع الإشارة ، في جملة أمور ، إلى القيود المفروضة على التعليمات التي يمكن أن تعمل بها المعاملات. نسخ بوقاحة من قبلي آخر
- وظيفة قبول تعليمات واحدة من bytecode الداخلية
- هنا يمكنك وضع وظائف مساعدة ، وهنا يمكنك أيضًا استخدام وظائف ثابتة من
tcg/tcg.c
بنفسي ، اخترت الاستراتيجية التالية: في الكلمات الأولى من كتلة الترجمة التالية ، كتبت أربعة مؤشرات: علامة البداية (قيمة معينة في محيط 0xFFFFFFFF
، والتي حددت الحالة الحالية للسل) ، والسياق ، والوحدة المولدة ، والرقم السحري للتصحيح. أولاً ، تم تعيين الملصق على 0xFFFFFFFF - n
، حيث n
هو رقم موجب صغير ، وفي كل مرة يتم فيها زيادة المترجم بمقدار 1. عندما وصلت إلى 0xFFFFFFFE
، حدث التجميع ، تم تخزين الوحدة النمطية في جدول الوظائف ، تم استيرادها إلى "قاذفة" صغيرة ، غادر التنفيذ tcg_qemu_tb_exec
، وتم حذف الوحدة النمطية من ذاكرة QEMU.
لإعادة صياغة الكلاسيكيات ، "عكاز ، مقدار المتداخل الذي يتداخل به هذا الصوت من أجل القلب ...". ومع ذلك ، كانت الذاكرة تتسرب في مكان ما. وكانت ذاكرة يديرها QEMU! كان لدي رمز ، عند كتابة التعليمات التالية (حسنًا ، أي مؤشر) ، قام بحذف التعليمة التي كان الارتباط بها في هذا المكان سابقًا ، لكن ذلك لم يساعد. في الواقع ، في أبسط الحالات ، يخصص QEMU الذاكرة عند بدء التشغيل ويكتب الرمز الذي تم إنشاؤه هناك. عندما ينتهي المخزن المؤقت ، يتم طرح الكود ، ويبدأ الكود التالي في مكانه.
بعد أن درست الكود ، أدركت أن العكاز ذي الرقم السحري سمح لنا بعدم الوقوع في تدمير الكومة ، مما أدى إلى تحرير شيء خاطئ في المخزن المؤقت غير المهيأ في أول تمريرة. ولكن من الذي يحل محل المخزن المؤقت لتجاوز وظيفتي في وقت لاحق؟ كما نصح مطورو Emscripten ، بعد أن واجهت مشكلة ، قمت بنقل الكود الناتج إلى التطبيق الأصلي ، واضبط Mozilla Record-Replay عليه ... بشكل عام ، ونتيجة لذلك ، أدركت شيئًا بسيطًا: تم تخصيص struct TranslationBlock
مع وصفها لكل كتلة. تخمين أين ... هذا صحيح ، أمام الكتلة مباشرة في المخزن المؤقت. بعد أن أدركت ذلك ، قررت ربطه بالعكازات (على الأقل بعضها) ، وقمت ببساطة بإخراج الرقم السحري ، وقمت بنقل الكلمات المتبقية إلى struct TranslationBlock
، وإنشاء قائمة مرتبطة واحدة يمكنك الانتقال إليها بسرعة عند إعادة تعيين ذاكرة التخزين المؤقت للترجمة وتحرير الذاكرة.
بقيت بعض العكازات: على سبيل المثال ، مؤشرات ملحوظة في المخزن المؤقت للكود - بعضها ببساطة BinaryenExpressionRef
، أي أنها تبحث في التعبيرات التي تحتاج إلى وضعها خطيًا في الوحدة الأساسية التي تم إنشاؤها ، جزء - شرط الانتقال بين WBs ، الجزء - إلى أين تذهب. حسنًا ، هناك كتل جاهزة بالفعل لـ Relooper ، والتي يجب توصيلها وفقًا للشروط. للتمييز بينهما ، يتم استخدام الافتراض بأن جميعها محاذاة على الأقل أربع بايت ، لذلك يمكنك استخدام البتات السفلية للتسمية بأمان ، تحتاج فقط إلى تذكر إزالتها إذا لزم الأمر. بالمناسبة ، يتم استخدام هذه التسميات بالفعل في QEMU للإشارة إلى سبب ترك دورة TCG.
باستخدام ثنائيان
تحتوي الوحدات النمطية في WebAssembly على وظائف ، كل منها يحتوي على نص يمثل تعبيرًا. التعبيرات عبارة عن عمليات أحادية وثنائية ، وكتل تتكون من قوائم التعبيرات الأخرى ، وتدفق التحكم ، إلخ. كما قلت بالفعل ، يتم تنظيم التحكم في التدفق هنا على وجه التحديد كفروع عالية المستوى ، وحلقات ، ومكالمات الوظائف ، إلخ. لا يتم تمرير الوسائط إلى الدالات على المكدس ، ولكن بشكل صريح ، كما في JS. هناك متغيرات عالمية ، لكنني لم أستخدمها ، لذلك لن أتحدث عنها.
تحتوي الوظائف أيضًا على متغيرات محلية ، مرقمة من البداية ، من النوع: int32 / int64 / float / double. المتغيرات المحلية الأولى هي الوسائط التي يتم تمريرها إلى الوظيفة. يرجى ملاحظة أنه على الرغم من أن كل شيء هنا ليس منخفضًا تمامًا فيما يتعلق بتدفق التحكم ، إلا أن الأرقام الصحيحة لا تحمل علامة / علامة غير موقعة: كيف سيتصرف الرقم يعتمد على رمز العملية.
بشكل عام ، يوفر Binaryen واجهة برمجة تطبيقات C-API بسيطة : تقوم بإنشاء وحدة نمطية ، حيث تقوم بإنشاء تعبيرات - أحادي ، وثنائي ، وكتل من التعبيرات الأخرى ، وتدفق التحكم ، إلخ. ثم تقوم بإنشاء وظيفة ، والتي يحتاج جسمها إلى تحديد تعبير. إذا كان لديك ، مثلي ، رسم بياني انتقالي منخفض المستوى ، فسيساعدك مكون relooper. بقدر ما أفهم ، من الممكن استخدام تحكم عالي المستوى لتدفق التنفيذ في الكتلة ، طالما أنه لا يتجاوز حدود الكتلة - أي أنه من الممكن إنشاء فرع مسار سريع داخلي / مسار بطيء داخل رمز معالجة ذاكرة التخزين المؤقت TLB المضمنة ، ولكن لا توجد وسيلة للتداخل مع تدفق التحكم "الخارجي" المدمج . عندما تقوم بتحرير relooper ، يتم تحرير كتلها ، عند تحرير وحدة نمطية ، وتختفي التعبيرات والوظائف ، وما إلى ذلك ، المخصصة في الحلبة الخاصة بها .
ومع ذلك ، إذا كنت ترغب في ترجمة الشفرة أثناء التنقل دون الحاجة إلى إنشاء مثيل المترجم الشفهي وحذفه ، فقد يكون من المنطقي نقل هذا المنطق إلى ملف C ++ ، ومن هناك يمكنك التحكم مباشرةً في مكتبة C ++ API بأكملها ، متجاوزة الأغلفة النهائية.
وبالتالي ، لإنشاء رمز ، تحتاج
… — , , — .
--, :
static char buf[1 << 20]; BinaryenModuleOptimize(MODULE); BinaryenSetMemory(MODULE, 0, -1, NULL, NULL, NULL, NULL, NULL, 0, 0); int sz = BinaryenModuleWrite(MODULE, buf, sizeof(buf)); BinaryenModuleDispose(MODULE); EM_ASM({ var module = new WebAssembly.Module(new Uint8Array(wasmMemory.buffer, $0, $1)); var fptr = $2; var instance = new WebAssembly.Instance(module, { 'env': { 'memory': wasmMemory,
- QEMU JS , ( ), . , translation block, , struct TranslationBlock
.
, ( ) Firefox. Chrome - , - WebAssembly, ...
. , , - . , . , WebAssembly , JS, , , .
: 32- , Binaryen, - - 2 32- . , Binaryen . ?
-, « , 32- Linux?» . , : 1 2 Gb.
- ( ). , — . « : , ...».
… Valgrind-, , , , , Valgrind :)
, - , ...