JIT سپورٹ کے ساتھ Qemu.js: آپ اب بھی کیما کو پیچھے کی طرف موڑ سکتے ہیں۔

چند سال پہلے Fabrice Bellard jslinux کے ذریعہ لکھا گیا۔ جاوا اسکرپٹ میں لکھا ہوا پی سی ایمولیٹر ہے۔ اس کے بعد کم از کم اور بھی تھا۔ ورچوئل x86. لیکن یہ سب، جہاں تک میں جانتا ہوں، ترجمان تھے، جبکہ کیمو، جو بہت پہلے اسی Fabrice Bellard نے لکھا تھا، اور، شاید، کوئی بھی خود ساختہ جدید ایمولیٹر، میزبان سسٹم کوڈ میں مہمان کوڈ کی JIT تالیف کا استعمال کرتا ہے۔ مجھے ایسا لگتا تھا کہ یہ براؤزر حل کرنے والے کام کے سلسلے میں مخالف کام کو نافذ کرنے کا وقت ہے: جاوا اسکرپٹ میں مشین کوڈ کی JIT تالیف، جس کے لیے یہ Qemu پورٹ کے لیے سب سے زیادہ منطقی معلوم ہوتا ہے۔ ایسا لگتا ہے، کیوں Qemu، وہاں آسان اور صارف دوست ایمولیٹر ہیں - وہی ورچوئل باکس، مثال کے طور پر - انسٹال اور کام کرتا ہے۔ لیکن کیمو میں کئی دلچسپ خصوصیات ہیں۔

  • آزاد مصدر
  • کرنل ڈرائیور کے بغیر کام کرنے کی صلاحیت
  • مترجم موڈ میں کام کرنے کی صلاحیت
  • میزبان اور مہمان فن تعمیر دونوں کی ایک بڑی تعداد کے لیے تعاون

تیسرے نکتے کے بارے میں، میں اب وضاحت کر سکتا ہوں کہ درحقیقت، TCI موڈ میں، یہ خود مہمان مشین کی ہدایات نہیں ہیں جن کی تشریح کی جاتی ہے، بلکہ ان سے حاصل کردہ بائیک کوڈ، لیکن اس سے جوہر نہیں بدلتا ہے - بنانے اور چلانے کے لیے۔ Qemu ایک نئے فن تعمیر پر، اگر آپ خوش قسمت ہیں، A C کمپائلر کافی ہے - کوڈ جنریٹر لکھنا ملتوی کیا جا سکتا ہے۔

اور اب، میرے فارغ وقت میں دو سال کے آرام سے کیمو سورس کوڈ کے ساتھ ٹنکرنگ کرنے کے بعد، ایک ورکنگ پروٹو ٹائپ نمودار ہوا، جس میں آپ پہلے ہی چلا سکتے ہیں، مثال کے طور پر، کولیبری OS۔

Emscripten کیا ہے؟

آج کل، بہت سے کمپائلرز نمودار ہوئے ہیں، جن کا آخری نتیجہ جاوا اسکرپٹ ہے۔ کچھ، جیسے ٹائپ اسکرپٹ، اصل میں ویب کے لیے لکھنے کا بہترین طریقہ بننا تھا۔ ایک ہی وقت میں، Emscripten موجودہ C یا C++ کوڈ لینے اور اسے براؤزر کے پڑھنے کے قابل شکل میں مرتب کرنے کا ایک طریقہ ہے۔ پر اس صفحہ پر ہم نے معروف پروگراموں کے بہت سے بندرگاہوں کو جمع کیا ہے: یہاںمثال کے طور پر، آپ PyPy کو دیکھ سکتے ہیں - ویسے، وہ دعویٰ کرتے ہیں کہ ان کے پاس JIT پہلے سے موجود ہے۔ درحقیقت، ہر پروگرام کو آسانی سے مرتب اور براؤزر میں نہیں چلایا جا سکتا ہے - اس کی ایک تعداد ہوتی ہے۔ خصوصیات، جسے آپ کو برداشت کرنا پڑے گا، تاہم، جیسا کہ اسی صفحہ پر لکھا ہوا ہے کہ "Emscripten کو تقریباً کسی بھی مرتب کرنے کے لیے استعمال کیا جا سکتا ہے۔ پورٹیبل جاوا اسکرپٹ کے لیے C/C++ کوڈ"۔ یعنی، ایسے بہت سے آپریشنز ہیں جو معیار کے مطابق غیر متعینہ رویے ہیں، لیکن عام طور پر x86 پر کام کرتے ہیں - مثال کے طور پر، متغیرات تک غیر منسلک رسائی، جو عام طور پر کچھ فن تعمیرات پر ممنوع ہے۔ , Qemu ایک کراس پلیٹ فارم پروگرام ہے اور، میں یقین کرنا چاہتا تھا، اور اس میں پہلے سے بہت زیادہ غیر متعینہ رویہ نہیں ہے - اسے لیں اور مرتب کریں، پھر JIT کے ساتھ تھوڑا سا ٹنکر کریں - اور آپ کا کام ہو گیا! لیکن یہ نہیں ہے معاملہ...

پہلی کوشش

عام طور پر، میں پہلا شخص نہیں ہوں جس نے Qemu کو JavaScript پر پورٹ کرنے کا خیال پیش کیا۔ ReactOS فورم پر ایک سوال پوچھا گیا تھا کہ کیا یہ Emscripten کا استعمال کرتے ہوئے ممکن تھا؟ اس سے قبل بھی، افواہیں تھیں کہ Fabrice Bellard نے ذاتی طور پر ایسا کیا تھا، لیکن ہم jslinux کے بارے میں بات کر رہے تھے، جو جہاں تک مجھے معلوم ہے، JS میں دستی طور پر کافی کارکردگی حاصل کرنے کی صرف ایک کوشش ہے، اور اسے شروع سے لکھا گیا تھا۔ بعد میں، ورچوئل x86 لکھا گیا - اس کے لیے غیر واضح ذرائع پوسٹ کیے گئے، اور جیسا کہ کہا گیا ہے، ایمولیشن کی بڑی "حقیقت پسندی" نے SeaBIOS کو بطور فرم ویئر استعمال کرنا ممکن بنایا۔ اس کے علاوہ، ایم اسکرپٹن کا استعمال کرتے ہوئے کیمو کو پورٹ کرنے کی کم از کم ایک کوشش تھی - میں نے ایسا کرنے کی کوشش کی۔ ساکٹ جوڑالیکن ترقی، جہاں تک میں سمجھتا ہوں، منجمد تھا۔

تو، ایسا لگتا ہے، یہاں ذرائع ہیں، یہاں Emscripten ہے - اسے لیں اور مرتب کریں۔ لیکن ایسی لائبریریاں بھی ہیں جن پر قیمو کا انحصار ہے، اور لائبریریاں جن پر وہ لائبریریاں منحصر ہیں، وغیرہ، اور ان میں سے ایک ہے۔ libffi، جس پر منحصر ہے۔ انٹرنیٹ پر افواہیں تھیں کہ ایم اسکرپٹن کے لیے لائبریریوں کی بندرگاہوں کے بڑے ذخیرے میں ایک موجود ہے، لیکن اس پر یقین کرنا کسی حد تک مشکل تھا: اول، یہ ایک نیا مرتب کرنے والا نہیں تھا، دوم، یہ بہت کم درجے کا تھا۔ لائبریری کو صرف اٹھانا ہے، اور جے ایس میں مرتب کرنا ہے۔ اور یہ صرف اسمبلی داخلوں کا معاملہ نہیں ہے - شاید، اگر آپ اسے موڑ دیتے ہیں، تو کچھ کالنگ کنونشنز کے لیے آپ اسٹیک پر ضروری دلائل تیار کر سکتے ہیں اور ان کے بغیر فنکشن کو کال کر سکتے ہیں۔ لیکن ایم اسکرپٹن ایک مشکل چیز ہے: تیار کردہ کوڈ کو براؤزر JS انجن آپٹیمائزر سے مانوس بنانے کے لیے، کچھ ترکیبیں استعمال کی جاتی ہیں۔ خاص طور پر، نام نہاد ری لوپنگ - کچھ تجریدی منتقلی ہدایات کے ساتھ موصولہ LLVM IR کا استعمال کرتے ہوئے ایک کوڈ جنریٹر ممکنہ ifs، loops وغیرہ کو دوبارہ بنانے کی کوشش کرتا ہے۔ ٹھیک ہے، فنکشن میں دلائل کیسے پاس کیے جاتے ہیں؟ قدرتی طور پر، جے ایس فنکشنز کے دلائل کے طور پر، یعنی اگر ممکن ہو تو اسٹیک کے ذریعے نہیں۔

شروع میں صرف JS کے ساتھ libffi کا متبادل لکھنے اور معیاری ٹیسٹ چلانے کا خیال تھا، لیکن آخر میں میں الجھن میں پڑ گیا کہ اپنی ہیڈر فائلوں کو کیسے بنایا جائے تاکہ وہ موجودہ کوڈ کے ساتھ کام کریں - میں کیا کر سکتا ہوں، جیسا کہ وہ کہتے ہیں، "کیا کام اتنے پیچیدہ ہیں "کیا ہم اتنے بیوقوف ہیں؟" مجھے libffi کو ایک اور فن تعمیر پر پورٹ کرنا پڑا، تو بات کرنے کے لیے - خوش قسمتی سے، Emscripten کے پاس ان لائن اسمبلی کے لیے دونوں میکرو ہیں (جاوا اسکرپٹ میں، ہاں - ٹھیک ہے، جو بھی فن تعمیر ہے، تو اسمبلر)، اور فلائی پر تیار کردہ کوڈ کو چلانے کی صلاحیت۔ عام طور پر، پلیٹ فارم پر منحصر libffi کے ٹکڑوں کے ساتھ کچھ وقت تک چھیڑ چھاڑ کرنے کے بعد، مجھے کچھ مرتب کرنے والا کوڈ ملا اور اسے پہلے ٹیسٹ میں چلا جس میں میں آیا۔ میری حیرت کی بات یہ ہے کہ ٹیسٹ کامیاب رہا۔ میری ذہانت سے دنگ رہ گیا - کوئی مذاق نہیں، اس نے پہلی لانچ سے ہی کام کیا - میں، ابھی تک اپنی آنکھوں پر یقین نہیں کر رہا تھا، نتیجے میں آنے والے کوڈ کو دوبارہ دیکھنے گیا، اس بات کا اندازہ کرنے کے لیے کہ اگلا کہاں کھودنا ہے۔ یہاں میں دوسری بار پاگل ہو گیا - میرے فنکشن نے صرف یہ کیا تھا۔ ffi_call - اس نے ایک کامیاب کال کی اطلاع دی۔ خود کوئی کال نہیں تھی۔ لہذا میں نے اپنی پہلی پل کی درخواست بھیجی، جس نے ٹیسٹ میں ایک ایسی غلطی کو درست کیا جو اولمپیاڈ کے کسی بھی طالب علم کے لیے واضح ہے - حقیقی نمبروں کا اس طرح موازنہ نہیں کیا جانا چاہیے a == b اور یہاں تک کہ کس طرح a - b < EPS - آپ کو ماڈیول کو بھی یاد رکھنے کی ضرورت ہے، ورنہ 0 بہت زیادہ 1/3 کے برابر ہو جائے گا... عام طور پر، میں libffi کی ایک خاص بندرگاہ لے کر آیا ہوں، جو آسان ترین ٹیسٹ پاس کرتا ہے، اور جس کے ساتھ glib ہے compiled - میں نے فیصلہ کیا کہ یہ ضروری ہوگا، میں اسے بعد میں شامل کروں گا۔ آگے دیکھتے ہوئے، میں یہ کہوں گا کہ، جیسا کہ یہ نکلا، مرتب کرنے والے نے حتمی کوڈ میں libffi فنکشن کو بھی شامل نہیں کیا۔

لیکن، جیسا کہ میں پہلے ہی کہہ چکا ہوں، کچھ حدود ہیں، اور مختلف غیر متعینہ رویے کے آزادانہ استعمال کے درمیان، ایک زیادہ ناخوشگوار خصوصیت چھپی ہوئی ہے - جاوا اسکرپٹ بذریعہ ڈیزائن مشترکہ میموری کے ساتھ ملٹی تھریڈنگ کو سپورٹ نہیں کرتا ہے۔ اصولی طور پر، اسے عام طور پر ایک اچھا خیال بھی کہا جا سکتا ہے، لیکن پورٹنگ کوڈ کے لیے نہیں جس کا فن تعمیر C تھریڈز سے منسلک ہے۔ عام طور پر، فائر فاکس مشترکہ کارکنوں کی حمایت کے ساتھ تجربہ کر رہا ہے، اور ایم اسکرپٹن ان کے لیے ایک پیتھریڈ کا نفاذ ہے، لیکن میں اس پر انحصار نہیں کرنا چاہتا تھا۔ مجھے Qemu کوڈ سے ملٹی تھریڈنگ کو آہستہ آہستہ روٹ آؤٹ کرنا تھا - یعنی یہ معلوم کریں کہ تھریڈز کہاں چل رہے ہیں، اس تھریڈ میں چلنے والے لوپ کی باڈی کو ایک الگ فنکشن میں منتقل کریں، اور ایسے فنکشنز کو مین لوپ سے ایک ایک کرکے کال کریں۔

دوسری کوشش

کسی موقع پر، یہ واضح ہو گیا کہ مسئلہ ابھی بھی موجود ہے، اور کوڈ کے ارد گرد بے ترتیبی سے بیساکھیوں کو ہلانے سے کوئی فائدہ نہیں ہوگا۔ نتیجہ: ہمیں بیساکھیوں کو جوڑنے کے عمل کو کسی نہ کسی طرح منظم کرنے کی ضرورت ہے۔ لہذا، ورژن 2.4.1، جو اس وقت تازہ تھا، لیا گیا تھا (2.5.0 نہیں، کیونکہ کون جانتا ہے، نئے ورژن میں ایسے کیڑے ہوں گے جو ابھی تک نہیں پکڑے گئے، اور میرے پاس اپنے کیڑے کافی ہیں۔ )، اور پہلی چیز اسے محفوظ طریقے سے دوبارہ لکھنا تھا۔ thread-posix.c. ٹھیک ہے، یہ اتنا ہی محفوظ ہے: اگر کسی نے آپریشن کرنے کی کوشش کی جس کے نتیجے میں بلاکنگ ہو، فنکشن کو فوری طور پر بلایا گیا۔ abort() - یقینا، اس سے تمام مسائل ایک ساتھ حل نہیں ہوئے، لیکن کم از کم یہ خاموشی سے متضاد ڈیٹا حاصل کرنے سے کہیں زیادہ خوشگوار تھا۔

عام طور پر، ایم اسکرپٹن کے اختیارات جے ایس کو کوڈ پورٹ کرنے میں بہت مددگار ہوتے ہیں۔ -s ASSERTIONS=1 -s SAFE_HEAP=1 - وہ کچھ قسم کے غیر متعینہ رویے کو پکڑتے ہیں، جیسے کہ غیر منسلک ایڈریس پر کالز (جو کہ ٹائپ کردہ صفوں کے کوڈ کے ساتھ بالکل بھی مطابقت نہیں رکھتا ہے جیسے HEAP32[addr >> 2] = 1) یا دلائل کی غلط تعداد کے ساتھ فنکشن کو کال کرنا۔

ویسے، صف بندی کی غلطیاں ایک الگ مسئلہ ہے۔ جیسا کہ میں نے پہلے ہی کہا ہے، Qemu کے پاس کوڈ جنریشن TCI (چھوٹا کوڈ انٹرپریٹر) کے لیے "ڈیجنریٹ" تشریحی پس منظر ہے، اور Qemu کو ایک نئے فن تعمیر پر بنانے اور چلانے کے لیے، اگر آپ خوش قسمت ہیں، تو ایک C مرتب کرنے والا کافی ہے۔ کلیدی الفاظ "اگر تم خوش قسمت ہو". میں بدقسمت تھا، اور یہ پتہ چلا کہ TCI اپنے بائیک کوڈ کو پارس کرتے وقت غیر منسلک رسائی کا استعمال کرتا ہے۔ یعنی، تمام قسم کے ARM اور دیگر فن تعمیرات پر ضروری طور پر برابر رسائی کے ساتھ، Qemu مرتب کرتا ہے کیونکہ ان کے پاس ایک عام TCG بیک اینڈ ہے جو مقامی کوڈ تیار کرتا ہے، لیکن آیا TCI ان پر کام کرے گا یا نہیں یہ ایک اور سوال ہے۔ تاہم، جیسا کہ یہ نکلا، TCI دستاویزات نے واضح طور پر کچھ ایسا ہی اشارہ کیا۔ نتیجے کے طور پر، غیر منسلک پڑھنے کے لیے فنکشن کالز کوڈ میں شامل کیے گئے، جو کیمو کے دوسرے حصے میں پائے گئے۔

ڈھیر کی تباہی۔

نتیجے کے طور پر، TCI تک غیر منسلک رسائی کو درست کر دیا گیا، ایک اہم لوپ بنایا گیا جسے بدلے میں پروسیسر، RCU اور کچھ دوسری چھوٹی چیزیں کہتے ہیں۔ اور اس طرح میں آپشن کے ساتھ Qemu لانچ کرتا ہوں۔ -d exec,in_asm,out_asm، جس کا مطلب ہے کہ آپ کو یہ بتانے کی ضرورت ہے کہ کوڈ کے کون سے بلاکس پر عمل درآمد ہو رہا ہے، اور براڈکاسٹ کے وقت یہ بھی لکھنا ہے کہ مہمان کوڈ کیا تھا، کون سا میزبان کوڈ بن گیا (اس معاملے میں، بائی کوڈ)۔ یہ شروع ہوتا ہے، متعدد ترجمے کے بلاکس کو انجام دیتا ہے، ڈیبگنگ پیغام لکھتا ہے جو میں نے چھوڑا تھا کہ RCU اب شروع ہو جائے گا اور... کریش abort() ایک فنکشن کے اندر free(). فنکشن کے ساتھ ٹنکرنگ کرکے free() ہم یہ معلوم کرنے میں کامیاب ہو گئے کہ ہیپ بلاک کے ہیڈر میں، جو کہ مختص میموری سے پہلے آٹھ بائٹس میں ہے، بلاک کے سائز یا اس سے ملتی جلتی چیز کے بجائے، کچرا تھا۔

ڈھیر کی تباہی - کتنا پیارا ہے... ایسی صورت میں، ایک مفید علاج ہے - (اگر ممکن ہو) انہی ذرائع سے، ایک مقامی بائنری کو جمع کریں اور اسے Valgrind کے تحت چلائیں۔ کچھ دیر بعد بائنری تیار ہو گئی۔ میں اسے انہی اختیارات کے ساتھ لانچ کرتا ہوں - یہ اصل میں عمل درآمد تک پہنچنے سے پہلے ہی ابتدا کے دوران بھی کریش ہو جاتا ہے۔ یہ ناخوشگوار ہے، یقیناً - بظاہر، ذرائع بالکل ایک جیسے نہیں تھے، جو کہ حیرت کی بات نہیں ہے، کیونکہ کنفیگر نے تھوڑا سا مختلف آپشنز تلاش کیے ہیں، لیکن میرے پاس ویلگرینڈ ہے - پہلے میں اس مسئلے کو ٹھیک کروں گا، اور پھر، اگر میں خوش قسمت ہوں ، اصل ظاہر ہوگا۔ میں وہی چیز Valgrind کے تحت چلا رہا ہوں... Y-y-y، y-y-y، uh-uh، یہ شروع ہوا، عام طور پر ابتداء سے گزرا اور غلط میموری تک رسائی کے بارے میں ایک بھی انتباہ کے بغیر اصل بگ سے گزر گیا، زوال کا ذکر نہ کرنا۔ زندگی، جیسا کہ وہ کہتے ہیں، نے مجھے اس کے لیے تیار نہیں کیا - ایک کریش پروگرام جب والگرینڈ کے تحت لانچ کیا گیا تو کریش ہونا بند ہو جاتا ہے۔ یہ کیا تھا ایک معمہ ہے۔ میرا مفروضہ یہ ہے کہ ایک بار موجودہ ہدایات کے آس پاس میں شروع ہونے کے دوران حادثے کے بعد، جی ڈی بی نے کام دکھایا memsetیا تو استعمال کرتے ہوئے ایک درست پوائنٹر کے ساتھ mmx، یا xmm رجسٹر کرتا ہے، پھر شاید یہ کسی قسم کی صف بندی کی غلطی تھی، حالانکہ اس پر یقین کرنا اب بھی مشکل ہے۔

ٹھیک ہے، ویلگرینڈ یہاں مدد نہیں کرتا۔ اور یہاں سے سب سے نفرت انگیز چیز شروع ہوئی - لگتا ہے کہ سب کچھ شروع ہوتا ہے، لیکن بالکل نامعلوم وجوہات کی بنا پر کریش ہو جاتا ہے جو کہ لاکھوں ہدایات سے پہلے ہو سکتا تھا۔ کافی دیر تک یہ بھی سمجھ نہیں آرہا تھا کہ کس طرح رجوع کیا جائے۔ آخر میں، مجھے اب بھی بیٹھ کر ڈیبگ کرنا پڑا۔ ہیڈر کو جس چیز کے ساتھ دوبارہ لکھا گیا تھا اس کو پرنٹ کرنے سے یہ ظاہر ہوتا ہے کہ یہ نمبر کی طرح نہیں لگتا ہے، بلکہ کسی قسم کا بائنری ڈیٹا ہے۔ اور، دیکھو، یہ بائنری سٹرنگ 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 کا استعمال کرتے ہوئے سوئچ کر سکتے ہیں: یہ مقامی تعمیر میں کام کرتا ہے، لیکن Emscripten میں نہیں۔ اختیارات کا استعمال کرتے ہوئے غیر ضروری ونڈوز سے چھٹکارا حاصل کرنے کے بعد -monitor none -parallel none -serial none اور ہر فریم پر پوری سکرین کو زبردستی دوبارہ کھینچنے کی ہدایات، سب کچھ اچانک کام کر گیا۔

کوروٹینز

لہذا، براؤزر میں ایمولیشن کام کرتی ہے، لیکن آپ اس میں کوئی دلچسپ سنگل فلاپی نہیں چلا سکتے، کیونکہ کوئی بلاک I/O نہیں ہے - آپ کو کورٹینز کے لیے سپورٹ کو نافذ کرنے کی ضرورت ہے۔ Qemu کے پاس پہلے سے ہی کئی کورٹین بیک اینڈز ہیں، لیکن JavaScript اور Emscripten کوڈ جنریٹر کی نوعیت کی وجہ سے، آپ صرف جگلنگ اسٹیکس شروع نہیں کر سکتے۔ ایسا لگتا ہے کہ "سب کچھ ختم ہو گیا ہے، پلاسٹر کو ہٹایا جا رہا ہے،" لیکن Emscripten کے ڈویلپرز نے پہلے سے ہی ہر چیز کا خیال رکھا ہے. یہ کافی مضحکہ خیز لاگو کیا گیا ہے: آئیے اس مشتبہ جیسی فنکشن کال کو کال کریں۔ emscripten_sleep اور کئی دوسرے Asyncify میکانزم کا استعمال کرتے ہوئے، نیز پوائنٹر کالز اور کسی بھی فنکشن کے لیے کالز جہاں پچھلے دو کیسز میں سے ایک اسٹیک کے نیچے ہو سکتا ہے۔ اور اب، ہر مشتبہ کال سے پہلے، ہم ایک async سیاق و سباق کا انتخاب کریں گے، اور کال کے فوراً بعد، ہم چیک کریں گے کہ آیا کوئی غیر مطابقت پذیر کال ہوئی ہے، اور اگر یہ ہے، تو ہم اس async سیاق و سباق میں تمام مقامی متغیرات کو محفوظ کریں گے، اس بات کی نشاندہی کریں گے کہ کون سا فنکشن کنٹرول کو منتقل کرنے کے لیے جب ہمیں عمل درآمد جاری رکھنے کی ضرورت ہو، اور موجودہ فنکشن سے باہر نکلیں۔ یہیں سے اثر کا مطالعہ کرنے کی گنجائش ہے۔ فضول خرچی - ایک غیر مطابقت پذیر کال سے واپس آنے کے بعد کوڈ پر عمل درآمد جاری رکھنے کی ضروریات کے لیے، کمپائلر ایک مشکوک کال کے بعد شروع ہونے والے فنکشن کے "اسٹبس" تیار کرتا ہے — اس طرح: اگر n مشکوک کالیں ہوں، تو فنکشن کو کہیں n/2 بڑھا دیا جائے گا۔ اوقات — یہ اب بھی ہے، اگر نہیں تو ذہن میں رکھیں کہ ہر ممکنہ طور پر غیر مطابقت پذیر کال کے بعد، آپ کو کچھ مقامی متغیرات کو اصل فنکشن میں محفوظ کرنا شامل کرنا ہوگا۔ اس کے بعد، مجھے ازگر میں ایک سادہ اسکرپٹ بھی لکھنا پڑا، جو کہ خاص طور پر زیادہ استعمال شدہ فنکشنز کے دیئے گئے سیٹ پر مبنی ہے جو قیاس کیا جاتا ہے کہ "غیر مطابقت پذیری کو خود سے گزرنے نہیں دیتے" (یعنی، اسٹیک پروموشن اور ہر وہ چیز جو میں نے ابھی بیان کی ہے، ایسا نہیں ہوتا ہے۔ ان میں کام)، پوائنٹرز کے ذریعے کالز کی نشاندہی کرتا ہے جس میں فنکشنز کو کمپائلر کو نظر انداز کرنا چاہیے تاکہ ان فنکشنز کو غیر مطابقت پذیر نہ سمجھا جائے۔ اور پھر 60 MB سے کم JS فائلیں واضح طور پر بہت زیادہ ہیں - آئیے کم از کم 30 کہتے ہیں۔ حالانکہ، ایک بار میں اسمبلی اسکرپٹ ترتیب دے رہا تھا، اور غلطی سے لنکر آپشنز کو باہر پھینک دیا، جن میں سے -O3. میں تیار کردہ کوڈ چلاتا ہوں، اور کرومیم میموری کو کھا جاتا ہے اور کریش ہو جاتا ہے۔ میں نے پھر اتفاق سے دیکھا کہ وہ کیا ڈاؤن لوڈ کرنے کی کوشش کر رہا ہے... ٹھیک ہے، میں کیا کہہ سکتا ہوں، اگر مجھے سوچ سمجھ کر 500+ MB جاوا اسکرپٹ کا مطالعہ کرنے اور آپٹمائز کرنے کو کہا جاتا تو میں بھی منجمد ہو جاتا۔

بدقسمتی سے، Asyncify سپورٹ لائبریری کوڈ میں چیک مکمل طور پر دوستانہ نہیں تھے۔ longjmp-s جو ورچوئل پروسیسر کوڈ میں استعمال ہوتے ہیں، لیکن ایک چھوٹے سے پیچ کے بعد جو ان چیکوں کو غیر فعال کر دیتا ہے اور سیاق و سباق کو زبردستی بحال کرتا ہے گویا سب کچھ ٹھیک تھا، کوڈ نے کام کیا۔ اور پھر ایک عجیب بات شروع ہوئی: بعض اوقات سنکرونائزیشن کوڈ میں چیکس کو متحرک کیا جاتا تھا - وہی جو کوڈ کو کریش کرتے ہیں اگر، عملدرآمد کی منطق کے مطابق، اسے بلاک کر دیا جانا چاہیے - کسی نے پہلے سے پکڑے ہوئے میوٹیکس کو پکڑنے کی کوشش کی۔ خوش قسمتی سے، یہ سیریلائزڈ کوڈ میں کوئی منطقی مسئلہ نہیں نکلا - میں صرف ایم اسکرپٹن کے ذریعہ فراہم کردہ معیاری مین لوپ فعالیت کو استعمال کر رہا تھا، لیکن بعض اوقات غیر مطابقت پذیر کال اسٹیک کو مکمل طور پر کھول دیتی ہے، اور اس وقت یہ ناکام ہو جاتی ہے۔ setTimeout مین لوپ سے - اس طرح، کوڈ پچھلے تکرار کو چھوڑے بغیر مین لوپ تکرار میں داخل ہوا۔ ایک لامحدود لوپ پر دوبارہ لکھا اور emscripten_sleep، اور mutexes کے ساتھ مسائل رک گئے. کوڈ اور بھی زیادہ منطقی ہو گیا ہے - آخر کار، درحقیقت، میرے پاس کوئی ایسا کوڈ نہیں ہے جو اگلا اینیمیشن فریم تیار کرے - پروسیسر بس کچھ حساب کرتا ہے اور اسکرین کو وقتاً فوقتاً اپ ڈیٹ کیا جاتا ہے۔ تاہم، مسائل وہیں نہیں رکے: بعض اوقات Qemu پر عمل درآمد بغیر کسی استثناء یا غلطی کے خاموشی سے ختم ہو جاتا ہے۔ اس وقت میں نے اسے ترک کر دیا، لیکن، آگے دیکھتے ہوئے، میں کہوں گا کہ مسئلہ یہ تھا: کورٹین کوڈ، حقیقت میں، استعمال نہیں کرتا setTimeout (یا کم از کم اتنی بار نہیں جتنا آپ سوچ سکتے ہیں): فنکشن emscripten_yield آسانی سے غیر مطابقت پذیر کال پرچم سیٹ کرتا ہے۔ پوری بات یہ ہے کہ emscripten_coroutine_next غیر مطابقت پذیر فنکشن نہیں ہے: اندرونی طور پر یہ پرچم کو چیک کرتا ہے، اسے دوبارہ ترتیب دیتا ہے اور کنٹرول کو وہاں منتقل کرتا ہے جہاں اس کی ضرورت ہوتی ہے۔ یعنی اسٹیک کا فروغ وہیں ختم ہو جاتا ہے۔ مسئلہ یہ تھا کہ استعمال کے بعد مفت کی وجہ سے، جو اس وقت ظاہر ہوا جب کورٹین پول اس حقیقت کی وجہ سے غیر فعال ہو گیا تھا کہ میں نے موجودہ کورٹین بیک اینڈ سے کوڈ کی ایک اہم لائن کاپی نہیں کی تھی، فنکشن qemu_in_coroutine سچ واپس آیا جب حقیقت میں اسے جھوٹا ہونا چاہئے تھا۔ اس سے ایک کال آئی emscripten_yieldجس کے اوپر اسٹیک پر کوئی نہیں تھا۔ emscripten_coroutine_next، اسٹیک بہت اوپر تک کھل گیا، لیکن نہیں۔ setTimeoutجیسا کہ میں پہلے ہی کہہ چکا ہوں، نمائش نہیں کی گئی۔

جاوا اسکرپٹ کوڈ جنریشن

اور یہاں، درحقیقت، وعدہ کیا گیا ہے "کیما بنایا ہوا گوشت واپس کرنا۔" واقعی نہیں۔ بلاشبہ، اگر ہم براؤزر میں Qemu، اور اس میں Node.js چلاتے ہیں، تو قدرتی طور پر، Qemu میں کوڈ بنانے کے بعد ہمیں مکمل طور پر غلط جاوا اسکرپٹ ملے گا۔ لیکن پھر بھی، کسی قسم کی ریورس تبدیلی۔

پہلے، کیمو کے کام کرنے کے طریقہ کے بارے میں تھوڑا سا۔ براہ کرم مجھے فوراً معاف کر دیں: میں ایک پیشہ ور Qemu ڈویلپر نہیں ہوں اور میرے نتائج کچھ جگہوں پر غلط ہو سکتے ہیں۔ جیسا کہ وہ کہتے ہیں، "طالب علم کی رائے کا استاد کی رائے، پیانو کے محوریات اور عام فہم سے ہم آہنگ ہونا ضروری نہیں ہے۔" Qemu میں معاون مہمان فن تعمیر کی ایک خاص تعداد ہے اور ہر ایک کے لیے ایک ڈائرکٹری ہے جیسے target-i386. تعمیر کرتے وقت، آپ کئی مہمان فن تعمیرات کے لیے معاونت کی وضاحت کر سکتے ہیں، لیکن نتیجہ صرف کئی بائنریز ہو گا۔ مہمان فن تعمیر کو سپورٹ کرنے کا کوڈ، بدلے میں، کچھ اندرونی Qemu آپریشنز تیار کرتا ہے، جسے TCG (Tiny Code Generator) پہلے سے ہی میزبان فن تعمیر کے لیے مشین کوڈ میں بدل دیتا ہے۔ جیسا کہ ٹی سی جی ڈائرکٹری میں واقع ریڈمی فائل میں بتایا گیا ہے، یہ اصل میں ایک باقاعدہ سی کمپائلر کا حصہ تھا، جسے بعد میں جے آئی ٹی کے لیے ڈھال لیا گیا۔ لہذا، مثال کے طور پر، اس دستاویز کے لحاظ سے ٹارگٹ آرکیٹیکچر اب مہمان فن تعمیر نہیں ہے، بلکہ ایک میزبان فن تعمیر ہے۔ کسی موقع پر، ایک اور جزو نمودار ہوا - ٹائنی کوڈ انٹرپریٹر (TCI)، جو کسی مخصوص میزبان فن تعمیر کے لیے کوڈ جنریٹر کی عدم موجودگی میں کوڈ (تقریباً ایک جیسی اندرونی کارروائیوں) کو چلانا چاہیے۔ درحقیقت، جیسا کہ اس کی دستاویزات میں کہا گیا ہے، یہ مترجم ہمیشہ JIT کوڈ جنریٹر کے طور پر کارکردگی کا مظاہرہ نہیں کر سکتا، نہ صرف رفتار کے لحاظ سے، بلکہ معیار کے لحاظ سے بھی۔ اگرچہ مجھے یقین نہیں ہے کہ اس کی وضاحت مکمل طور پر متعلقہ ہے۔

سب سے پہلے میں نے ایک مکمل TCG بیک اینڈ بنانے کی کوشش کی، لیکن سورس کوڈ اور بائی کوڈ ہدایات کی مکمل طور پر واضح تفصیل میں جلدی سے الجھ گیا، اس لیے میں نے TCI ترجمان کو لپیٹنے کا فیصلہ کیا۔ اس سے کئی فوائد حاصل ہوئے:

  • کوڈ جنریٹر کو لاگو کرتے وقت، آپ ہدایات کی تفصیل پر نہیں بلکہ مترجم کوڈ کو دیکھ سکتے ہیں
  • آپ ہر ٹرانسلیشن بلاک کا سامنا کرنے والے فنکشنز نہیں بنا سکتے، بلکہ، مثال کے طور پر، صرف سوویں عمل کے بعد
  • اگر تیار کردہ کوڈ میں تبدیلی آتی ہے (اور ایسا لگتا ہے کہ لفظ پیچ والے ناموں کے ساتھ افعال کو دیکھتے ہوئے یہ ممکن ہے)، مجھے تیار کردہ جے ایس کوڈ کو باطل کرنے کی ضرورت ہوگی، لیکن کم از کم میرے پاس اسے دوبارہ تخلیق کرنے کے لیے کچھ ہوگا

تیسرے نکتے کے بارے میں، مجھے یقین نہیں ہے کہ پہلی بار کوڈ کے نفاذ کے بعد پیچ ​​کرنا ممکن ہے، لیکن پہلے دو نکات کافی ہیں۔

ابتدائی طور پر، کوڈ ایک بڑے سوئچ کی صورت میں اصل بائی کوڈ ہدایات کے پتے پر تیار کیا گیا تھا، لیکن پھر، ایم اسکرپٹن، جنریٹڈ جے ایس کی اصلاح اور دوبارہ لوٹنے کے بارے میں مضمون کو یاد کرتے ہوئے، میں نے مزید انسانی کوڈ بنانے کا فیصلہ کیا، خاص طور پر تجرباتی طور پر یہ پتہ چلا کہ ترجمہ بلاک میں داخل ہونے کا واحد نقطہ اس کا آغاز ہے۔ کچھ دیر کے بعد ہمارے پاس ایک کوڈ جنریٹر تھا جو ifs کے ساتھ کوڈ تیار کرتا تھا (اگرچہ لوپس کے بغیر)۔ لیکن بدقسمتی سے، یہ کریش ہو گیا، جس سے یہ پیغام دیا گیا کہ ہدایات کچھ غلط لمبائی کی تھیں۔ مزید یہ کہ اس تکرار کی سطح پر آخری ہدایت تھی۔ brcond. ٹھیک ہے، میں بار بار آنے والی کال سے پہلے اور بعد میں اس ہدایات کی نسل میں ایک جیسی چیک شامل کروں گا اور... ان میں سے ایک پر بھی عمل نہیں ہوا، لیکن اسسٹ سوئچ کے بعد بھی وہ ناکام رہے۔ آخر میں، جنریٹڈ کوڈ کا مطالعہ کرنے کے بعد، میں نے محسوس کیا کہ سوئچ کے بعد، موجودہ انسٹرکشن کی طرف پوائنٹر کو اسٹیک سے دوبارہ لوڈ کیا جاتا ہے اور ممکنہ طور پر تیار کردہ JavaScript کوڈ کے ذریعے اسے اوور رائٹ کیا جاتا ہے۔ اور اس طرح یہ نکلا۔ بفر کو ایک میگا بائٹ سے دس تک بڑھانے سے کچھ حاصل نہیں ہوا، اور یہ واضح ہو گیا کہ کوڈ جنریٹر حلقوں میں چل رہا ہے۔ ہمیں یہ چیک کرنا تھا کہ ہم موجودہ ٹی بی کی حدود سے باہر تو نہیں گئے، اور اگر ہم نے ایسا کیا تو مائنس کے نشان کے ساتھ اگلی ٹی بی کا پتہ جاری کریں تاکہ ہم پھانسی جاری رکھ سکیں۔ اس کے علاوہ، یہ مسئلہ حل کرتا ہے "اگر بائیک کوڈ کا یہ ٹکڑا تبدیل ہو گیا ہے تو کون سے تخلیق کردہ فنکشنز کو باطل کر دیا جائے؟" - صرف اس فنکشن کو جو اس ترجمے کے بلاک سے مطابقت رکھتا ہے کو باطل کرنے کی ضرورت ہے۔ ویسے، اگرچہ میں نے کرومیم میں ہر چیز کو ڈیبگ کیا ہے (چونکہ میں فائر فاکس استعمال کرتا ہوں اور تجربات کے لیے الگ براؤزر استعمال کرنا میرے لیے آسان ہے)، فائر فاکس نے 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"]

حاصل يہ ہوا

لہذا، کام ابھی تک مکمل نہیں ہوا ہے، لیکن میں اس طویل المدتی تعمیر کو خفیہ طور پر تکمیل تک پہنچاتے ہوئے تھک گیا ہوں۔ اس لیے، میں نے فیصلہ کیا کہ جو کچھ میرے پاس ہے اسے شائع کروں۔ کوڈ جگہوں پر تھوڑا خوفناک ہے، کیونکہ یہ ایک تجربہ ہے، اور یہ پہلے سے واضح نہیں ہے کہ کیا کرنے کی ضرورت ہے۔ شاید، پھر یہ Qemu کے کچھ اور جدید ورژن کے اوپر عام جوہری کمٹ جاری کرنے کے قابل ہے۔ اس دوران، گیتا میں بلاگ کی شکل میں ایک دھاگہ ہے: ہر ایک "سطح" کے لیے جو کم از کم کسی نہ کسی طرح گزر چکا ہے، روسی زبان میں ایک تفصیلی تبصرہ شامل کیا گیا ہے۔ درحقیقت، یہ مضمون بڑی حد تک نتیجہ کو دوبارہ بیان کرنے والا ہے۔ git log.

آپ یہ سب آزما سکتے ہیں۔ یہاں (ٹریفک سے بچو)

پہلے سے کیا کام کر رہا ہے:

  • x86 ورچوئل پروسیسر چل رہا ہے۔
  • مشین کوڈ سے جاوا اسکرپٹ تک جے آئی ٹی کوڈ جنریٹر کا ورکنگ پروٹو ٹائپ موجود ہے۔
  • دیگر 32 بٹ گیسٹ آرکیٹیکچرز کو جمع کرنے کے لیے ایک ٹیمپلیٹ موجود ہے: ابھی آپ لوڈنگ کے مرحلے پر براؤزر میں MIPS فن تعمیر کے منجمد ہونے کے لیے لینکس کی تعریف کر سکتے ہیں۔

آپ اور کیا کر سکتے ہیں۔

  • ایمولیشن کو تیز کریں۔ جے آئی ٹی موڈ میں بھی ایسا لگتا ہے کہ یہ ورچوئل x86 کے مقابلے میں آہستہ چل رہا ہے (لیکن ممکنہ طور پر بہت سے ایمولیٹڈ ہارڈویئر اور آرکیٹیکچرز کے ساتھ ایک پورا Qemu موجود ہے)
  • ایک عام انٹرفیس بنانے کے لیے - سچ کہوں تو، میں ایک اچھا ویب ڈویلپر نہیں ہوں، اس لیے فی الحال میں نے معیاری ایم اسکرپٹن شیل کو بہترین طریقے سے دوبارہ بنایا ہے۔
  • مزید پیچیدہ Qemu فنکشنز - نیٹ ورکنگ، VM منتقلی وغیرہ شروع کرنے کی کوشش کریں۔
  • یوپیڈی: آپ کو اپنی چند پیش رفت اور بگ رپورٹس Emscripten upstream پر جمع کروانے کی ضرورت ہوگی، جیسا کہ Qemu کے پچھلے پورٹرز اور دیگر پروجیکٹس نے کیا تھا۔ میرے کام کے حصے کے طور پر Emscripten میں اپنی شراکت کو واضح طور پر استعمال کرنے کے قابل ہونے پر ان کا شکریہ۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں