QEMU.js: اوس جدي او د WASM سره

یو وخت ما د ساتیرۍ لپاره پریکړه وکړه د پروسې د بیرته راګرځیدو وړتیا ثابت کړئ او زده کړئ چې څنګه د ماشین کوډ څخه جاوا سکریپټ (په دقیق ډول Asm.js) تولید کړئ. QEMU د تجربې لپاره غوره شوی و، او څه موده وروسته د حبر په اړه یوه مقاله لیکل شوې وه. په نظرونو کې ما ته مشوره ورکړل شوه چې پروژه په WebAssembly کې بیا جوړ کړم، او حتی خپل ځان پریږدم تقریبا بشپړ شوی ما یو څه نه غوښتل پروژه ... کار روان و، مګر ډیر ورو، او اوس، پدې وروستیو کې په مقاله کې راڅرګند شو تبصره په موضوع "نو دا ټول څنګه پای ته ورسیدل؟" زما د مفصل ځواب په ځواب کې، ما اوریدلي چې "دا د یوې مقالې په څیر ښکاري." ښه، که تاسو کولی شئ، یو مضمون به وي. شاید یو څوک به دا ګټور ومومي. له دې څخه لوستونکی به د QEMU کوډ نسل بیک انډونو ډیزاین په اړه ځینې حقایق زده کړي ، په بیله بیا د ویب غوښتنلیک لپاره د Just-in-Time کمپیلر لیکلو څرنګوالی.

دندې

له هغه ځایه چې ما دمخه زده کړې وه چې څنګه جاواسکریپټ ته QEMU "په یو ډول" پورټ کړم ، دا ځل پریکړه وشوه چې دا په هوښیارۍ سره ترسره کړم او زړې غلطۍ تکرار نه کړم.

د تېروتنې شمېره: د پوائنټ خوشې کولو څخه څانګه

زما لومړۍ تېروتنه دا وه چې زما نسخه د اپ سټریم نسخه 2.4.1 څخه جوړه کړه. بیا دا ماته یو ښه نظر ښکاري: که چیرې د ټکي خوشې شتون شتون ولري ، نو دا شاید د ساده 2.4 څخه ډیر مستحکم وي ، او حتی نور هم څانګه master. او له هغه وخته چې ما پلان کړی و چې د خپلو کیګونو کافي اندازه اضافه کړم، ما د بل چا اړتیا نه درلوده. شاید دا څنګه وګرځید. مګر دلته خبره ده: QEMU لاهم ولاړ نه دی، او په ځینو وختونو کې دوی حتی د 10 سلنې لخوا د تولید شوي کوډ اصلاح اعلان کړ. "هو، اوس زه کنګل کیږم،" ما فکر وکړ او مات شو. دلته موږ اړتیا لرو چې یو تحلیل وکړو: د QEMU.js د واحد تار شوي ماهیت له امله او دا حقیقت چې اصلي QEMU د څو-تریډینګ نشتوالي معنی نه لري (یعنې په ورته وخت کې د څو غیر اړونده کوډ لارو چلولو وړتیا ، او نه یوازې "ټول دانه وکاروئ") د دې لپاره خورا مهم دي ، د تارونو اصلي دندې چې ما باید "دا یې وګرځولې" ترڅو وکولی شم له بهر څخه تلیفون وکړم. دا د ادغام په جریان کې ځینې طبیعي ستونزې رامینځته کړې. په هرصورت، حقیقت دا دی چې د څانګې څخه ځینې بدلونونه master، د کوم سره چې ما د خپل کوډ ضمیمه کولو هڅه وکړه ، د پوائنټ ریلیز کې چیری هم غوره شوی و (او له همدې امله زما په څانګه کې) هم شاید اسانتیا اضافه نه کړي.

په عموم کې ، ما پریکړه وکړه چې دا لاهم معنی لري چې پروټوټایپ وغورځوئ ، د برخو لپاره یې جلا کړئ او له سکریچ څخه نوې نسخه د یو څه تازه او اوس څخه جوړه کړئ. master.

دوهمه خطا: د TLP میتودولوژي

په اصل کې، دا یوه تېروتنه نه ده، په عموم کې، دا یوازې د یوې پروژې رامینځته کول دي چې د بشپړ غلط فهم په شرایطو کې دواړه "چیرې او څنګه حرکت وکړي؟" او په عموم کې "ایا موږ به هلته ورسیږو؟" په دې شرایطو کې بې کاره برنامه کول یو توجیه شوی انتخاب و، مګر، په طبیعي توګه، زه نه غواړم دا په غیر ضروري توګه تکرار کړم. دا ځل ما غوښتل دا په هوښیارۍ سره ترسره کړم: اټومي ژمنې ، شعوري کوډ بدلونونه (او نه "تصادفي حروف سره یوځای کول تر هغه چې دا راټول نشي (د اخطارونو سره)" ، لکه څنګه چې لینس توروالډز یوځل د یو چا په اړه وویل ، د ویکي لیکس په وینا) ، او داسې نور.

دریمه تېروتنه: پرته له دې چې فورډ پوه شي اوبو ته ننوځي

زه لا تر اوسه په بشپړه توګه له دې څخه خلاص شوی نه یم، مګر اوس ما پریکړه کړې چې د لږ تر لږه مقاومت لاره تعقیب نه کړم، او دا د "بلوغ په توګه" ترسره کړم، د بیلګې په توګه، زما د TCG پس منظر له سکریچ څخه ولیکئ. باید وروسته ووایم، "هو، دا البته، ورو ورو، مګر زه نشم کولی هرڅه کنټرول کړم - دا څنګه TCI لیکل کیږي ..." سربیره پردې ، دا په پیل کې د څرګند حل په څیر بریښي ، ځکه چې زه بائنری کوډ تولیدوم. لکه څنګه چې دوی وايي، "ګینټ راټول شویу, مګر هغه نه دی": کوډ، البته، بائنری دی، مګر کنټرول په ساده ډول دې ته نشي لیږدول کیدی - دا باید په واضح ډول د تالیف لپاره براوزر ته واستول شي، په پایله کې د JS نړۍ څخه یو مشخص شی، کوم چې لاهم اړتیا لري. په کوم ځای کې خوندي شي. په هرصورت، په نورمال RISC جوړښتونو کې، تر هغه چې زه پوهیږم، یو عادي حالت د بیا رغول شوي کوډ لپاره د لارښوونې کیچ په واضح ډول بیا تنظیمولو ته اړتیا ده - که دا هغه څه نه وي چې موږ ورته اړتیا لرو، نو په هر حالت کې، نږدې دی. برسېره پردې، زما د وروستۍ هڅې څخه، ما زده کړل چې داسې نه بریښي چې کنټرول د ژباړې بلاک منځ ته لیږدول شوی وي، نو موږ واقعیا اړتیا نلرو چې د هیڅ آفسټ څخه تشریح شوي بایټکوډ ته اړتیا نلرو، او موږ کولی شو دا په ساده ډول د TB فعالیت څخه تولید کړو. .

دوی راغلل او ټکان یې ورکړ

که څه هم ما د جولای په میاشت کې د کوډ بیا لیکل پیل کړل ، یو جادویی کک د پام وړ نه و رامینځته شو: معمولا د GitHub څخه لیکونه د مسلو او پل غوښتنو ته د ځوابونو په اړه خبرتیاو په توګه راځي ، مګر دلته ، ناڅاپه په تار کې ذکر Binaryen د qemu پس منظر په توګه په شرایطو کې، "هغه داسې یو څه وکړل، شاید هغه به یو څه ووایي." موږ د Emscripten اړوند کتابتون کارولو په اړه خبرې کولې بینریین د WASM JIT جوړولو لپاره. ښه ، ما وویل چې تاسو هلته د اپاچي 2.0 جواز لرئ ، او QEMU په ټوله کې د GPLv2 لاندې توزیع شوی ، او دوی خورا مطابقت نلري. ناڅاپه دا معلومه شوه چې جواز کیدی شي په یو ډول یې حل کړئ (زه نه پوهیږم: شاید دا بدل کړئ، شاید دوه اړخیز جواز، شاید بل څه ...). دا، البته، زه خوشحاله کړم، ځکه چې په هغه وخت کې ما مخکې له نږدې لیدلی و بائنری بڼه WebAssembly، او زه یو څه غمجن او د پوهیدو وړ وم. دلته یو کتابتون هم و چې د لیږد ګراف سره به بنسټیز بلاکونه وخوري، بایټ کوډ تولید کړي، او حتی د اړتیا په صورت کې به یې پخپله ژباړونکي کې چلوي.

بیا نور هم وو یو لیک د QEMU میلینګ لیست کې، مګر دا د پوښتنې په اړه ډیر څه دي، "څوک ورته اړتیا لري؟" او دا دی ناڅاپه، دا معلومه شوه چې دا اړینه وه. لږترلږه، تاسو کولی شئ د کارولو لاندې امکانات یوځای سکریپ کړئ، که دا ډیر یا لږ چټک کار وکړي:

  • پرته له کوم نصب څخه د زده کړې یو څه پیل کول
  • په iOS کې مجازی کول، چیرې چې د افواهاتو په وینا، یوازینی غوښتنلیک چې په الوتنه کې د کوډ تولید حق لري د JS انجن دی (ایا دا ریښتیا ده؟)
  • د مینی OS مظاهره - واحد فلاپی، جوړ شوی، هر ډول فرم ویئر، او نور ...

د براوزر د چلولو ځانګړتیاوې

لکه څنګه چې ما مخکې وویل، QEMU د ملټي ریډینګ سره تړلی دی، مګر براوزر دا نلري. ښه، دا دی، نه ... په لومړي سر کې دا شتون نه درلود، بیا د ویب کارکونکي ښکاره شول - تر هغه ځایه چې زه پوهیږم، دا د پیغام تیریدو پراساس څو ټریډینګ دی د شریک متغیرونو پرته. په طبیعي توګه، دا د پام وړ ستونزې رامینځته کوي کله چې د ګډ حافظې ماډل پراساس موجود کوډ پورټ کول. بیا یې د ولس تر فشار لاندې هم تر نامه لاندې تطبیق کړ SharedArrayBuffers. دا په تدریجي ډول معرفي شو، دوی په مختلفو براوزرونو کې د هغې پیل ولمانځل، بیا دوی نوی کال ولمانځله، او بیا میلټډاؤن ... وروسته له دې چې دوی دې پایلې ته ورسیدل چې د وخت اندازه کول یا موټی دي، مګر د ګډ حافظې په مرسته. تار د کاونټر زیاتوالی، دا ټول یو شان دی دا به ډیر دقیق کار وکړي. نو موږ د ګډې حافظې سره ملټي ریډینګ غیر فعال کړ. داسې بریښي چې دوی وروسته دا بیرته فعاله کړه ، مګر لکه څنګه چې دا د لومړۍ تجربې څخه څرګنده شوه ، پرته له دې ژوند شتون لري ، او که داسې وي ، نو موږ به هڅه وکړو چې دا په څو ټریډینګ تکیه کولو پرته ترسره کړو.

دویمه ځانګړتیا د سټیک سره د ټیټې کچې لاسوهنې ناممکنیت دی: تاسو نشئ کولی په ساده ډول واخلئ ، اوسنی شرایط خوندي کړئ او د نوي سټیک سره نوي ته لاړشئ. د کال سټیک د JS مجازی ماشین لخوا اداره کیږي. داسې ښکاري چې ستونزه څه ده، ځکه چې موږ لاهم پریکړه کړې چې پخوانی جریان په بشپړ ډول په لاسي ډول اداره کړو؟ حقیقت دا دی چې په QEMU کې بلاک I/O د کورټینونو له لارې پلي کیږي، او دا هغه ځای دی چې د ټیټې کچې سټیک لاسوهنې به په کار کې راشي. خوشبختانه، Emscipten لا دمخه د غیر متناسب عملیاتو لپاره میکانیزم لري، حتی دوه: Asyncify и ژباړونکی. لومړی یې په تولید شوي جاواسکریپټ کوډ کې د پام وړ بلوټ له لارې کار کوي او نور ملاتړ نه کوي. دوهم اوسنۍ "سمه لار" ده او د اصلي ژباړونکي لپاره د بایټکوډ نسل له لارې کار کوي. دا کار کوي، البته، ورو، مګر دا کوډ نه غوړوي. ریښتیا، د دې میکانیزم لپاره د کورټینونو ملاتړ باید په خپلواکه توګه مرسته وشي (د Asyncify لپاره دمخه کورټینونه لیکل شوي وو او د Emterpreter لپاره نږدې ورته API پلي کول شتون درلود، تاسو یوازې د دوی سره نښلولو ته اړتیا لرئ).

په اوس وخت کې، ما لا تر اوسه نه دی توانیدلی چې کوډ په WASM کې راټول شوي او د ایمټرپریټر په کارولو سره تشریح کړي، نو د بلاک وسایل لاهم کار نه کوي (په راتلونکي لړۍ کې وګورئ، لکه څنګه چې دوی وايي ...). دا دی، په پای کې تاسو باید د دې مسخره پرت شوي شی په څیر یو څه ترلاسه کړئ:

  • تشریح شوی بلاک I/O. ښه ، ایا تاسو واقعیا د اصلي فعالیت سره د تقلید شوي NVMe تمه لرئ؟ 🙂
  • په ثابت ډول ترتیب شوی اصلي QEMU کوډ (ژباړونکی، نور نقل شوي وسایل، او نور)
  • په متحرک ډول د میلمنو کوډ WASM ته تالیف شوی

د QEMU سرچینو ځانګړتیاوې

لکه څنګه چې تاسو شاید دمخه اټکل کړی وي ، د میلمنو جوړښتونو تقلید کولو کوډ او د کوربه ماشین لارښوونو رامینځته کولو کوډ په QEMU کې جلا شوی. په حقیقت کې، دا حتی یو څه پیچلی دی:

  • د میلمنو معمارۍ شتون لري
  • ده سرعت کونکي، د بیلګې په توګه ، په لینکس کې د هارډویر مجازی کولو لپاره KVM (د میلمنو او کوربه سیسټمونو لپاره چې یو له بل سره مطابقت لري) ، د JIT کوډ تولید لپاره TCG هرچیرې. د QEMU 2.9 سره پیل کول ، په وینډوز کې د HAXM هارډویر مجازی کولو معیار لپاره ملاتړ څرګند شو (توضیحات)
  • که چیرې TCG کارول کیږي او د هارډویر مجازی نه وي، نو دا د هر کوربه جوړښت لپاره جلا کوډ تولید ملاتړ لري، او همدارنګه د نړیوال ژباړونکي لپاره
  • ... او د دې ټولو په شاوخوا کې - جذب شوي پرفیریلز، د کاروونکي انٹرفیس، مهاجرت، ریکارډ بیا پلی کول، او نور.

په لاره کې، تاسو پوهیږئ: QEMU کولی شي نه یوازې ټول کمپیوټر تقلید کړي ، بلکه په کوربه کرنل کې د جلا کارونکي پروسې لپاره پروسیسر هم رامینځته کړي ، کوم چې کارول کیږي ، د مثال په توګه ، د بائنری وسیلو لپاره د AFL فوزر لخوا. شاید یو څوک وغواړي چې د QEMU عملیاتي حالت JS ته پورټ کړي؟ 😉

د ډیری اوږدمهاله وړیا سافټویر په څیر، QEMU د تلیفون له لارې جوړ شوی configure и make. راځئ چې ووایو تاسو پریکړه وکړه چې یو څه اضافه کړئ: د TCG بیکینډ، د تار تطبیق، بل څه. د Autoconf سره د خبرو اترو په امکان کې د خوښ/ویریدلو (د مناسبو په توګه اشاره کولو) لپاره بیړه مه کوئ - په حقیقت کې، configure QEMU په ښکاره ډول پخپله لیکل شوی او له هیڅ شی څخه نه رامینځته شوی.

ویب پاڼه

نو دا څه شی دی چې د WebAssembly (عرف WASM) په نوم یادیږي؟ دا د Asm.js لپاره بدیل دی، نور د جاوا سکریپټ کوډ د اعتبار وړ نه وي. برعکس، دا په بشپړه توګه بائنری او مطلوب دی، او حتی په ساده ډول په دې کې د عدد لیکل خورا ساده ندي: د کمپیکٹیت لپاره، دا په بڼه کې ساتل کیږي. LEB128.

تاسو ممکن د Asm.js لپاره د بیرته راستنیدو الګوریتم په اړه اوریدلي وي - دا د "لوړې کچې" جریان کنټرول لارښوونو بیا رغونه ده (یعنې که بیا - نو، لوپس، او نور)، د کوم لپاره چې د JS انجنونه ډیزاین شوي، له دې څخه. د ټیټې کچې LLVM IR، د پروسیسر لخوا اجرا شوي ماشین کوډ ته نږدې. په طبیعي توګه، د QEMU منځمهاله استازیتوب دویم ته نږدې دی. داسې ښکاري چې دلته دا دی، بایټکوډ، د عذاب پای ... او بیا دلته بلاکونه دي، که بیا - بل او لوپس! ..

او دا یو بل دلیل دی چې ولې بائنریین ګټور دی: دا کولی شي په طبیعي ډول د لوړې کچې بلاکونه ومني هغه څه ته نږدې چې په WASM کې زیرمه کیږي. مګر دا کولی شي د لومړني بلاکونو ګراف څخه کوډ هم تولید کړي او د دوی ترمینځ لیږدونه. ښه، ما دمخه ویلي دي چې دا د مناسب C/C++ API شاته د WebAssembly ذخیره بڼه پټوي.

TCG (کوچنی کوډ جنریټر)

GCT په اصل کې وه د C کمپیلر لپاره backend. بیا، په ښکاره ډول، دا نشي کولی د GCC سره سیالي وکړي، مګر په پای کې یې د کوربه پلیټ فارم لپاره د کوډ تولید میکانیزم په توګه په QEMU کې خپل ځای وموند. دلته د TCG بیکینډ هم شتون لري چې یو څه خلاص بایټ کوډ رامینځته کوي ، کوم چې سمدلاسه د ژباړونکي لخوا اجرا کیږي ، مګر ما پریکړه وکړه چې دا ځل د دې کارولو څخه ډډه وکړم. په هرصورت، حقیقت دا دی چې په QEMU کې دا دمخه ممکنه ده چې د فعالیت له لارې تولید شوي TB ته لیږد فعال کړئ. tcg_qemu_tb_exec، دا زما لپاره خورا ګټور ثابت شو.

QEMU ته د نوي TCG پس منظر اضافه کولو لپاره، تاسو اړتیا لرئ یو فرعي لارښود جوړ کړئ tcg/<имя архитектуры> (په دې حالت کې، tcg/binaryen)، او دا دوه فایلونه لري: tcg-target.h и tcg-target.inc.c и راجستر دا ټول په اړه دي configure. تاسو کولی شئ هلته نورې فایلونه واچوئ، مګر، لکه څنګه چې تاسو د دې دوو نومونو څخه اټکل کولی شئ، دوی به په یو ځای کې شامل شي: یو د منظم سرلیک فایل په توګه (دا په کې شامل دی. tcg/tcg.h، او دا یو لا دمخه په لارښودونو کې په نورو فایلونو کې دی tcg, accel او نه یوازې)، بل - یوازې د کوډ ټوټې په توګه tcg/tcg.c، مګر دا خپلو جامد کارونو ته لاسرسی لري.

پریکړه کول چې زه به ډیر وخت په تفصیلي څیړنو کې مصرف کړم چې دا څنګه کار کوي ، ما په ساده ډول د دې دوه فایلونو "سکالټونز" د بل پس منظر پلي کولو څخه کاپي کړل ، په صادقانه توګه دا د جواز سرلیک کې په ګوته کوي.

د دوتنې tcg-target.h په فورمه کې په عمده ډول ترتیبات شامل دي #define-s:

  • په نښه شوي جوړښت کې څومره راجسترونه او په کوم عرض کې شتون لري (موږ څومره چې غواړو، څومره چې موږ غواړو - پوښتنه د دې په اړه ډیره ده چې د "بشپړ هدف" جوړښت کې د براوزر لخوا به ډیر اغیزمن کوډ کې څه رامینځته شي. ...)
  • د کوربه لارښوونو سمون: په x86 کې، او حتی په TCI کې، لارښوونې په سمه توګه ندي، مګر زه به د کوډ بفر کې ځای پرځای کړم چې لارښوونې نه، مګر د Binaryen کتابتون جوړښتونو ته اشاره کوي، نو زه به ووایم: 4 بایټس
  • کوم اختیاري لارښوونې چې بیکینډ کولی شي تولید کړي - موږ هر هغه څه شامل کوو چې موږ یې په باینرین کې پیدا کوو، سرعت کوونکی اجازه راکړئ چې پاتې نور په ساده ډول مات کړو
  • د شاليد لخوا غوښتل شوي د TLB کیچ نږدې اندازه څومره ده. حقیقت دا دی چې په QEMU کې هرڅه جدي دي: که څه هم دلته مرستندویه دندې شتون لري چې د میلمه MMU په پام کې نیولو سره بار / پلورنځي ترسره کوي (اوس به موږ چیرته یو؟) ، دوی د دوی د ژباړې زیرمه د جوړښت په توګه خوندي کوي ، د کوم پروسس کول اسانه دي چې مستقیم د نشر بلاکونو کې ځای په ځای شي. پوښتنه دا ده چې په دې جوړښت کې کوم آفسیټ په خورا مؤثره توګه د کمانډونو د کوچني او ګړندي ترتیب لخوا پروسس کیږي؟
  • دلته تاسو کولی شئ د یو یا دوه خوندي راجسترونو هدف ټیک کړئ، د فنکشن له لارې د TB تلیفون کول فعال کړئ او په اختیار کې یو څو کوچني تشریح کړئ. inline- لکه دندې flush_icache_range (مګر دا زموږ قضیه نه ده)

د دوتنې tcg-target.inc.cالبته، معمولا په اندازې کې خورا لوی وي او ډیری لازمي دندې لري:

  • پیل کول، په شمول د محدودیتونو په شمول چې کوم لارښوونې کولی شي په کوم عملیات باندې کار وکړي. په ښکاره ډول زما لخوا د بل پس منظر څخه کاپي شوی
  • فنکشن چې یو داخلي بایټکوډ لارښوونې اخلي
  • تاسو کولی شئ دلته مرستندویه فعالیتونه هم واچوئ، او تاسو کولی شئ د جامد افعال څخه هم کار واخلئ tcg/tcg.c

د ځان لپاره، ما لاندې تګلاره غوره کړه: د راتلونکي ژباړې بلاک په لومړیو ټکو کې، ما څلور ټکي لیکلي: د پیل نښه (په شاوخوا کې یو ټاکلی ارزښت 0xFFFFFFFF، کوم چې د TB اوسنی حالت ټاکلی) شرایط، تولید شوي ماډل، او د ډیبګ کولو لپاره جادو شمیره. په لومړي سر کې نښه کېښودل شوه 0xFFFFFFFF - nچیرته n - یو کوچنی مثبت شمیره، او هرکله چې دا د ژباړونکي له لارې اجرا کیږي دا د 1 لخوا ډیریږي. 0xFFFFFFFE، تالیف ترسره شو ، ماډل د فنکشن په میز کې خوندي شو ، په یوه کوچني "لانچر" کې وارد شو ، له کوم ځای څخه اجرا کول tcg_qemu_tb_exec، او ماډل د QEMU حافظې څخه لرې شوی.

د کلاسیکونو د تشریح کولو لپاره، "کرچ، د پروګر د زړه لپاره په دې غږ کې څومره ښکیل دی ...". په هرصورت، حافظه په یو ځای کې خپره شوه. سربیره پردې ، دا د QEMU لخوا اداره شوی حافظه وه! ما یو کوډ درلود چې ، کله چې د راتلونکي لارښوونې لیکلو (ښه ، دا یو پوائنټر) دی ، هغه یې حذف کړ چې لینک یې دمخه پدې ځای کې و ، مګر دا مرسته ونه کړه. په حقیقت کې، په ساده قضیه کې، QEMU په پیل کې حافظه تخصیص کوي او هلته تولید شوی کوډ لیکي. کله چې بفر پای ته ورسیږي، کوډ غورځول کیږي او بل یې په خپل ځای کې لیکل کیږي.

د کوډ له مطالعې وروسته ، ما پوهیده چې د جادو شمیرې چال ما ته اجازه راکړه چې په لومړي پاس کې په غیر پیل شوي بفر کې د یو څه غلط خلاصولو سره د ټوټو ویجاړولو کې ناکامه شم. مګر څوک بفر بیا لیکي ترڅو زما فعالیت وروسته پریږدي؟ لکه څنګه چې د ایمسکریپټین پراختیا کونکي مشوره ورکوي ، کله چې زه له ستونزې سره مخ شوم ، ما پایله کوډ بیرته اصلي غوښتنلیک ته پورټ کړ ، په هغې کې موزیلا ریکارډ - ریپلې تنظیم کړه ... په عموم کې ، په پای کې ما یو ساده شی احساس کړ: د هر بلاک لپاره ، a struct TranslationBlock د وضاحت سره. اټکل وکړئ چیرته ... دا سمه ده، په بفر کې د بلاک څخه مخکې. د دې په پوهیدو سره ، ما پریکړه وکړه چې د کرچونو کارول پریږدم (لږترلږه ځینې) ، او په ساده ډول یې د جادو شمیره وغورځوله ، او پاتې ټکي یې ورته لیږدول. struct TranslationBlock، د یو واحد تړل شوي لیست رامینځته کول چې په چټکۍ سره لیږدول کیدی شي کله چې د ژباړې زیرمه بیا تنظیم شي ، او حافظه خالي کړي.

ځینې ​​کرچونه پاتې دي: د بیلګې په توګه، د کوډ بفر کې نښه شوي نښې - ځینې یې ساده دي BinaryenExpressionRef، دا دی، دوی هغه څرګندونې ګوري چې اړتیا لري په لیکه توګه تولید شوي بنسټیز بلاک کې واچول شي، برخه د BBs تر مینځ د لیږد لپاره شرط دی، برخه هغه ځای دی چې چیرته ځي. ښه ، د ریلوپر لپاره دمخه چمتو شوي بلاکونه شتون لري چې اړتیا لري د شرایطو سره سم وصل شي. د دوی توپیر کولو لپاره، انګیرنه کارول کیږي چې دا ټول لږترلږه څلور بایټس سره سمون لري، نو تاسو کولی شئ په خوندي توګه د لیبل لپاره لږترلږه مهم دوه بټونه وکاروئ، تاسو یوازې د اړتیا په صورت کې د لرې کولو لپاره په یاد ولرئ. په هرصورت ، دا ډول لیبلونه دمخه په QEMU کې کارول شوي ترڅو د TCG لوپ څخه د وتلو دلیل په ګوته کړي.

د Binaryen کارول

په WebAssembly کې ماډلونه دندې لري، چې هر یو یې یو بدن لري، کوم چې یو بیان دی. څرګندونه یونري او بائنری عملیات دي، بلاکونه چې د نورو بیانونو لیست، د کنټرول جریان، او نور لري. لکه څنګه چې ما مخکې وویل، دلته د کنټرول جریان په سمه توګه د لوړې کچې څانګو، لوپونو، فنکشن کالونو، او داسې نورو په څیر تنظیم شوی. د دندو لپاره دلیلونه په سټیک کې نه تیریږي، مګر په واضح ډول، لکه څنګه چې په JS کې. نړیوال متغیرونه هم شتون لري، مګر ما دوی نه دي کارولي، نو زه به تاسو ته د دوی په اړه ونه وایم.

فنکشنونه محلي متغیرونه هم لري، د صفر څخه شمیرل شوي، ډول ډول: int32 / int64 / float / double. په دې حالت کې، لومړی n محلي متغیرونه هغه دلیلونه دي چې فنکشن ته لیږدول شوي. مهرباني وکړئ په یاد ولرئ چې که څه هم دلته هرڅه د کنټرول جریان له مخې په بشپړ ډول ټیټه کچه ندي ، انټیجرونه لاهم د "لاسلیک شوي / نه لاسلیک شوي" صفت نه لري: شمیره څنګه چلند کوي د عملیاتو کوډ پورې اړه لري.

په عمومي توګه خبرې کول، Binaryen چمتو کوي ساده C-API: تاسو یو ماډل جوړ کړئ، په هغه کې څرګندونه رامینځته کړئ - یونیری ، بائنری ، د نورو څرګندونو څخه بلاکونه ، د کنټرول جریان ، او داسې نور. بیا تاسو د هغې د بدن په توګه د بیان سره یو فنکشن رامینځته کړئ. که تاسو، زما په څیر، د ټیټې کچې لیږد ګراف ولرئ، د ریلوپر برخه به ستاسو سره مرسته وکړي. تر هغه ځایه چې زه پوهیږم، دا ممکنه ده چې په بلاک کې د اعدام جریان د لوړې کچې کنټرول وکاروئ، تر هغه چې دا د بلاک له حدودو څخه بهر نه وي - دا دا دی چې دا ممکنه ده چې داخلي ګړندۍ لاره / سست جوړ کړئ. د جوړ شوي TLB کیچ پروسس کولو کوډ کې د لارې شاخ کول، مګر د "بهرني" کنټرول جریان سره مداخله نه کوي. کله چې تاسو یو ریلوپر آزاد کړئ، د هغې بلاکونه خلاصیږي؛ کله چې تاسو یو ماډل آزاد کړئ، د هغې لپاره تخصیص شوي څرګندونې، دندې، او نور له منځه ځي. میدان.

په هرصورت، که تاسو غواړئ د ژباړونکي مثال غیر ضروري رامینځته کولو او حذف کولو پرته په الوتنه کې کوډ تشریح کړئ ، نو دا به معنی ولري چې دا منطق په C++ فایل کې ځای په ځای کړئ ، او له هغه ځایه په مستقیم ډول د کتابتون ټول C++ API اداره کړئ ، له چمتووالي څخه تیریږي. پوښونه جوړ کړي.

نو د هغه کوډ تولید لپاره چې تاسو ورته اړتیا لرئ

// настроить глобальные параметры (можно поменять потом)
BinaryenSetAPITracing(0);

BinaryenSetOptimizeLevel(3);
BinaryenSetShrinkLevel(2);

// создать модуль
BinaryenModuleRef MODULE = BinaryenModuleCreate();

// описать типы функций (как создаваемых, так и вызываемых)
helper_type  BinaryenAddFunctionType(MODULE, "helper-func", BinaryenTypeInt32(), int32_helper_args, ARRAY_SIZE(int32_helper_args));
// (int23_helper_args приоб^Wсоздаются отдельно)

// сконструировать супер-мега выражение
// ... ну тут уж вы как-нибудь сами :)

// потом создать функцию
BinaryenAddFunction(MODULE, "tb_fun", tb_func_type, func_locals, FUNC_LOCALS_COUNT, expr);
BinaryenAddFunctionExport(MODULE, "tb_fun", "tb_fun");
...
BinaryenSetMemory(MODULE, (1 << 15) - 1, -1, NULL, NULL, NULL, NULL, NULL, 0, 0);
BinaryenAddMemoryImport(MODULE, NULL, "env", "memory", 0);
BinaryenAddTableImport(MODULE, NULL, "env", "tb_funcs");

// запросить валидацию и оптимизацию при желании
assert (BinaryenModuleValidate(MODULE));
BinaryenModuleOptimize(MODULE);

... که ما څه هیر کړي، بخښنه، دا یوازې د پیمانه استازیتوب کوي، او توضیحات په اسنادو کې دي.

او اوس د کریک-فیکس-پیکس پیل کیږي، داسې یو څه:

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,
          // ...
      }
  );
  // и вот уже у вас есть instance!
}, buf, sz);

د دې لپاره چې په یو ډول د QEMU او JS نړۍ سره وصل کړئ او په ورته وخت کې تالیف شوي افعال ته ګړندي لاسرسی ومومئ ، یو سري رامینځته شوی (په لانچر کې د واردولو لپاره د دندو جدول) ، او تولید شوي افعال هلته ځای په ځای شوي. د شاخص په چټکۍ سره محاسبه کولو لپاره، د صفر کلمې د ژباړې بلاک شاخص په پیل کې د هغې په توګه کارول کیده، مګر بیا د دې فورمول په کارولو سره محاسبه شوې شاخص په ساده ډول په ساحه کې فټ کول پیل کړل. struct TranslationBlock.

په لاره کې، ډیمو (اوس مهال د مبهم جواز سره) یوازې په فایرفوکس کې ښه کار کوي. د کروم پراختیا کونکي وو یو څه چمتو نه دی دې حقیقت ته چې یو څوک غواړي د WebAssembly ماډلونو له زرو څخه ډیر مثالونه رامینځته کړي ، نو دوی په ساده ډول د هر یو لپاره د مجازی پتې ځای ګیګابایټ تخصیص کړی ...

د اوس لپاره هم دومره. شاید یو بل مقاله وي که څوک علاقه ولري. یعنی، لږ تر لږه پاتې دی یوازې د بلاک وسیلو کار کول. دا ممکن د WebAssembly انډولونو تالیف هم معنی ولري، لکه څنګه چې د JS نړۍ کې رواج دی، ځکه چې لاهم یو ژباړونکی شتون لري چې دا ټول کولی شي تر هغه چې اصلي ماډل چمتو نه وي.

په پای کې یوه معما: تاسو په 32-bit آرکیټیکچر کې بائنری ترتیب کړی دی، مګر کوډ، د حافظې عملیاتو له لارې، د باینریین څخه پورته کیږي، په سټیک کې، یا د 2-bit پته ځای په پورتنۍ 32 GB کې بل ځای کې. ستونزه دا ده چې د بائنریین له نظره دا خورا لوی پایلې پتې ته لاسرسی لري. د دې شاوخوا څنګه ترلاسه کول؟

د مدیر په توګه

ما دا ازموینه پای ته نه ده رسولې ، مګر زما لومړی فکر دا و چې "که ما 32-bit لینکس نصب کړی څه به وي؟" بیا د پته ځای پورتنۍ برخه به د کرنل لخوا ونیول شي. یوازینۍ پوښتنه دا ده چې څومره به ونیول شي: 1 یا 2 جی بی.

د پروګرامر په طریقه (د متخصصینو لپاره اختیار)

راځئ چې د پته ځای په پورتنۍ برخه کې یو بلبل ولګوو. زه پخپله نه پوهیږم چې ولې دا کار کوي - هلته لا د هلته باید یوه کڅوړه وي. مګر "موږ تمرین کونکي یو: هرڅه زموږ لپاره کار کوي، مګر هیڅوک نه پوهیږي چې ولې ..."

// 2gbubble.c
// Usage: LD_PRELOAD=2gbubble.so <program>

#include <sys/mman.h>
#include <assert.h>

void __attribute__((constructor)) constr(void)
{
  assert(MAP_FAILED != mmap(1u >> 31, (1u >> 31) - (1u >> 20), PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
}

... دا ریښتیا ده چې دا د والګرینډ سره مطابقت نلري ، مګر خوشبختانه ، والګرینډ پخپله خورا مؤثره هرڅوک له هغه ځایه وباسي :)

شاید یو څوک به ښه توضیح ورکړي چې زما دا کوډ څنګه کار کوي ...

سرچینه: www.habr.com

Add a comment