Qemu.js JIT جي مدد سان: توھان اڃا تائين مائنس کي پوئتي موٽائي سگھو ٿا

ڪجھ سال اڳ Fabrice Bellard jslinux پاران لکيل آهي جاوا اسڪرپٽ ۾ لکيل هڪ PC ايموليٽر آهي. ان کان پوء اتي گهٽ ۾ گهٽ هو مجازي x86. پر اهي سڀئي، جيتري قدر مون کي خبر آهي، مترجم هئا، جڏهن ته Qemu، گهڻو اڳ ساڳئي Fabrice Bellard طرفان لکيو ويو آهي، ۽ شايد، ڪنهن به خود اعتمادي وارو جديد ايموليٽر، ميزبان سسٽم ڪوڊ ۾ مهمان ڪوڊ جي JIT تاليف کي استعمال ڪري ٿو. اهو مون کي لڳي ٿو ته اهو وقت آهي سامهون واري ڪم کي لاڳو ڪرڻ جو ان سلسلي ۾ جيڪو برائوزر حل ڪري ٿو: JIT مشين ڪوڊ جو جاوا اسڪرپٽ ۾ گڏ ڪرڻ، جنهن لاءِ اهو لڳي رهيو هو بندرگاهه Qemu لاءِ تمام گهڻو منطقي. اهو لڳي ٿو، ڇو Qemu، اتي آسان ۽ صارف-دوست ايموليٽر آهن - ساڳيو VirtualBox، مثال طور - انسٽال ٿيل ۽ ڪم ڪري ٿو. پر ڪيمو ۾ ڪيترائي دلچسپ خاصيتون آهن

  • کليل ذريعو
  • ڪنيل ڊرائيور کان سواءِ ڪم ڪرڻ جي صلاحيت
  • مترجم موڊ ۾ ڪم ڪرڻ جي صلاحيت
  • ميزبان ۽ مهمان فن تعمير جي وڏي تعداد لاءِ سپورٽ

ٽئين نقطي جي حوالي سان، مان هاڻي وضاحت ڪري سگهان ٿو ته حقيقت ۾، TCI موڊ ۾، اهو نه آهي ته مهمان مشين هدايتون پاڻ کي تفسير ڪيون ويون آهن، پر انهن مان حاصل ڪيل بائيٽ ڪوڊ، پر اهو جوهر تبديل نٿو ڪري - ترتيب ڏيڻ ۽ هلائڻ لاء. Qemu هڪ نئين اڏاوت تي، جيڪڏهن توهان خوش قسمت آهيو، A C compiler ڪافي آهي - هڪ ڪوڊ جنريٽر لکڻ کي ملتوي ڪري سگهجي ٿو.

۽ هاڻي، ٻن سالن کان پوء آرام سان منهنجي مفت وقت ۾ قيمو سورس ڪوڊ سان ٽڪننگ ڪرڻ کان پوء، هڪ ڪم ڪندڙ پروٽوٽائپ ظاهر ٿيو، جنهن ۾ توهان اڳ ۾ ئي هلائي سگهو ٿا، مثال طور، ڪوليبري او ايس.

Emscripten ڇا آهي؟

اڄڪلهه، ڪيترائي مرتب ڪندڙ ظاهر ٿيا آهن، جن جو آخري نتيجو JavaScript آهي. ڪجهه، ٽائپ اسڪرپٽ وانگر، اصل ۾ ويب لاءِ لکڻ جو بهترين طريقو هو. ساڳئي وقت، Emscripten هڪ طريقو آهي موجوده C يا C++ ڪوڊ وٺڻ ۽ ان کي گڏ ڪرڻ جو هڪ برائوزر-پڙهڻ لائق فارم ۾. تي هي صفحو اسان ڪيترن ئي بندرگاهن کي گڏ ڪيو آهي معروف پروگرام: هتيمثال طور، توهان ڏسي سگهو ٿا PyPy - رستي ۾، اهي دعوي ڪندا آهن ته اڳ ۾ ئي JIT آهي. حقيقت ۾، هر پروگرام کي آسان طور تي مرتب نه ڪري سگھجي ٿو ۽ برائوزر ۾ هلائي سگھجي ٿو - اتي ھڪڙو نمبر آھن خاصيتون، جنهن کي توهان سان گڏ رکڻو آهي، جڏهن ته، ساڳئي صفحي تي لکيل لکيل آهي "Emscripten تقريبا ڪنهن به ترتيب ڏيڻ لاء استعمال ڪري سگهجي ٿو. ايبل C/C++ ڪوڊ جاوا اسڪرپٽ ڏانهن." اهو آهي، اهڙا ڪيترائي عمل آهن جيڪي معيار جي مطابق اڻ ڄاڻايل رويي آهن، پر عام طور تي x86 تي ڪم ڪن ٿا - مثال طور، متغيرن تائين غير ترتيب واري رسائي، جيڪا عام طور تي ڪجهه آرڪيٽيڪچر تي منع ٿيل آهي. , Qemu هڪ ڪراس پليٽ فارم پروگرام آهي ۽، مان مڃڻ چاهيان ٿو، ۽ ان ۾ اڳ ۾ ئي گهڻيون غير تعريف ٿيل رويا شامل نه آهن - ان کي وٺو ۽ گڏ ڪريو، پوءِ JIT سان ٿورو ٽِڪر ڪريو - ۽ توهان ڪيو! پر اهو ناهي ڪيس...

پهرين ڪوشش

عام طور تي ڳالهائڻ، مان پهريون شخص نه آهيان جيڪو Qemu کي جاوا اسڪرپٽ ڏانهن پورٽ ڪرڻ جي خيال سان آيو آهيان. اتي هڪ سوال پڇيو ويو ReactOS فورم تي جيڪڏهن اهو ممڪن هو Emscripten استعمال ڪندي. جيتوڻيڪ اڳ ۾، افواهون هيون ته Fabrice Bellard اهو ذاتي طور تي ڪيو، پر اسان jslinux بابت ڳالهائي رهيا هئاسين، جيڪو، جيترو مون کي ڄاڻ آهي، صرف هڪ ڪوشش آهي دستي طور تي JS ۾ ڪافي ڪارڪردگي حاصل ڪرڻ جي، ۽ شروع کان لکيو ويو هو. بعد ۾، ورچوئل x86 لکيو ويو - ان لاءِ اڻ ڄاتل ذريعا پوسٽ ڪيا ويا، ۽، جيئن چيو ويو آهي، ايموليشن جي وڏي ”حقيقت پسندي“ ان کي ممڪن بڻائي ڇڏيو آهي SeaBIOS کي فرم ویئر طور استعمال ڪرڻ. ان کان سواء، اتي گهٽ ۾ گهٽ هڪ ڪوشش هئي بندرگاهه قمو ايم اسڪرپٽ استعمال ڪندي - مون هن کي ڪرڻ جي ڪوشش ڪئي ساکٽ جوڙوپر ترقي، جيتري قدر مان سمجهان ٿو، منجمد هئي.

تنهن ڪري، اهو لڳي ٿو، هتي ذريعن آهن، هتي آهي Emscripten - ان کي وٺو ۽ مرتب ڪريو. پر اهڙيون لائبريريون به آهن جن تي قيمو منحصر آهي، ۽ لائبريريون جن تي اهي لائبريريون منحصر آهن، وغيره، ۽ انهن مان هڪ آهي. libffi، جنهن تي دارومدار رکي ٿو. انٽرنيٽ تي افواهون هيون ته ايم اسڪرپٽن لاءِ لائبريرين جي بندرگاهن جي وڏي مجموعي ۾ هڪ آهي، پر ان تي يقين ڪرڻ ڪنهن حد تائين مشڪل هو: پهرين، اهو هڪ نئون مرتب ڪرڻ جو ارادو نه هو، ٻيو، اهو تمام گهٽ سطح وارو هو. لئبرري کي کڻڻ لاءِ، ۽ گڏ ڪرڻ لاءِ JS. ۽ اهو صرف اسمبلي داخل ڪرڻ جو معاملو ناهي - شايد، جيڪڏهن توهان ان کي موڙيندا آهيو، ڪجهه ڪالنگ ڪنوينشن لاءِ توهان اسٽيڪ تي ضروري دليل ٺاهي سگهو ٿا ۽ انهن کان سواءِ فنڪشن کي ڪال ڪري سگهو ٿا. پر Emscripten هڪ مشڪل شيءِ آهي: ٺاهيل ڪوڊ ڏسڻ لاءِ براؤزر JS انجڻ آپٽمائزر کان واقف ٿي، ڪجهه ترڪيبون استعمال ڪيون وينديون آهن. خاص طور تي، نام نهاد ريلوپنگ - حاصل ڪيل LLVM IR استعمال ڪندي هڪ ڪوڊ جنريٽر ڪجهه تجريدي منتقلي جي هدايتن سان گڏ ممڪن ifs، loops، وغيره کي ٻيهر ٺاهڻ جي ڪوشش ڪري ٿو. خير، ڪئين دليلن کي فنڪشن ڏانهن منتقل ڪيو ويو آهي؟ قدرتي طور تي، جي ايس افعال جي دليلن جي طور تي، اهو آهي، جيڪڏهن ممڪن هجي، اسٽيڪ ذريعي نه.

شروعات ۾ هڪ خيال هو ته صرف JS سان libffi لاءِ متبادل لکان ۽ معياري ٽيسٽ هلايان، پر آخر ۾ مون کي ان ڳالهه ۾ الجھن پئجي وئي ته ڪيئن پنهنجو هيڊر فائلون ٺاهيون ته جيئن اهي موجوده ڪوڊ سان ڪم ڪن - مان ڇا ڪري سگهان ٿو، جيئن اهي چون ٿا، "ڇا ڪم تمام پيچيده آهن "ڇا اسان ايترا بيوقوف آهيون؟" مون کي libffi کي ٻئي آرڪيٽيڪچر ڏانهن پورٽ ڪرڻو پيو، سو ڳالهائڻ لاءِ - خوشقسمتيءَ سان، Emscripten وٽ ان لائن اسيمبليءَ لاءِ ٻئي ميڪرو آهن (جاوا اسڪرپٽ ۾، ها - چڱو، جيڪو به هجي آرڪيٽيڪچر، پوءِ گڏ ڪرڻ وارو)، ۽ فلائي تي ٺاهيل ڪوڊ هلائڻ جي صلاحيت. عام طور تي، ڪجهه وقت لاءِ پليٽ فارم تي منحصر لبفي ٽڪرن سان ٽڪرائڻ کان پوءِ، مون کي ڪجهه مرتب ڪرڻ وارو ڪوڊ مليو ۽ ان کي پهرين ٽيسٽ تي هليو ويو، جنهن ۾ آئون آيو آهيان. منهنجي تعجب ۾، امتحان ڪامياب ٿي ويو. منهنجي ذهانت کان دنگ رهجي ويو - ڪو به مذاق نه، اهو پهرين لانچ کان ڪم ڪيو - مون، اڃا تائين منهنجي اکين تي اعتبار نه ڪيو، نتيجو ڪوڊ کي ٻيهر ڏسڻ لاء، جائزو وٺڻ لاء اڳتي وڌڻ لاء ڪٿي. هتي مان ٻيو ڀيرو بيوقوف ٿي ويس - صرف اهو ڪم ڪيو ويو جيڪو منهنجو ڪم هو ffi_call - هن هڪ ڪامياب ڪال ٻڌايو. پاڻ ڪو سڏ نه هو. تنهن ڪري مون پنهنجي پهرين پل جي درخواست موڪلي، جنهن ٽيسٽ ۾ هڪ غلطي کي درست ڪيو جيڪو اولمپياڊ جي ڪنهن به شاگرد لاءِ واضح آهي - حقيقي انگن جو مقابلو نه ڪيو وڃي جيئن a == b ۽ جيتوڻيڪ ڪيئن a - b < EPS - توھان کي پڻ ماڊل ياد رکڻ جي ضرورت آھي، ٻي صورت ۾ 0 تمام گھڻو ٿي ويندو 1/3 جي برابر... عام طور تي، مون کي libffi جي ھڪڙي خاص بندرگاھ سان گڏ آيو آھي، جيڪو آسان ترين ٽيسٽ پاس ڪري ٿو، ۽ جنھن سان glib آھي. مرتب ٿيل - مون فيصلو ڪيو ته اهو ضروري هوندو، مان ان کي بعد ۾ شامل ڪندس. اڳتي ڏسندي، مان چوندس ته، جيئن ته اهو نڪتو، مرتب ڪندڙ به شامل نه ڪيو آهي libffi فنڪشن کي حتمي ڪوڊ ۾.

پر، جيئن مون اڳ ۾ ئي چيو آهي، اتي ڪجهه حدون آهن، ۽ مختلف اڻ ڄاڻايل رويي جي آزاد استعمال جي وچ ۾، هڪ وڌيڪ ناپسنديده خصوصيت لڪايو ويو آهي - ڊيزائن طرفان جاوا اسڪرپٽ گڏيل ميموري سان ملٽي ٿريڊنگ کي سپورٽ نٿو ڪري. اصول ۾، اهو عام طور تي هڪ سٺو خيال پڻ سڏيو وڃي ٿو، پر پورٽنگ ڪوڊ لاء نه، جن جي فن تعمير سي ٿريڊز سان ڳنڍيل آهي. عام طور تي ڳالهائڻ، فائر فاڪس تجربو ڪري رهيو آهي حصيداري ڪارڪنن جي حمايت سان، ۽ ايم اسڪرپٽ انهن لاء هڪ پيٿريڊ عمل درآمد ڪيو آهي، پر مان ان تي انحصار ڪرڻ نه چاهيندس. مون کي قيمو ڪوڊ مان ملٽي ٿريڊنگ کي آهستي آهستي روٽ آئوٽ ڪرڻو هو- يعني اهو معلوم ڪريو ته ٿريڊ ڪٿي هلي رهيا آهن، هن ٿريڊ ۾ هلندڙ لوپ جي باڊي کي هڪ الڳ فنڪشن ۾ منتقل ڪريو، ۽ اهڙن ڪمن کي هڪ هڪ ڪري مين لوپ مان ڪال ڪريو.

ٻئي ڪوشش

ڪجهه نقطي تي، اهو واضح ٿي ويو ته مسئلو اڃا تائين موجود آهي، ۽ ڪوڊ جي چوڌاري بيچيني طور تي ڇڪڻ واري ڪيچ ڪنهن به سٺي جي اڳواڻي نه ڪندي. نتيجو: اسان کي ڪنهن نه ڪنهن طريقي سان ڪرچ شامل ڪرڻ جي عمل کي منظم ڪرڻ جي ضرورت آهي. تنهن ڪري، نسخو 2.4.1، جيڪو ان وقت تازو هو، ورتو ويو (نه 2.5.0، ڇاڪاڻ ته، ڪير ڄاڻي ٿو، نئين نسخي ۾ ڪي ڪيڙا هوندا جيڪي اڃا تائين نه پڪڙيا ويا آهن، ۽ مون وٽ ڪافي ڪافي آهن. )، ۽ پهرين شيء ان کي محفوظ طور تي ٻيهر لکڻو هو thread-posix.c. خير، اھو آھي، محفوظ طور تي: جيڪڏھن ڪو ماڻھو ھڪڙي آپريشن ڪرڻ جي ڪوشش ڪئي جيڪا بلاڪ ڪرڻ جي ڪري، فنڪشن کي فوري طور تي سڏيو ويو abort() - يقينا، اهو سڀ مسئلا هڪ ڀيرو حل نه ڪيو، پر گهٽ ۾ گهٽ اهو ڪجهه وڌيڪ خوشگوار هو خاموشيء سان غير مطابقت واري ڊيٽا حاصل ڪرڻ کان.

عام طور تي، ايم اسڪرپٽ جا اختيار تمام مددگار هوندا آهن ڪوڊ پورٽ ڪرڻ ۾ JS -s ASSERTIONS=1 -s SAFE_HEAP=1 - اهي ڪجهه قسم جي اڻ ٺهندڙ رويي کي پڪڙيندا آهن، جهڙوڪ اڻ ترتيب ڏنل پتي تي ڪالون (جيڪو ٽائپ ڪيل صفن جي ڪوڊ سان بلڪل مطابقت نه آهي جهڙوڪ HEAP32[addr >> 2] = 1) يا هڪ فنڪشن کي ڪال ڪرڻ سان غلط نمبر دليلن سان.

رستي جي ذريعي، ترتيب جي غلطي هڪ الڳ مسئلو آهي. جيئن مون اڳ ۾ ئي چيو آهي، Qemu وٽ ڪوڊ جنريشن TCI (ننڍو ڪوڊ مترجم) لاءِ ”تجارتي“ تشريح وارو پس منظر آهي، ۽ قيمو کي نئين اڏاوت تي تعمير ۽ هلائڻ لاءِ، جيڪڏهن توهان خوش قسمت آهيو، ته هڪ C مرتب ڪندڙ ڪافي آهي. Keywords "جيڪڏهن توهان خوش قسمت آهيو". مون کي بدقسمتي هئي، ۽ اهو ظاهر ٿيو ته TCI ان جي بائيٽ ڪوڊ کي پارس ڪرڻ وقت غير ترتيب واري رسائي استعمال ڪندو آهي. اهو آهي، سڀني قسمن جي ARM ۽ ٻين آرڪيٽيڪچرن تي لازمي طور تي رسائي سان گڏ، Qemu گڏ ڪري ٿو ڇاڪاڻ ته انهن وٽ هڪ عام TCG پس منظر آهي جيڪو اصلي ڪوڊ ٺاهي ٿو، پر ڇا TCI انهن تي ڪم ڪندو هڪ ٻيو سوال آهي. بهرحال، جيئن اهو نڪتو، TCI دستاويز واضح طور تي ڪجهه ظاهر ڪيو. نتيجي طور، غير ترتيب ڏنل پڙهڻ لاءِ فنڪشن ڪالز ڪوڊ ۾ شامل ڪيا ويا، جيڪي قيمو جي ٻئي حصي ۾ دريافت ڪيا ويا.

ڍڳي جي تباهي

نتيجي طور، TCI تائين اڻڄاتل رسائي کي درست ڪيو ويو، ھڪڙو مکيه لوپ ٺاھيو ويو جنھن کي موڙ ۾ پروسيسر، RCU ۽ ڪجھ ٻيون ننڍيون شيون سڏيو ويندو آھي. ۽ پوءِ مان آپشن سان Qemu لانچ ڪيو -d exec,in_asm,out_asm، جنهن جو مطلب آهي ته توهان کي اهو چوڻ جي ضرورت آهي ته ڪوڊ جا ڪهڙا بلاڪ جاري ڪيا پيا وڃن، ۽ پڻ نشر ڪرڻ جي وقت لکڻ لاءِ ته مهمان ڪوڊ ڇا هو، ڪهڙو ميزبان ڪوڊ بڻجي ويو (هن صورت ۾، بائيٽ ڪوڊ). اهو شروع ٿئي ٿو، ڪيترن ئي ترجمي جي بلاڪن تي عمل ڪري ٿو، ڊيبگنگ پيغام لکي ٿو جيڪو مون ڇڏي ڏنو ته RCU هاڻي شروع ٿيندو ۽... حادثا abort() هڪ فنڪشن اندر free(). فنڪشن سان ٽڪرائڻ سان free() اسان اهو معلوم ڪرڻ ۾ ڪامياب ٿي ويا آهيون ته هيڊ بلاڪ جي هيڊر ۾، جيڪو مختص ڪيل ياداشت کان اڳ اٺ بائيٽس ۾ هوندو آهي، بلاڪ جي سائيز جي بدران يا اهڙي شيءِ، اتي گندگي هئي.

هيپ جي تباهي - ڪيترو پيارو آهي ... اهڙي صورت ۾، هڪ مفيد علاج آهي - (جيڪڏهن ممڪن هجي) ساڳئي ذريعن کان، هڪ مقامي بائنري کي گڏ ڪريو ۽ ان کي Valgrind جي تحت هلايو. ڪجهه وقت کان پوء، بائنري تيار ٿي ويو. مان ان کي ساڳين اختيارن سان لانچ ڪريان ٿو - اھو حادثو ٿي سگھي ٿو شروعات دوران به، اصل ۾ پھچڻ کان اڳ. اها اڻ وڻندڙ ​​​​آهي، يقينا - ظاهري طور تي، ذريعا بلڪل ساڳيا نه هئا، جيڪا تعجب جي ڳالهه ناهي، ڇاڪاڻ ته ترتيب ڏيڻ ۾ ڪجهه مختلف اختيارن کي نظر انداز ڪيو ويو آهي، پر مون وٽ Valgrind آهي - پهرين آئون هن بگ کي درست ڪندس، ۽ پوء، جيڪڏهن مان خوش قسمت آهيان. ، اصل ظاهر ٿيندو. مان ساڳيو ڪم هلائي رهيو آهيان Valgrind جي تحت... Y-y-y، y-y-y، uh-uh، اهو شروع ٿيو، عام طور تي شروعات کان گذريو ۽ غلط ياداشت جي رسائي بابت هڪ ڊيڄاريندڙ بغير اصل بگ کي ماضي تي منتقل ڪيو ويو، فالس بابت ذڪر نه ڪرڻ. زندگي، جيئن اهي چون ٿا، مون کي ان لاءِ تيار نه ڪيو - هڪ حادثو ٿيڻ وارو پروگرام جڏهن والگرينڊ جي تحت شروع ڪيو ويو ته حادثو ٿيڻ بند ٿي ويو. اهو ڇا هو هڪ راز آهي. منهنجو مفروضو اهو آهي ته هڪ دفعي موجوده هدايتن جي ڀرپاسي ۾ هڪ حادثي کان پوء شروعاتي دوران، جي ڊي بي ڪم ڏيکاريو memset-a استعمال ڪندي صحيح پوائنٽر سان mmx، يا xmm registers، پوءِ شايد اهو ڪنهن قسم جي ترتيب جي غلطي هئي، جيتوڻيڪ اهو اڃا تائين يقين ڪرڻ ڏکيو آهي.

ٺيڪ، Valgrind هتي مدد ڪرڻ نٿو لڳي. ۽ هتي سڀ کان وڌيڪ ناپسنديده شيء شروع ٿي - هر شيء شروع ٿيڻ لڳي، پر بلڪل اڻڄاتل سببن جي ڪري حادثي جي ڪري هڪ واقعي جي ڪري ٿي سگهي ٿي جيڪا لکين هدايتن کان اڳ ٿي سگهي ٿي. گهڻي وقت تائين اهو به سمجهه ۾ نه آيو ته ڪيئن پهچجي. آخر ۾، مون کي اڃا تائين ويهڻ ۽ ڊيبگ ڪرڻو پيو. ڇپائي ڇاپيو ويو جنهن سان هيڊر ٻيهر لکيو ويو اهو ظاهر ٿيو ته اهو هڪ نمبر وانگر نظر نٿو اچي، بلڪه ڪجهه قسم جي بائنري ڊيٽا. ۽، ڏسو ۽ ڏسو، هي بائنري اسٽرنگ BIOS فائل ۾ مليو هو - اهو آهي، هاڻي اهو ممڪن آهي ته مناسب اعتماد سان اهو چوڻ آهي ته اهو هڪ بفر اوور فلو هو، ۽ اهو پڻ واضح آهي ته اهو هن بفر ڏانهن لکيو ويو آهي. خير، پوءِ ڪجهه هن طرح - Emscripten ۾، خوشقسمتيءَ سان، ايڊريس جي جاءِ جي ڪا به ترتيب نه آهي، نه ته ان ۾ ڪي سوراخ آهن، تنهنڪري توهان ڪوڊ جي وچ ۾ ڪٿي به لکي سگهو ٿا ته آخري لانچ کان پوائنٽر ذريعي ڊيٽا ڪڍڻ لاءِ، ڊيٽا کي ڏسو، پوائنٽر کي ڏسو، ۽، جيڪڏھن اھو تبديل نه ٿيو آھي، سوچڻ لاء کاڌو حاصل ڪريو. سچ، ڪنهن به تبديلي کان پوء ڳنڍڻ لاء ڪجهه منٽ لڳن ٿا، پر توهان ڇا ڪري سگهو ٿا؟ نتيجي طور، هڪ مخصوص لائن ملي وئي جيڪا BIOS کي عارضي بفر کان مهمان ياداشت ڏانهن نقل ڪيو ويو - ۽، حقيقت ۾، بفر ۾ ڪافي جاء نه هئي. انهي عجيب بفر ايڊريس جو ذريعو ڳولڻ جي نتيجي ۾ هڪ فنڪشن qemu_anon_ram_alloc فائل ۾ oslib-posix.c - اتي منطق هي هئي: ڪڏهن ڪڏهن اهو ڪارائتو ٿي سگهي ٿو پتي کي ترتيب ڏيڻ لاءِ 2 MB سائيز جي وڏي صفحي تي، ان لاءِ اسان پڇنداسين mmap پهرين ٿورو وڌيڪ، ۽ پوء اسان مدد سان اضافي واپس ڪنداسين munmap. ۽ جيڪڏهن اهڙي ترتيب گهربل نه آهي، ته پوء اسان 2 MB جي بدران نتيجو ظاهر ڪنداسين getpagesize() - mmap اهو اڃا تائين هڪ ترتيب ڏنل ايڊريس ڏيندو... تنهنڪري Emscripten ۾ mmap صرف سڏي ٿو malloc، پر يقينن اهو صفحو تي ترتيب نٿو ڏئي. عام طور تي، هڪ بگ جيڪو مون کي ڪجهه مهينن لاء مايوس ڪيو هو ان ۾ تبديلي سان درست ڪيو ويو двух لائينون.

ڪالنگ فنڪشن جون خاصيتون

۽ ھاڻي پروسيسر ڪجھ ڳڻيو آھي، قيمو حادثو نٿو ٿئي، پر اسڪرين آن نٿي ٿئي، ۽ پروسيسر جلدي لوپ ۾ وڃي ٿو، ٻاھرين جي حساب سان. -d exec,in_asm,out_asm. هڪ مفروضو سامهون آيو آهي: ٽائمر مداخلت (يا، عام طور تي، سڀ مداخلت) نه ايندا آهن. ۽ درحقيقت، جيڪڏهن توهان مقامي اسيمبليء کان مداخلت کي ختم ڪيو، جيڪو ڪجهه سببن لاء ڪم ڪيو، توهان هڪ ساڳئي تصوير حاصل ڪندا. پر اهو سڀ ڪجهه جواب نه هو: مٿي ڏنل آپشن سان جاري ڪيل نشانن جو مقابلو ڏيکاريو ويو آهي ته عملدرآمد جي پيچيدگي تمام جلدي ٿي وئي. هتي اهو چوڻ گهرجي ته لانچر استعمال ڪندي رڪارڊ ڪيل شين جي مقابلي ۾ emrun ڏيهي اسيمبلي جي آئوٽ پٽ سان ڊيبگنگ آئوٽ مڪمل طور تي مشيني عمل نه آهي. مون کي خبر ناهي ته برائوزر ۾ هلندڙ پروگرام ڪيئن ڳنڍجي ٿو emrun، پر آئوٽ پُٽ ۾ ڪي سٽون وري ترتيب ڏيڻ لاءِ نڪرنديون آهن، تنهنڪري تفاوت ۾ فرق اڃا تائين اهو فرض ڪرڻ جو ڪو سبب نه آهي ته پيچرو مختلف ٿي ويا آهن. عام طور تي، اهو واضح ٿيو ته هدايتن جي مطابق ljmpl اتي مختلف پتي ڏانهن منتقلي آهي، ۽ ٺاهيل بائيٽ ڪوڊ بنيادي طور تي مختلف آهي: هڪ ۾ مددگار فنڪشن کي سڏڻ جي هدايت شامل آهي، ٻيو نه. هدايتن کي گوگل ڪرڻ ۽ ڪوڊ جو مطالعو ڪرڻ کان پوءِ جيڪو انهن هدايتن جو ترجمو ڪري ٿو، اهو واضح ٿي ويو ته، پهرين، فوري طور تي رجسٽر ۾ cr0 هڪ رڪارڊنگ ڪئي وئي - پڻ هڪ مددگار استعمال ڪندي - جنهن پروسيسر کي محفوظ موڊ ۾ تبديل ڪيو، ۽ ٻيو، ته js ورزن ڪڏهن به محفوظ موڊ ۾ تبديل نه ڪيو. پر حقيقت اها آهي ته ايم اسڪرپٽن جي هڪ ٻي خصوصيت اها آهي ته ان جي ڪوڊ کي برداشت ڪرڻ کان انڪار، جيئن ته هدايتن تي عمل ڪرڻ. call TCI ۾، جيڪو ڪنهن به فنڪشن پوائنٽر جي نتيجي ۾ قسم ۾ long long f(int arg0, .. int arg9) - فعل لازمي طور تي دليلن جي صحيح تعداد سان سڏيو وڃي ٿو. جيڪڏهن هن قاعدي جي ڀڃڪڙي ڪئي وئي آهي، ڊيبگنگ سيٽنگن تي منحصر ڪري، پروگرام يا ته حادثو ٿيندو (جيڪو سٺو آهي) يا غلط فنڪشن کي سڏيندو (جيڪو ڊيبگ ڪرڻ لاء افسوس ٿيندو). اتي پڻ ٽيون اختيار آھي - لفافين جي نسل کي فعال ڪريو جيڪي دليلن کي شامل / ختم ڪن ٿا، پر مجموعي طور تي اھي لفافي تمام گھڻي جاء وٺن ٿا، ان حقيقت جي باوجود ته حقيقت ۾ مون کي صرف سؤ لفافن کان ٿورو وڌيڪ گھرجي. اهو اڪيلو تمام افسوسناڪ آهي، پر اتي هڪ وڌيڪ سنگين مسئلو ٿي ويو آهي: ريپر جي ٺاهيل ڪوڊ ۾، دليلن کي تبديل ڪيو ويو ۽ تبديل ڪيو ويو، پر ڪڏهن ڪڏهن پيدا ٿيل دليلن سان فنڪشن کي نه سڏيو ويندو هو - سٺو، جيئن ته منهنجي libffi عمل درآمد. اهو آهي، ڪجهه مددگارن کي صرف قتل نه ڪيو ويو.

خوشقسمتيءَ سان، Qemu وٽ ھيڊر فائل جي صورت ۾ مددگارن جي مشين پڙھڻ لائق لسٽون آھن جھڙوڪ

DEF_HELPER_0(lock, void)
DEF_HELPER_0(unlock, void)
DEF_HELPER_3(write_eflags, void, env, tl, i32)

اهي ڪافي مضحکہ خیز استعمال ڪيا ويا آهن: پهريون، ميڪرو کي سڀ کان وڌيڪ عجيب انداز ۾ بيان ڪيو ويو آهي DEF_HELPER_n، ۽ پوءِ چالو ٿئي ٿو helper.h. انهي حد تائين ته ميڪرو کي وڌايو ويو آهي هڪ ساخت جي شروعات ڪندڙ ۽ ڪاما ۾، ۽ پوء هڪ صف بيان ڪئي وئي آهي، ۽ عناصر جي بدران - #include <helper.h> نتيجي طور، مون کي آخرڪار ڪم تي لائبريري جي ڪوشش ڪرڻ جو موقعو مليو pyparsing، ۽ هڪ اسڪرپٽ لکيو ويو آهي جيڪو ٺاهي ٿو بلڪل انهن لفافن کي بلڪل انهن ڪمن لاءِ جن لاءِ انهن جي ضرورت آهي.

۽ ائين، ان کان پوء پروسيسر ڪم ڪرڻ لڳي. اهو لڳي ٿو ڇاڪاڻ ته اسڪرين کي ڪڏهن به شروع نه ڪيو ويو آهي، جيتوڻيڪ memtest86+ مقامي اسيمبلي ۾ هلائڻ جي قابل هئي. هتي اهو واضح ڪرڻ ضروري آهي ته Qemu بلاڪ I/O ڪوڊ ڪوروٽين ۾ لکيل آهي. ايم اسڪرپٽن جو پنهنجو تمام مشڪل عمل آهي، پر ان کي اڃا به سپورٽ ڪرڻ جي ضرورت آهي Qemu ڪوڊ ۾، ۽ توهان هاڻي پروسيسر کي ڊيبگ ڪري سگهو ٿا: Qemu اختيارن کي سپورٽ ڪري ٿو -kernel, -initrd, -append، جنهن سان توهان لينڪس بوٽ ڪري سگهو ٿا يا، مثال طور، memtest86+، بغير ڪنهن بلاڪ ڊوائيسز استعمال ڪرڻ جي. پر هتي مسئلو آهي: مقامي اسيمبليءَ ۾ ڪو به ڏسي سگهي ٿو لينڪس ڪنسل آئوٽ پٽ کي ڪنسول ڏانهن اختيار سان -nographic، ۽ برائوزر کان ٽرمينل تائين ڪوبه ٻاھر نه نڪتو جتان اھو لانچ ڪيو ويو emrun، نه آيو. اهو آهي، اهو واضح ناهي: پروسيسر ڪم نه ڪري رهيو آهي يا گرافڪس آئوٽ ڪم نه ڪري رهيو آهي. ۽ پوءِ مون کي خيال آيو ته ٿورو انتظار ڪريان. اهو ظاهر ٿيو ته "پروسيسر سمهڻ وارو نه آهي، پر صرف آهستي آهستي چمڪي رهيو آهي،" ۽ اٽڪل پنجن منٽن کان پوء ڪنييل پيغامن جو هڪ گروپ ڪنسول تي اڇلائي ڇڏيو ۽ ٽنگڻ جاري رکي. اهو واضح ٿيو ته پروسيسر، عام طور تي، ڪم ڪري ٿو، ۽ اسان کي SDL2 سان ڪم ڪرڻ لاء ڪوڊ ۾ کڙو ڪرڻو پوندو. بدقسمتي سان، مون کي خبر ناهي ته هي لائبريري ڪيئن استعمال ڪجي، تنهنڪري ڪجهه هنڌن تي مون کي بي ترتيب تي ڪم ڪرڻو پيو. ڪجهه نقطي تي، لائن parallel0 هڪ نيري پس منظر تي اسڪرين تي چمڪيو، جنهن ۾ ڪجهه سوچون تجويز ڪيون ويون. آخر ۾، اهو ظاهر ٿيو ته مسئلو اهو هو ته Qemu هڪ فزيڪل ونڊو ۾ ڪيترن ئي ورچوئل ونڊوز کوليندو آهي، جن جي وچ ۾ توهان Ctrl-Alt-n استعمال ڪندي سوئچ ڪري سگهو ٿا: اهو ڪم ڪري ٿو اصلي تعمير ۾، پر ايم اسڪرپٽ ۾ نه. اختيارن کي استعمال ڪندي غير ضروري ونڊوز کان نجات حاصل ڪرڻ کان پوء -monitor none -parallel none -serial none ۽ هر فريم تي پوري اسڪرين کي زبردستي ٻيهر ٺاهڻ لاءِ هدايتون، هر شيءِ اوچتو ڪم ٿي وئي.

ڪوروٽين

تنهن ڪري، برائوزر ۾ ايموليشن ڪم ڪري ٿو، پر توهان ان ۾ ڪا به دلچسپ سنگل فلاپي نه هلائي سگهو ٿا، ڇاڪاڻ ته ڪو به بلاڪ I/O ناهي - توهان کي ڪوروٽين لاءِ سپورٽ لاڳو ڪرڻ جي ضرورت آهي. Qemu وٽ اڳ ۾ ئي ڪيترائي ڪوروٽين پس منظر آهن، پر جاوا اسڪرپٽ ۽ ايم اسڪرپٽ ڪوڊ جنريٽر جي فطرت جي ڪري، توهان صرف اسٽيڪ شروع نه ڪري سگهو ٿا. اهو لڳي ٿو ته "هر شيء ختم ٿي وئي آهي، پلستر کي هٽايو پيو وڃي،" پر Emscripten ڊولپرز اڳ ۾ ئي هر شيء جو خيال رکيو آهي. اهو لاڳو ڪيو ويو آهي ڪافي مضحکہ خیز: اچو ته هن مشڪوڪ وانگر فنڪشن ڪال کي سڏين emscripten_sleep ۽ ٻيا ڪيترائي Asyncify ميڪانيزم استعمال ڪندي، انهي سان گڏ پوائنٽر ڪالز ۽ ڪالز ڪنهن به فنڪشن کي جتي پوئين ٻن ڪيسن مان هڪ ٿي سگهي ٿي اسٽيڪ جي هيٺان. ۽ ھاڻي، ھر مشڪوڪ ڪال کان اڳ، اسين ھڪ async context چونڊينداسين، ۽ ڪال کان پوءِ فوري طور تي، اسان چيڪ ڪنداسين ته ڇا ھڪ اسينڪرونس ڪال آئي آھي، ۽ جيڪڏھن اھو آھي، اسان سڀني مقامي متغيرن کي محفوظ ڪنداسين ھن async حوالي سان، ظاھر ڪريون ته ڪھڙي فنڪشن. ڪنٽرول کي منتقل ڪرڻ لاءِ جڏهن اسان کي عمل جاري رکڻ جي ضرورت آهي، ۽ موجوده فنڪشن مان نڪرڻ. اهو آهي جتي اثر جي مطالعي جي گنجائش آهي فضول خرچي - هڪ غير مطابقت واري ڪال مان موٽڻ کان پوءِ جاري رکڻ واري ڪوڊ جي عمل جي ضرورتن لاءِ، ڪمپلر مشڪوڪ ڪال کان پوءِ شروع ٿيندڙ فنڪشن جا ”اسٽبس“ ٺاهي ٿو - جيئن ته: جيڪڏهن n مشڪوڪ ڪالون آهن، ته پوءِ فنڪشن کي وڌايو ويندو n/2. ڀيرا - اهو اڃا تائين آهي، جيڪڏهن نه ذهن ۾ رکو ته هر ممڪن طور تي غير مطابقت واري ڪال کان پوء، توهان کي اصل فنڪشن ۾ ڪجهه مقامي متغيرن کي بچائڻ جي ضرورت آهي. ان کان پوء، مون کي پٿون ۾ هڪ سادي اسڪرپٽ به لکڻو پيو، جيڪو خاص طور تي استعمال ٿيل افعال جي هڪ ڏنل سيٽ تي ٻڌل آهي، جيڪو سمجهي ٿو ته "غير مطابقت پذير ٿيڻ جي اجازت نه ڏيو" (يعني، اسٽيڪ پروموشن ۽ هر شيء جيڪا مون بيان ڪيو آهي. ڪم انهن ۾)، اشارو ڪري ٿو ڪالن ذريعي پوائنٽرز جنهن ۾ ڪمپلر طرفان ڪم کي نظرانداز ڪيو وڃي ته جيئن اهي فنڪشن غير مطابقت نه سمجهيا وڃن. ۽ پوءِ 60 MB کان هيٺ JS فائلون واضح طور تي تمام گهڻيون آهن - اچو ته چئون ته گهٽ ۾ گهٽ 30. جيتوڻيڪ، هڪ ڀيرو آئون هڪ اسمبلي اسڪرپٽ سيٽ ڪري رهيو هوس، ۽ اتفاقي طور تي لنڪر جا آپشن ڪڍي ڇڏيا، جن مان -O3. مان ٺاهيل ڪوڊ هلائيان ٿو، ۽ ڪروميم ياداشت کي کائي ٿو ۽ حادثا. مون پوءِ اتفاقي طور تي ڏٺو ته هو ڇا ڊائون لوڊ ڪرڻ جي ڪوشش ڪري رهيو هو... خير، مان ڇا ٿو چوان، جيڪڏهن مون کي 500+ MB جاوا اسڪرپٽ کي سوچي سمجهي پڙهڻ ۽ بهتر ڪرڻ لاءِ چيو وڃي ها ته مان به منجمد ٿي وڃان ها.

بدقسمتي سان، Asyncify سپورٽ لائبريري ڪوڊ ۾ چيڪ مڪمل طور تي دوستانه نه هئا longjmp-s جيڪي ورچوئل پروسيسر ڪوڊ ۾ استعمال ڪيا ويا آهن، پر هڪ ننڍڙي پيچ کان پوء جيڪي انهن چيڪن کي غير فعال ڪري ٿو ۽ زبردستي ڪنٽينٽس کي بحال ڪري ٿو ڄڻ ته سڀ ڪجهه ٺيڪ هو، ڪوڊ ڪم ڪيو. ۽ پوءِ هڪ عجيب شيءِ شروع ٿي: ڪڏهن ڪڏهن هم وقت سازي جي ڪوڊ ۾ چيڪ شروع ڪيا ويا - ساڳيا جيڪي ڪوڊ کي خراب ڪن ٿا جيڪڏهن، عمل جي منطق جي مطابق، ان کي بلاڪ ڪيو وڃي - ڪو ماڻهو اڳ ۾ ئي قبضو ڪيل ميٽيڪس کي پڪڙڻ جي ڪوشش ڪئي. خوشقسمتيءَ سان، سيريل ٿيل ڪوڊ ۾ اهو ڪو منطقي مسئلو نه ثابت ٿيو- مان صرف ايم اسڪرپٽن پاران مهيا ڪيل معياري مين لوپ ڪارڪردگيءَ کي استعمال ڪري رهيو هوس، پر ڪڏهن ڪڏهن asynchronous ڪال مڪمل طور تي اسٽيڪ کي ختم ڪري ڇڏيندو، ۽ ان وقت اهو ناڪام ٿي ويندو. setTimeout مکيه لوپ مان - اهڙيء طرح، ڪوڊ اڳئين ورهاڱي کي ڇڏڻ کان سواء مکيه لوپ جي ورهاڱي ۾ داخل ٿيو. هڪ لامحدود لوپ تي ٻيهر لکيو ۽ emscripten_sleep، ۽ mutexes سان مسئلا بند ٿي ويا. ڪوڊ اڃا به وڌيڪ منطقي ٿي چڪو آهي - آخرڪار، حقيقت ۾، مون وٽ ڪجهه ڪوڊ نه آهي جيڪو ايندڙ اينيميشن فريم تيار ڪري ٿو - پروسيسر صرف ڪجهه حساب ڪري ٿو ۽ اسڪرين کي وقتي طور تي اپڊيٽ ڪيو ويندو آهي. بهرحال، مسئلا اتي نه روڪيا ويا: ڪڏهن ڪڏهن Qemu جي عملدرآمد بغير ڪنهن استثنا يا غلطي جي خاموشيء سان ختم ٿي ويندي هئي. ان وقت مون ان کي ڇڏي ڏنو، پر، اڳتي ڏسندي، مان چوندس ته مسئلو هي هو: ڪوروٽين ڪوڊ، حقيقت ۾، استعمال نٿو ڪري setTimeout (يا گهٽ ۾ گهٽ نه جيترو توهان سوچيو هجي): فنڪشن emscripten_yield صرف غير مطابقت واري ڪال پرچم کي سيٽ ڪري ٿو. سڄي ڳالهه اها آهي emscripten_coroutine_next هڪ هم وقت ساز فنڪشن نه آهي: اندروني طور تي اهو پرچم کي چيڪ ڪري ٿو، ان کي ري سيٽ ڪري ٿو ۽ ڪنٽرول کي منتقل ڪري ٿو جتي ان جي ضرورت آهي. اهو آهي، اسٽيڪ جي واڌاري اتي ختم ٿئي ٿي. مسئلو اهو هو ته استعمال کان پوءِ-مفت جي ڪري، جيڪو ظاهر ٿيو جڏهن ڪوروٽين پول کي غير فعال ڪيو ويو ان حقيقت جي ڪري ته مون موجوده ڪوروٽين پس منظر مان ڪوڊ جي هڪ اهم لائن ڪاپي نه ڪئي هئي، فنڪشن qemu_in_coroutine سچو موٽيو جڏهن حقيقت ۾ اهو غلط موٽڻ گهرجي ها. اهو هڪ سڏ جو سبب بڻيو emscripten_yieldجنهن جي مٿي تي ڪو به نه هو emscripten_coroutine_next، اسٽيڪ تمام مٿاھينءَ تائين پکڙجي ويو، پر نه setTimeout، جيئن مون اڳ ۾ چيو، نمائش نه ڪئي وئي.

جاوا اسڪرپٽ ڪوڊ نسل

۽ هتي، حقيقت ۾، واعدو ڪيو ويو آهي "مٽيل گوشت کي واپس ڦيرايو." سچي نه. يقينا، جيڪڏهن اسان برائوزر ۾ Qemu کي هلائيندا آهيون، ۽ ان ۾ Node.js، پوء، قدرتي طور تي، Qemu ۾ ڪوڊ پيدا ڪرڻ کان پوء اسان کي مڪمل طور تي غلط JavaScript حاصل ڪنداسين. پر اڃا تائين، ڪجهه قسم جي ريورس تبديلي.

پهرين، ٿورڙي بابت ڪيئن Qemu ڪم ڪري ٿو. مهرباني ڪري مون کي فوري طور تي معاف ڪجو: مان هڪ پروفيشنل Qemu ڊولپر نه آهيان ۽ منهنجو نتيجو ڪجهه هنڌن تي غلط ٿي سگهي ٿو. جيئن چون ٿا ته، ”شاگرد جي راءِ جو استاد جي راءِ، پيانو جي محوري ۽ عام فهميءَ سان ٺهڪندڙ نه آهي. Qemu وٽ هڪ خاص تعداد ۾ سپورٽ ٿيل مهمان آرڪيٽيڪچرز آهن ۽ هر هڪ لاءِ هڪ ڊاريڪٽري آهي target-i386. تعمير ڪرڻ وقت، توھان ڪيترن ئي مھمانن جي فن تعمير لاءِ مدد بيان ڪري سگھو ٿا، پر نتيجو صرف ڪيترائي بائنري ھوندا. مهمان آرڪيٽيڪچر کي سپورٽ ڪرڻ لاءِ ڪوڊ، موڙ ۾، ڪجهه اندروني Qemu آپريشنز ٺاهي ٿو، جيڪو TCG (ننڍو ڪوڊ جنريٽر) اڳ ۾ ئي ميزبان آرڪيٽيڪچر لاءِ مشين ڪوڊ ۾ بدلجي ٿو. جيئن بيان ڪيو ويو آهي readme فائل ۾ واقع tcg ڊاريڪٽري ۾، اهو اصل ۾ هڪ باقاعده سي مرتب ڪندڙ جو حصو هو، جيڪو بعد ۾ JIT لاء ٺاهيل هو. تنهن ڪري، مثال طور، هن دستاويز جي لحاظ کان ٽارگيٽ فن تعمير هاڻي مهمان فن تعمير نه آهي، پر هڪ ميزبان فن تعمير. ڪجهه نقطي تي، هڪ ٻيو جزو ظاهر ٿيو - ننڍو ڪوڊ انٽرپريٽر (TCI)، جيڪو هڪ مخصوص ميزبان فن تعمير لاء ڪوڊ جنريٽر جي غير موجودگي ۾ ڪوڊ (تقريبا ساڳئي اندروني عملن) تي عمل ڪرڻ گهرجي. حقيقت ۾، جيئن ان جي دستاويزن ۾ بيان ڪيو ويو آهي، اهو مترجم هميشه جي آءِ ٽي ڪوڊ جنريٽر جي حيثيت سان، نه صرف مقدار جي لحاظ سان رفتار جي لحاظ سان، پر معيار جي لحاظ سان پڻ. جيتوڻيڪ مون کي پڪ ناهي ته هن جي وضاحت مڪمل طور تي لاڳاپيل آهي.

پهرين ۾ مون هڪ مڪمل TCG پس منظر ٺاهڻ جي ڪوشش ڪئي، پر جلدي سورس ڪوڊ ۾ مونجهارو ٿي ويو ۽ بائيٽ ڪوڊ هدايتن جي مڪمل طور تي واضح وضاحت نه ڪئي وئي، تنهنڪري مون فيصلو ڪيو ته TCI مترجم کي لپي. هن ڪيترن ئي فائدن ڏنو:

  • جڏهن ڪوڊ جنريٽر کي لاڳو ڪرڻ، توهان هدايتن جي وضاحت کي نه، پر مترجم ڪوڊ تي ڏسي سگهو ٿا
  • توهان فنڪشن پيدا ڪري سگهو ٿا نه هر ترجمي واري بلاڪ لاءِ جنهن جو سامنا ڪيو ويو آهي، پر، مثال طور، صرف سوهين عمل کان پوءِ
  • جيڪڏهن ٺاهيل ڪوڊ تبديل ٿئي ٿو (۽ اهو ممڪن لڳي ٿو، نالن جي نالن سان عمل ڪندي لفظ پيچ تي مشتمل آهي)، مون کي پيدا ڪيل JS ڪوڊ کي رد ڪرڻ جي ضرورت پوندي، پر گهٽ ۾ گهٽ مون وٽ ان کي ٻيهر پيدا ڪرڻ لاء ڪجهه هوندو.

ٽئين نقطي جي حوالي سان، مون کي پڪ ناهي ته پيچنگ ممڪن آهي ته ڪوڊ پهريون ڀيرو لاڳو ٿيڻ کان پوء، پر پهريان ٻه نقطا ڪافي آهن.

شروعات ۾، ڪوڊ اصل بائيٽ ڪوڊ هدايتن جي ايڊريس تي وڏي سوئچ جي صورت ۾ پيدا ڪيو ويو، پر پوء، ايم اسڪرپٽ بابت آرٽيڪل کي ياد ڪندي، ٺاهيل JS جي اصلاح ۽ ريلوپنگ، مون وڌيڪ انساني ڪوڊ پيدا ڪرڻ جو فيصلو ڪيو، خاص طور تي تجرباتي طور تي. اهو ظاهر ٿيو ته ترجمي واري بلاڪ ۾ صرف داخلا پوائنٽ ان جي شروعات آهي. جلد ئي نه چيو ويو آهي، ٿوري دير کان پوء اسان وٽ هڪ ڪوڊ جنريٽر هو جيڪو ڪوڊ ٺاهي ٿو ifs سان (جيتوڻيڪ بغير بغير). پر بدقسمتي سان، اهو تباهه ٿي ويو، هڪ پيغام ڏنو ته هدايتون ڪجهه غلط ڊيگهه جا هئا. ان کان سواء، هن recursion سطح تي آخري هدايتون هو brcond. ٺيڪ آهي، مان هن هدايت جي نسل ۾ هڪجهڙائي چيڪ شامل ڪندس ٻيهر ورهاڱي واري ڪال کان اڳ ۽ بعد ۾ ۽... انهن مان هڪ به نه ڪيو ويو، پر اصرار سوئچ کان پوءِ اهي اڃا ناڪام ٿيا. آخر ۾، ٺاهيل ڪوڊ جي مطالعي کان پوء، مون محسوس ڪيو ته سوئچ کان پوء، موجوده هدايتن ڏانهن اشارو اسٽيڪ مان ٻيهر لوڊ ڪيو ويو آهي ۽ شايد ٺاهيل جاوا اسڪرپٽ ڪوڊ طرفان مٿي لکيو ويو آهي. ۽ پوء اهو نڪتو. بفر کي هڪ ميگا بائيٽ کان ڏهه تائين وڌائڻ سان ڪجهه به نه ٿيو، ۽ اهو واضح ٿي ويو ته ڪوڊ جنريٽر حلقن ۾ هلندو هو. اسان کي چيڪ ڪرڻو هو ته اسان موجوده ٽي بي جي حدن کان ٻاهر ته نه وڃون، ۽ جيڪڏهن اسان ڪيو، ته پوء ايندڙ ٽي بي جو پتو مائنس جي نشاني سان جاري ڪيو وڃي ته جيئن اسان عملدرآمد جاري رکي سگهون. ان کان علاوه، اهو مسئلو حل ڪري ٿو "جيڪڏهن هن بائيٽ ڪوڊ جو ٽڪرو تبديل ٿي ويو آهي ته ڪهڙي ٺاهيل افعال کي غلط قرار ڏنو وڃي؟" - صرف فعل جيڪو ھن ترجمي واري بلاڪ سان ملندو آھي ان کي رد ڪرڻ جي ضرورت آھي. رستي ۾، جيتوڻيڪ مون Chromium ۾ سڀڪنھن شيء کي ڊيبگ ڪيو (جڏھن مان فائر فاڪس استعمال ڪريان ٿو ۽ تجربن لاءِ الڳ برائوزر استعمال ڪرڻ مون لاءِ آسان آھي)، فائرفاڪس مون کي asm.js معيار سان ناانصافين کي درست ڪرڻ ۾ مدد ڪئي، جنھن کان پوءِ ڪوڊ تيزيءَ سان ڪم ڪرڻ لڳو. ڪروميم.

ٺاهيل ڪوڊ جو مثال

Compiling 0x15b46d0:
CompiledTB[0x015b46d0] = function(stdlib, ffi, heap) {
"use asm";
var HEAP8 = new stdlib.Int8Array(heap);
var HEAP16 = new stdlib.Int16Array(heap);
var HEAP32 = new stdlib.Int32Array(heap);
var HEAPU8 = new stdlib.Uint8Array(heap);
var HEAPU16 = new stdlib.Uint16Array(heap);
var HEAPU32 = new stdlib.Uint32Array(heap);

var dynCall_iiiiiiiiiii = ffi.dynCall_iiiiiiiiiii;
var getTempRet0 = ffi.getTempRet0;
var badAlignment = ffi.badAlignment;
var _i64Add = ffi._i64Add;
var _i64Subtract = ffi._i64Subtract;
var Math_imul = ffi.Math_imul;
var _mul_unsigned_long_long = ffi._mul_unsigned_long_long;
var execute_if_compiled = ffi.execute_if_compiled;
var getThrew = ffi.getThrew;
var abort = ffi.abort;
var qemu_ld_ub = ffi.qemu_ld_ub;
var qemu_ld_leuw = ffi.qemu_ld_leuw;
var qemu_ld_leul = ffi.qemu_ld_leul;
var qemu_ld_beuw = ffi.qemu_ld_beuw;
var qemu_ld_beul = ffi.qemu_ld_beul;
var qemu_ld_beq = ffi.qemu_ld_beq;
var qemu_ld_leq = ffi.qemu_ld_leq;
var qemu_st_b = ffi.qemu_st_b;
var qemu_st_lew = ffi.qemu_st_lew;
var qemu_st_lel = ffi.qemu_st_lel;
var qemu_st_bew = ffi.qemu_st_bew;
var qemu_st_bel = ffi.qemu_st_bel;
var qemu_st_leq = ffi.qemu_st_leq;
var qemu_st_beq = ffi.qemu_st_beq;

function tb_fun(tb_ptr, env, sp_value, depth) {
  tb_ptr = tb_ptr|0;
  env = env|0;
  sp_value = sp_value|0;
  depth = depth|0;
  var u0 = 0, u1 = 0, u2 = 0, u3 = 0, result = 0;
  var r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0;
  var r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0, r16 = 0, r17 = 0, r18 = 0, r19 = 0;
  var r20 = 0, r21 = 0, r22 = 0, r23 = 0, r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0;
  var r30 = 0, r31 = 0, r41 = 0, r42 = 0, r43 = 0, r44 = 0;
    r14 = env|0;
    r15 = sp_value|0;
  START: do {
    r0 = HEAPU32[((r14 + (-4))|0) >> 2] | 0;
    r42 = 0;
    result = ((r0|0) != (r42|0))|0;
    HEAPU32[1445307] = r0;
    HEAPU32[1445321] = r14;
    if(result|0) {
    HEAPU32[1445322] = r15;
    return 0x0345bf93|0;
    }
    r0 = HEAPU32[((r14 + (16))|0) >> 2] | 0;
    r42 = 8;
    r0 = ((r0|0) - (r42|0))|0;
    HEAPU32[(r14 + (16)) >> 2] = r0;
    r1 = 8;
    HEAPU32[(r14 + (44)) >> 2] = r1;
    r1 = r0|0;
    HEAPU32[(r14 + (40)) >> 2] = r1;
    r42 = 4;
    r0 = ((r0|0) + (r42|0))|0;
    r2 = HEAPU32[((r14 + (24))|0) >> 2] | 0;
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    HEAPU32[1445309] = r2;
    HEAPU32[1445321] = r14;
    HEAPU32[1445322] = r15;
    qemu_st_lel(env|0, r0|0, r2|0, 34, 22759218);
if(getThrew() | 0) abort();
    r0 = 3241038392;
    HEAPU32[1445307] = r0;
    r0 = qemu_ld_leul(env|0, r0|0, 34, 22759233)|0;
if(getThrew() | 0) abort();
    HEAPU32[(r14 + (24)) >> 2] = r0;
    r1 = HEAPU32[((r14 + (12))|0) >> 2] | 0;
    r2 = HEAPU32[((r14 + (40))|0) >> 2] | 0;
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    HEAPU32[1445309] = r2;
    qemu_st_lel(env|0, r2|0, r1|0, 34, 22759265);
if(getThrew() | 0) abort();
    r0 = HEAPU32[((r14 + (24))|0) >> 2] | 0;
    HEAPU32[(r14 + (40)) >> 2] = r0;
    r1 = 24;
    HEAPU32[(r14 + (52)) >> 2] = r1;
    r42 = 0;
    result = ((r0|0) == (r42|0))|0;
    if(result|0) {
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    }
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    return execute_if_compiled(22759392|0, env|0, sp_value|0, depth|0) | 0;
    return execute_if_compiled(23164080|0, env|0, sp_value|0, depth|0) | 0;
    break;
  } while(1); abort(); return 0|0;
}
return {tb_fun: tb_fun};
}(window, CompilerFFI, Module.buffer)["tb_fun"]

ٿڪل

تنهن ڪري، ڪم اڃا مڪمل نه ٿيو آهي، پر مان ٿڪجي پيو آهيان ڳجهي طور تي هن ڊگهي مدي واري تعمير کي تڪميل تائين پهچايو. تنهن ڪري، مون فيصلو ڪيو ته شايع ڪرڻ جو ڇا مون وٽ آهي. ڪوڊ هنڌن تي ٿورڙو خوفناڪ آهي، ڇاڪاڻ ته هي هڪ تجربو آهي، ۽ اهو اڳ ۾ واضح ناهي ته ڇا ٿيڻ جي ضرورت آهي. شايد، پوءِ اهو قابل آهي ته عام ايٽمي ڪميٽ جاري ڪرڻ جي مٿان قيمو جي ڪجهه وڌيڪ جديد ورزن جي مٿان. ساڳئي وقت، گيتا ۾ هڪ بلاگ فارميٽ ۾ هڪ موضوع آهي: هر "سطح" لاء جيڪو گهٽ ۾ گهٽ ڪنهن نه ڪنهن طرح گذري چڪو آهي، روسي ۾ هڪ تفصيلي تبصرو شامل ڪيو ويو آهي. درحقيقت، هي مضمون وڏي حد تائين نتيجي جي ٻيهر بيان ڪرڻ آهي git log.

توهان اهو سڀ ڪوشش ڪري سگهو ٿا هتي (ٽريفڪ کان بچو).

ڇا اڳ ۾ ئي ڪم ڪري رهيو آهي:

  • x86 ورچوئل پروسيسر
  • مشين ڪوڊ کان جاوا اسڪرپٽ تائين JIT ڪوڊ جنريٽر جو ھڪڙو ڪم ڪندڙ پروٽوٽائپ آھي
  • ٻين 32-bit گيسٽ آرڪيٽيڪچرز کي گڏ ڪرڻ لاءِ هڪ ٽيمپليٽ موجود آهي: هن وقت توهان لينڪس جي تعريف ڪري سگهو ٿا MIPS آرڪيٽيڪچر لاءِ برائوزر ۾ منجهيل لوڊ ٿيڻ واري مرحلي تي

ٻيو ڇا ٿو ڪري سگهان

  • ايموليشن کي تيز ڪريو. جيتوڻيڪ JIT موڊ ۾ اهو لڳي ٿو ته اهو Virtual x86 کان وڌيڪ سست آهي (پر اتي امڪاني طور تي هڪ مڪمل Qemu آهي تمام گهڻو نقل ٿيل هارڊويئر ۽ آرڪيٽيڪچر سان)
  • عام انٽرفيس ٺاهڻ لاءِ - سچ ته، مان سٺو ويب ڊولپر نه آهيان، تنهن ڪري هن وقت لاءِ مون معياري ايم اسڪرپٽ شيل کي ٻيهر ٺاهيو آهي جيترو مان ڪري سگهان ٿو.
  • وڌيڪ پيچيده Qemu افعال شروع ڪرڻ جي ڪوشش ڪريو - نيٽ ورڪنگ، VM لڏپلاڻ، وغيره.
  • يو ايس ڊي توھان کي پنھنجيون ڪجھ ترقيون ۽ بگ رپورٽون Emscripten upstream ڏانھن موڪلڻ جي ضرورت پوندي، جيئن قيمو جي پوئين پورٽرز ۽ ٻين منصوبن ڪئي ھئي. انهن جي مهرباني جو منهنجي ڪم جي حصي طور Emscripten ۾ پنهنجو حصو واضح طور تي استعمال ڪرڻ جي قابل ٿي ويا.

جو ذريعو: www.habr.com

تبصرو شامل ڪريو