زه لا تر اوسه په بشپړه توګه له دې څخه خلاص شوی نه یم، مګر اوس ما پریکړه کړې چې د لږ تر لږه مقاومت لاره تعقیب نه کړم، او دا د "بلوغ په توګه" ترسره کړم، د بیلګې په توګه، زما د 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 کې راټول شوي او د ایمټرپریټر په کارولو سره تشریح کړي، نو د بلاک وسایل لاهم کار نه کوي (په راتلونکي لړۍ کې وګورئ، لکه څنګه چې دوی وايي ...). دا دی، په پای کې تاسو باید د دې مسخره پرت شوي شی په څیر یو څه ترلاسه کړئ:
نو دا څه شی دی چې د 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:
په 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);
د اوس لپاره هم دومره. شاید یو بل مقاله وي که څوک علاقه ولري. یعنی، لږ تر لږه پاتې دی یوازې د بلاک وسیلو کار کول. دا ممکن د WebAssembly انډولونو تالیف هم معنی ولري، لکه څنګه چې د JS نړۍ کې رواج دی، ځکه چې لاهم یو ژباړونکی شتون لري چې دا ټول کولی شي تر هغه چې اصلي ماډل چمتو نه وي.
په پای کې یوه معما: تاسو په 32-bit آرکیټیکچر کې بائنری ترتیب کړی دی، مګر کوډ، د حافظې عملیاتو له لارې، د باینریین څخه پورته کیږي، په سټیک کې، یا د 2-bit پته ځای په پورتنۍ 32 GB کې بل ځای کې. ستونزه دا ده چې د بائنریین له نظره دا خورا لوی پایلې پتې ته لاسرسی لري. د دې شاوخوا څنګه ترلاسه کول؟
د مدیر په توګه
ما دا ازموینه پای ته نه ده رسولې ، مګر زما لومړی فکر دا و چې "که ما 32-bit لینکس نصب کړی څه به وي؟" بیا د پته ځای پورتنۍ برخه به د کرنل لخوا ونیول شي. یوازینۍ پوښتنه دا ده چې څومره به ونیول شي: 1 یا 2 جی بی.
د پروګرامر په طریقه (د متخصصینو لپاره اختیار)
راځئ چې د پته ځای په پورتنۍ برخه کې یو بلبل ولګوو. زه پخپله نه پوهیږم چې ولې دا کار کوي - هلته لا د هلته باید یوه کڅوړه وي. مګر "موږ تمرین کونکي یو: هرڅه زموږ لپاره کار کوي، مګر هیڅوک نه پوهیږي چې ولې ..."