Qemu.js бо дастгирии JIT: шумо ба ҳар ҳол метавонед мурғро ба ақиб гардонед

Чанд сол пеш Фабрис Беллард навиштааст аз ҷониби jslinux як эмулятори компютер аст, ки дар JavaScript навишта шудааст. Баъд аз ин, ҳадди аққал бештар буд Virtual x86. Аммо ҳамаи онҳо, то ҷое ки ман медонам, тарҷумон буданд, дар ҳоле ки Qemu, ки хеле пештар аз ҷониби ҳамон Фабрис Беллард навишта шудааст ва эҳтимолан ҳама гуна эмулятори муосири эҳтиромкунанда, JIT-и коди меҳмононро ба рамзи системаи ҳост истифода мебарад. Ба ман чунин менамуд, ки вақти он расидааст, ки вазифаи муқобилро нисбат ба вазифае, ки браузерҳо ҳал мекунанд: Ҷамъоварии JIT коди мошин ба JavaScript, ки барои он бандари Qemu мантиқтарин ба назар мерасид. Чунин ба назар мерасад, ки чаро Qemu, эмуляторҳои соддатар ва барои истифодабарандагон мавҷуданд - ҳамон VirtualBox, масалан - насб ва кор мекунад. Аммо Qemu дорои якчанд хусусиятҳои ҷолиб аст

  • манбаи кушода
  • қобилияти кор кардан бидуни драйвери ядро ​​​​
  • қобилияти кор кардан дар реҷаи тарҷумон
  • дастгирии шумораи зиёди меъмории ҳам мизбон ва ҳам меҳмонон

Дар робита ба нуқтаи сеюм, ман ҳоло метавонам фаҳмонам, ки дар асл, дар ҳолати TCI на худи дастурҳои мошини меҳмонон, балки байткоди аз онҳо гирифташуда тафсир карда мешаванд, аммо ин моҳиятро тағир намедиҳад - барои сохтан ва иҷро кардан Qemu дар меъмории нав, агар шумо хушбахт бошед, компилятори C кофӣ аст - навиштани генератори кодро ба таъхир андохтан мумкин аст.

Ва акнун, пас аз ду соли оромона бо коди сарчашмаи Qemu дар вақти холии ман, як прототипи корӣ пайдо шуд, ки дар он шумо аллакай метавонед, масалан, Kolibri OS -ро иҷро кунед.

Emscripten чист

Дар айни замон, бисёр компиляторҳо пайдо шуданд, ки натиҷаи ниҳоии онҳо JavaScript мебошад. Баъзеҳо, ба монанди Type Script, дар ибтидо барои беҳтарин роҳи навиштан барои веб пешбинӣ шуда буданд. Ҳамзамон, Emscripten як роҳи гирифтани рамзи мавҷудаи C ё C++ ва тартиб додани он ба шакли барои браузер хондашаванда мебошад. Дар ин саҳифа Мо бисёр бандарҳои барномаҳои маъруфро ҷамъ овардем: дар ин ҷоМасалан, шумо метавонед ба PyPy нигаред - дар омади гап, онҳо даъво доранд, ки аллакай JIT доранд. Дар асл, на ҳар як барномаро дар браузер танҳо тартиб додан ва иҷро кардан мумкин аст - шумораи онҳо вуҷуд дорад Вижагиҳо, ки шумо бояд бо он тоқат кунед, аммо тавре ки навиштаҷоти ҳамон саҳифа мегӯяд: "Emscripten метавонад барои тартиб додани қариб ҳама чиз истифода шавад. ихчамсохт Рамзи C/C++ ба JavaScript". Яъне, як қатор амалиётҳо вуҷуд доранд, ки аз рӯи стандарт рафтори номуайян мебошанд, аммо одатан дар x86 кор мекунанд - масалан, дастрасии мутаносиб ба тағирёбандаҳо, ки умуман дар баъзе архитектураҳо манъ аст. Дар маҷмӯъ. , Qemu як барномаи байнисоҳавӣ аст ва ман мехостам бовар кунам ва он аллакай бисёр рафтори номуайянро дар бар намегирад - онро гиред ва тартиб диҳед, сипас каме бо JIT кор кунед - ва шумо тамом шудед! Аммо ин нест парванда...

Аввал кӯшиш кунед

Умуман, ман аввалин шахсе нестам, ки идеяи интиқоли Qemu ба JavaScript-ро пешниҳод кардааст. Дар форуми ReactOS саволе буд, ки оё ин бо истифода аз Emscripten имконпазир аст. Ҳатто пештар, овозаҳо буданд, ки Фабрис Беллард ин корро шахсан кардааст, аммо мо дар бораи jslinux сӯҳбат мекардем, ки то ҷое ки ман медонам, танҳо кӯшиши ба даст овардани нишондиҳандаҳои кофӣ дар JS аст ва аз сифр навишта шудааст. Баъдтар, Virtual x86 навишта шуд - барои он манбаъҳои номаълум ҷойгир карда шуданд ва тавре ки гуфта шуд, "реализм" -и бештари эмулятсия имкон дод, ки SeaBIOS ҳамчун нармафзор истифода шавад. Илова бар ин, ҳадди аққал як кӯшиши интиқоли Qemu бо истифода аз Emscripten буд - ман кӯшиш кардам, ки ин корро кунам ҷуфти розетка, аммо рушд, то ҷое ки ман фаҳмидам, ях баста буд.

Ҳамин тавр, чунин ба назар мерасад, ки ин ҷо сарчашмаҳо ҳастанд, ин ҷо Emscripten аст - онро гиред ва тартиб диҳед. Аммо китобхонаҳое низ ҳастанд, ки Кему ба онҳо вобаста аст ва китобхонаҳое ҳастанд, ки ин китобхонаҳо ба онҳо вобастаанд ва ғайра ва яке аз онҳо libffi, ба кадом глип вобаста аст. Дар Интернет овозаҳое буданд, ки дар коллексияи бузурги портҳои китобхонаҳои Emscripten мавҷуд аст, аммо ба он бовар кардан душвор буд: аввалан, он барои таҳиягари нав пешбинӣ нашуда буд, дуюм, он хеле паст буд. китобхона барои танҳо гирифтан ва ба JS тартиб додан. Ва ин танҳо як масъалаи варақаҳои васлкунӣ нест - эҳтимол, агар шумо онро печонед, шумо метавонед барои баъзе конвенсияҳои даъват далелҳои заруриро дар стек тавлид кунед ва функсияро бе онҳо даъват кунед. Аммо Emscripten як чизи душвор аст: барои он ки рамзи тавлидшуда ба оптимизатори муҳаррики браузери JS шинос шавад, баъзе ҳиллаҳо истифода мешаванд. Аз ҷумла, ба истилоҳ азнавсозӣ - генератори рамзӣ бо истифода аз LLVM IR-и гирифташуда бо баъзе дастурҳои абстрактии гузариш кӯшиш мекунад, ки ifs, ҳалқаҳо ва ғайраро дубора эҷод кунад. Хуб, аргументҳо ба функсия чӣ гуна интиқол дода мешаванд? Табиист, ки ҳамчун далелҳо ба функсияҳои JS, яъне агар имконпазир бошад, на тавассути стек.

Дар ибтидо идеяе вуҷуд дошт, ки танҳо иваз кардани libffi бо JS нависед ва санҷишҳои стандартиро иҷро кунед, аммо дар ниҳоят ман дар бораи чӣ гуна сохтани файлҳои сарлавҳаи худ ошуфта шудам, то онҳо бо коди мавҷуда кор кунанд - ман чӣ кор карда метавонам, чунон ки мегуянд: «Магар вазифахо ин кадар мураккабанд «Оё мо ин кадар беаклем?». Ман маҷбур будам, ки libffi-ро ба меъмории дигар интиқол диҳам - хушбахтона, Emscripten ҳам макросҳо барои монтажи дохилӣ дорад (дар Javascript, ҳа - хуб, новобаста аз меъморӣ, ҳамин тавр ассемблер) ва қобилияти иҷро кардани коди дар парвоз тавлидшуда. Умуман, пас аз чанд вақт бо порчаҳои libffi вобаста ба платформа, ман якчанд рамзи тартибдодашаванда гирифтам ва онро дар озмоиши аввалине, ки дучор шудам, иҷро кардам. Тааҷҷубовар аст, ки санҷиш бомуваффақият гузашт. Аз нобиғаи ман ҳайрон шудам - ​​шӯхӣ нест, он аз оғози аввал кор кард - ман, то ҳол ба чашмони худ бовар накарда, рафтам, то бори дигар ба коди ҳосилшуда нигоҳ кунам, то дар куҷо кобед. Дар ин ҷо ман бори дуюм чормағз шудам - ​​ягона коре, ки вазифаи ман кард, буд ffi_call - ин занги муваффақ хабар дод. Худи занг набуд. Ҳамин тавр, ман дархости аввалини худро фиристодам, ки хатои санҷишро ислоҳ кард, ки барои ҳар як донишҷӯи олимпиада равшан аст - рақамҳои воқеӣ набояд бо муқоиса карда шаванд. a == b ва ҳатто чӣ тавр a - b < EPS - шумо инчунин бояд модулро дар хотир доред, вагарна 0 ба 1/3 баробар мешавад... Умуман, ман як порти муайяни libffi пайдо кардам, ки аз озмоишҳои соддатарин мегузарад ва бо кадом glib аст. тартиб дода шудааст - Ман қарор додам, ки ин зарур аст, ман онро баъдтар илова мекунам. Ба пеш нигоҳ карда, ман мегӯям, ки, тавре маълум шуд, компилятор ҳатто функсияи libffi-ро дар коди ниҳоӣ дохил накардааст.

Аммо, тавре ки ман аллакай гуфтам, баъзе маҳдудиятҳо мавҷуданд ва дар байни истифодаи ройгони рафтори гуногуни номуайян, як хусусияти ногувортар пинҳон карда шудааст - JavaScript аз рӯи тарроҳӣ мултимедиявиро бо хотираи муштарак дастгирӣ намекунад. Аслан, инро одатан ҳатто идеяи хуб номидан мумкин аст, аммо на барои интиқоли код, ки меъмории он ба риштаҳои C алоқаманд аст. Умуман, Firefox бо дастгирии коргарони муштарак озмуда истодааст ва Emscripten барои онҳо татбиқи pthread дорад, аммо ман намехостам аз он вобаста бошам. Ба ман лозим омад, ки оҳиста-оҳиста решаи мултимедиявиро аз рамзи Qemu решакан кунам - яъне ҷӯед, ки риштаҳо аз куҷо оғоз мешаванд, ҷисми ҳалқаи дар ин ришта иҷрошавандаро ба функсияи алоҳида интиқол диҳед ва чунин функсияҳоро як ба як аз ҳалқаи асосӣ даъват кунед.

Кӯшиши дуюм

Баъзан маълум шуд, ки мушкилидо хануз хам чой доранд ва бепарвоёна ба гирди код кашидани асобачахо ба хайре намебарад. Хулоса: ба мо лозим аст, ки ягон хел раванди илова кардани асобагонро ба низом даровардан лозим аст. Аз ин рӯ, версияи 2.4.1, ки он вақт тару тоза буд, гирифта шуд (на 2.5.0, зеро кӣ медонад, дар версияи нав хатогиҳо хоҳанд буд, ки ҳанӯз дастгир нашудаанд ва ман хатогиҳои худро кофӣ дорам. ), ва аввалин чизе, ки онро бехатар аз нав навиштан буд thread-posix.c. Хуб, яъне ҳамчун бехатар: агар касе кӯшиш кунад, ки амалиётеро анҷом диҳад, ки ба басташавӣ оварда мерасонад, функсия фавран даъват карда шуд abort() - Албатта, ин ҳама мушкилотро якбора ҳал накард, аммо ҳадди аққал ин назар ба оромона гирифтани маълумоти номувофиқ гуворотар буд.

Умуман, имконоти Emscripten барои интиқоли код ба JS хеле муфиданд -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 дорам - аввал ин хатогиро ислоҳ мекунам ва баъд, агар бахтам бошад , нусхаи аслӣ пайдо мешавад. Ман ҳамон чизеро зери Valgrind иҷро мекунам ... Y-y-y, y-y-y, uh-uh, он оғоз шуд, ба таври муқаррарӣ тавассути оғозёбӣ гузашт ва бе огоҳии ягона дар бораи дастрасии нодурусти хотира, дар бораи афтидани он ба хатогии аслӣ гузашт. Ҳаёт, чунон ки мегӯянд, маро ба ин омода накардааст - барномаи шикастхӯрда ҳангоми ба кор даровардани Walgrind аз кор бозмеистад. Он чӣ буд, сирре аст. Фарзияи ман ин аст, ки як бор дар наздикии дастури ҷорӣ пас аз садама ҳангоми оғозёбӣ, gdb кореро нишон дод memset-a бо нишоннамои дуруст бо истифода аз яке mmx, ё xmm ба қайд мегирад, пас шояд ин як навъ хатогии ҳамоҳангӣ буд, гарчанде ки ба он бовар кардан душвор аст.

Хуб, ба назар чунин мерасад, ки Valgrind дар ин ҷо кӯмак намекунад. Ва дар ин ҷо чизи нафратовар оғоз ёфт - ҳама чиз ҳатто оғоз мешавад, аммо бо сабабҳои комилан номаълум аз сабаби ҳодисае, ки метавонист миллионҳо дастурҳои пештар рӯй диҳад, садама мезанад. Муддати дуру дароз хатто маълум набуд, ки чй тавр наздик шудан лозим аст. Дар ниҳоят, ба ман лозим омад, ки нишинам ва ислоҳ кунам. Чоп кардани он чизе, ки сарлавҳа бо он аз нав навишта шуда буд, нишон дод, ки он ба адад монанд нест, балки ба як навъ маълумоти дуӣ монанд аст. Ва инак, ин сатри бинарӣ дар файли BIOS пайдо шуд - яъне ҳоло метавон бо боварии оқилона гуфт, ки он пур аз буфер буд ва ҳатто равшан аст, ки он ба ин буфер навишта шудааст. Хуб, пас чизе монанди ин - дар Emscripten, хушбахтона, тасодуфии фазои суроға вуҷуд надорад, дар он сӯрохиҳо низ вуҷуд надорад, аз ин рӯ шумо метавонед дар ҷое дар мобайни код нависед, то маълумотро бо нишондиҳанда аз оғози охирин бароварда кунед, ба маълумот нигаред, ба нишоннамо нигаред ва , агар он тағир наёфта бошад, барои фикр кардан ғизо гиред. Дуруст аст, ки пас аз ҳама гуна тағирот барои пайвастшавӣ якчанд дақиқа лозим аст, аммо шумо чӣ кор карда метавонед? Дар натиҷа, хати мушаххасе ёфт шуд, ки BIOS-ро аз буфери муваққатӣ ба хотираи меҳмонон нусхабардорӣ мекунад - ва дар ҳақиқат, дар буфер фазои кофӣ набуд. Ҷустуҷӯи манбаи он суроғаи буферии аҷиб боиси функсия гардид qemu_anon_ram_alloc дар файл oslib-posix.c - мантиқ чунин буд: баъзан метавонад мувофиқ кардани суроға ба саҳифаи азими 2 МБ андоза муфид бошад, барои ин мо мепурсем. mmap аввал каме бештар, ва баъд мо бо кӯмаки зиёдатӣ бармегардонем munmap. Ва агар чунин ҳамоҳангсозӣ талаб карда нашавад, мо натиҷаро ба ҷои 2 МБ нишон медиҳем getpagesize() - mmap он ҳоло ҳам суроғаи мувофиқро медиҳад ... Ҳамин тавр, дар Emscripten mmap танҳо занг мезанад malloc, аммо албатта он дар саҳифа мувофиқат намекунад. Умуман, хатое, ки маро дар тӯли якчанд моҳ рӯҳафтода мекард, бо тағир додани он ислоҳ карда шуд двух хатҳо.

Хусусиятҳои функсияҳои занг

Ва ҳоло протсессор чизеро ҳисоб мекунад, Qemu суқут намекунад, аммо экран намегирад ва протсессор аз рӯи натиҷа ба зудӣ ба ҳалқаҳо мегузарад -d exec,in_asm,out_asm. Гипотеза пайдо шуд: таймер халалдор (ё, умуман, ҳама халалҳо) намерасанд. Ва дар ҳақиқат, агар шумо халалҳоро аз маҷлиси ватанӣ, ки бо ягон сабаб кор мекард, кушоед, шумо манзараи шабеҳро ба даст меоред. Аммо ин аслан ҷавоб набуд: муқоисаи пайҳое, ки бо варианти боло дода шудаанд, нишон дод, ки траекторияҳои иҷроиш хеле барвақт аз ҳам ҷудо шудаанд. Дар ин ҷо бояд гуфт, ки муқоисаи он чизе, ки бо истифода аз оғозкунанда сабт шудааст emrun баровардани хатогиҳо бо натиҷаи маҷмӯаи ватанӣ раванди комилан механикӣ нест. Ман аниқ намедонам, ки чӣ гуна барномае, ки дар браузер кор мекунад, ба он пайваст мешавад emrun, аммо баъзе сатрҳо дар баромад дубора ба тартиб дароварда мешаванд, бинобар ин фарқият дар фарқият ҳанӯз барои фарз кардани траекторияҳо аз ҳам ҷудо шуда наметавонад. Умуман маълум шуд, ки мувофики дастур ljmpl гузариш ба суроғаҳои гуногун вуҷуд дорад ва байткоди тавлидшуда ба таври куллӣ фарқ мекунад: яке дастури даъват кардани функсияи ёрирасонро дар бар мегирад, дигаре не. Пас аз ҷустуҷӯи дастурҳо ва омӯхтани коде, ки ин дастурҳоро тарҷума мекунад, маълум шуд, ки, аввалан, фавран пеш аз он дар реестр cr0 сабт карда шуд - инчунин бо истифода аз ёрирасон - ки протсессорро ба ҳолати муҳофизатӣ иваз кард ва дуюм, версияи js ҳеҷ гоҳ ба ҳолати муҳофизатӣ нагузашт. Аммо далел дар он аст, ки хусусияти дигари Emscripten худдории он ба таҳаммули код, ба монанди иҷрои дастурҳо мебошад. 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 дар корутинҳо навишта шудааст. Emscripten татбиқи хеле душвори худро дорад, аммо он ҳанӯз ҳам бояд дар коди Qemu дастгирӣ карда шавад ва шумо метавонед протсессоро ҳоло ислоҳ кунед: Qemu вариантҳоро дастгирӣ мекунад -kernel, -initrd, -append, ки бо он шумо метавонед Linux ё, масалан, memtest86+ -ро бе истифода аз дастгоҳҳои блок комилан бор кунед. Аммо мушкилот дар ин ҷост: дар маҷлиси маҳаллӣ метавон баромади ядрои Linux-ро ба консол бо интихоби дидан мумкин аст. -nographic, ва ҳеҷ натиҷае аз браузер ба терминал аз ҷое, ки он оғоз шуда буд emrun, наомадааст. Яъне, маълум нест: протсессор кор намекунад ё баромади графикӣ кор намекунад. Ва баъд ба сарам омад, ки каме интизор шавам. Маълум шуд, ки «протсессор хоб намекунад, балки оҳиста-оҳиста чашмак мезанад» ва баъд аз тақрибан панҷ дақиқа ядро ​​як қатор паёмҳоро ба консол партофта, овезонро идома додааст. Маълум шуд, ки протсессор умуман кор мекунад ва мо бояд коди кор бо SDL2-ро кобед. Мутаассифона, ман намедонам, ки ин китобхонаро чӣ тавр истифода барам, аз ин рӯ дар баъзе ҷойҳо ман маҷбур будам, ки ба таври тасодуфӣ амал кунам. Дар баъзе мавридҳо, хатти параллел0 дар экран дар заминаи кабуд дурахшид, ки баъзе фикрҳоро пешниҳод мекард. Дар ниҳоят, маълум шуд, ки мушкилот дар он буд, ки Qemu дар як равзанаи физикӣ якчанд тирезаҳои виртуалиро мекушояд, ки шумо метавонед бо истифода аз Ctrl-Alt-n гузаред: он дар сохтори аслӣ кор мекунад, аммо на дар Emscripten. Пас аз халос шудан аз тирезаҳои нолозим бо истифода аз имконоти -monitor none -parallel none -serial none ва дастурҳо оид ба маҷбуран аз нав кашидани тамоми экран дар ҳар як чаҳорчӯба, ҳама чиз ногаҳон кор кард.

Корутинҳо

Ҳамин тавр, эмулятсия дар браузер кор мекунад, аммо шумо наметавонед ягон дискети ҷолибро дар он иҷро кунед, зеро блоки I/O вуҷуд надорад - шумо бояд дастгирии корутинҳоро амалӣ кунед. Qemu аллакай якчанд пуштибонии корутинӣ дорад, аммо аз сабаби табиати JavaScript ва генератори рамзи Emscripten, шумо наметавонед танҳо ба стекҳо шурӯъ кунед. Чунин ба назар мерасад, ки "ҳама чиз нест, гаҷ бардошта мешавад", аммо таҳиягарони Emscripten аллакай ҳама чизро ҳал кардаанд. Ин хеле хандаовар амалӣ карда мешавад: биёед занги функсияро ба монанди ин шубҳанок даъват кунем emscripten_sleep ва чанд нафари дигар бо истифода аз механизми Asyncify, инчунин зангҳои ишоракунанда ва зангҳо ба ҳама гуна функсия, ки яке аз ду ҳолати қаблӣ метавонад дар поёни стек рух диҳад. Ва ҳоло, пеш аз ҳар як занги шубҳанок, мо контексти асинхронӣ интихоб мекунем ва дарҳол пас аз занг, мо тафтиш мекунем, ки оё занги асинхронӣ рух додааст ва агар он дошта бошад, мо ҳама тағирёбандаҳои маҳаллиро дар ин контексти асинхронӣ захира мекунем, нишон диҳед, ки кадом функсия интиқол додани назорат ба он вақте, ки мо бояд иҷроро идома диҳем ва аз функсияи ҷорӣ хориҷ шавем. Ин аст, ки дар он ҷо барои омӯзиши таъсир имконият мавҷуд аст исрофкорй — барои эҳтиёҷоти идомаи иҷроиши код пас аз баргаштан аз занги асинхронӣ, компилятор "нотаҳои" функсияро, ки пас аз занги шубҳанок оғоз мешавад, тавлид мекунад — ба монанди ин: агар n занги шубҳанок мавҷуд бошад, он гоҳ функсия дар ҷое васеъ карда мешавад n/2 маротиба — ин ҳамон аст, агар не Дар хотир доред, ки пас аз ҳар як занги эҳтимолан асинхронӣ, шумо бояд захираи баъзе тағирёбандаҳои маҳаллиро ба функсияи аслӣ илова кунед. Баъдан, ба ман лозим омад, ки ҳатто дар Python як скрипти оддиеро нависам, ки дар асоси маҷмӯи додаҳои функсияҳои аз ҳад зиёд истифодашуда, ки гӯё “асинхронияро ба худ намегузоранд” (яъне таблиғи стек ва ҳама чизҳое, ки ман тавсиф карда будам, намегузоранд) кор дар онҳо), зангҳоро тавассути нишондиҳандаҳо нишон медиҳад, ки дар онҳо функсияҳо бояд аз ҷониби компилятор нодида гирифта шаванд, то ин функсияҳо асинхронӣ ҳисобида нашаванд. Ва он гоҳ файлҳои JS зери 60 МБ бешубҳа аз ҳад зиёданд - биёед бигӯем, ҳадди аққал 30. Ҳарчанд, вақте ки ман як скрипти монтажро насб мекардам ва тасодуфан имконоти пайвандкунандаро, ки дар байни онҳо буданд, партофтам. -O3. Ман рамзи тавлидшударо иҷро мекунам ва Chromium хотираро мехӯрад ва садама мезанад. Пас аз он ман тасодуфан ба он чизе, ки ӯ мехоҳад зеркашӣ кунад, нигоҳ кардам... Хуб, чӣ гуфтан мумкин аст, агар аз ман хоҳиш карда мешуд, ки Javascript-и 500+ МБ-ро бодиққат омӯзам ва оптимизатсия кунам.

Мутаассифона, чекҳо дар коди китобхонаи дастгирии Asyncify комилан дӯстона набуданд longjmp-s, ки дар коди протсессори виртуалӣ истифода мешаванд, аммо пас аз як ямоқи хурде, ки ин чекҳоро ғайрифаъол мекунад ва контекстҳоро маҷбуран барқарор мекунад, гӯё ҳама чиз хуб буд, код кор кард. Ва он гоҳ як чизи аҷибе сар шуд: баъзан санҷишҳо дар коди синхронизатсия оғоз мешуданд - ҳамонҳое, ки кодро вайрон мекунанд, агар мувофиқи мантиқи иҷро он бояд баста шавад - касе кӯшиш кард, ки мутекси аллакай забтшударо гирад. Хушбахтона, ин як мушкили мантиқӣ дар коди силсилавӣ набуд - ман танҳо функсияи стандартии ҳалқаи аз ҷониби Emscripten пешниҳодшударо истифода мебурдам, аммо баъзан занги асинхронӣ стекро пурра мекушояд ва дар он лаҳза он ноком мешавад. setTimeout аз ҳалқаи асосӣ - ҳамин тавр, код бе тарк кардани такрори қаблӣ ба итератсияи ҳалқаи асосӣ ворид шуд. Дар як ҳалқаи беохир аз нав навишт ва emscripten_sleep, ва проблемахои му-тахасснсон катъ гардиданд. Рамз ҳатто мантиқтар шуд - дар асл, ман ягон коде надорам, ки чаҳорчӯбаи аниматсияи навбатиро омода кунад - протсессор танҳо чизеро ҳисоб мекунад ва экран давра ба давра нав карда мешавад. Бо вуҷуди ин, мушкилот бо ин тамом нашуданд: баъзан иҷрои Qemu бе ягон истисно ё хатогӣ хомӯшона қатъ мешуд. Дар он лаҳза ман аз он даст кашидам, аммо ба пеш нигоҳ карда мегӯям, ки мушкилот ин буд: рамзи корутин, воқеан, истифода намекунад setTimeout (ё ҳадди аққал на он қадаре ки шумо фикр мекунед): функсия emscripten_yield танҳо парчами занги асинхронӣ муқаррар мекунад. Гап дар сари он аст emscripten_coroutine_next вазифаи асинхронӣ нест: дар дохили он парчамро тафтиш мекунад, онро аз нав танзим мекунад ва назоратро ба ҷои лозима интиқол медиҳад. Яъне, пешбурди стек дар он ҷо ба охир мерасад. Мушкилот дар он буд, ки бо сабаби истифодаи пас аз озод, ки ҳангоми хомӯш кардани ҳавзи корутин ба сабаби он, ки ман сатри муҳими кодро аз пушти корутини мавҷуда нусхабардорӣ накардаам, пайдо шуд, функсия qemu_in_coroutine ҳақиқӣ баргашт, вақте ки дар асл он бояд бардурӯғ бармегардад. Ин боиси занг шуд emscripten_yield, ки дар болои он касе дар анбор набуд emscripten_coroutine_next, стек то ба боло кушода шуд, аммо не setTimeout, чунон ки гуфтам, намоиш дода нашудааст.

Насли коди JavaScript

Ва дар ин ҷо, воқеан, ваъда дода шудааст, ки "гӯштро баргардонед". На дарвоқеъ. Албатта, агар мо Qemu-ро дар браузер иҷро кунем ва дар он Node.js кор кунем, табиист, ки пас аз тавлиди код дар Qemu мо JavaScript-ро комилан нодуруст мегирем. Аммо ба ҳар ҳол, як навъ табдили баръакс.

Аввалан, каме дар бораи чӣ гуна кор кардани Qemu. Лутфан маро дарҳол бубахшед: ман таҳиягари касбии Qemu нестам ва хулосаҳои ман дар баъзе ҷойҳо хато буда метавонанд. Тавре ки мегӯянд, "фикри донишҷӯ набояд бо андешаи муаллим, аксиоматика ва ақли солим мувофиқат кунад." Qemu дорои шумораи муайяни меъмории меҳмонони дастгирӣшаванда ва барои ҳар як феҳрист ба монанди мавҷуд аст target-i386. Ҳангоми сохтан, шумо метавонед дастгирии якчанд меъмории меҳмононро муайян кунед, аммо натиҷа танҳо якчанд бинарӣ хоҳад буд. Рамзи дастгирии меъмории меҳмонон, дар навбати худ, баъзе амалиёти дохилии Qemu-ро тавлид мекунад, ки TCG (Tiny Code Generator) аллакай ба рамзи мошин барои меъмории мизбон табдил меёбад. Тавре ки дар файли readme, ки дар директорияи tcg ҷойгир аст, гуфта шудааст, ин аслан як ҷузъи компилятори муқаррарии C буд, ки баъдтар барои JIT мутобиқ карда шуд. Аз ин рӯ, масалан, меъмории ҳадаф аз нуқтаи назари ин ҳуҷҷат дигар меъмории меҳмон нест, балки меъмории мизбон аст. Дар баъзе лаҳзаҳо, ҷузъи дигаре пайдо шуд - Tiny Code Interpreter (TCI), ки бояд кодро (тақрибан ҳамон амалиёти дохилиро) дар сурати мавҷуд набудани генератори код барои меъмории мушаххаси мизбон иҷро кунад. Дарвоқеъ, тавре ки дар ҳуҷҷатҳои он гуфта мешавад, ин тарҷумон на ҳамеша метавонад ҳамчун генератори коди JIT на танҳо аз ҷиҳати миқдорӣ, балки аз ҷиҳати сифат низ хуб кор кунад. Ҳарчанд ман боварӣ надорам, ки тавсифи ӯ комилан мувофиқ аст.

Дар аввал ман кӯшиш кардам, ки пуштибонии мукаммали TCG созам, аммо зуд дар коди сарчашма ва тавсифи комилан равшани дастурҳои байткод ошуфта шудам, аз ин рӯ ман қарор додам, ки тарҷумони TCI-ро печонам. Ин як қатор афзалиятҳо дод:

  • Ҳангоми татбиқи генератори код, шумо метавонед на ба тавсифи дастурҳо, балки ба рамзи тарҷумон назар кунед
  • шумо метавонед функсияҳоро на барои ҳар як блоки тарҷумаи дучоршуда, балки, масалан, танҳо пас аз иҷрои садум тавлид кунед.
  • агар рамзи тавлидшуда тағир ёбад (ва ин имконпазир аст, аз рӯи функсияҳое, ки номҳои дорои ямоқи калима доранд), ба ман лозим меояд, ки коди тавлидшудаи JS-ро беэътибор кунам, аммо ҳадди аққал ман чизе дорам, ки онро аз нав тавлид кунам.

Дар робита ба нуқтаи сеюм, ман боварӣ надорам, ки пас аз иҷро кардани код бори аввал имконпазир аст, аммо ду нуқтаи аввал кофӣ аст.

Дар аввал, код дар шакли коммутатори калон дар суроғаи дастури байткоди аслӣ тавлид шуда буд, аммо баъдан, мақоларо дар бораи Emscripten, оптимизатсияи JS-и тавлидшуда ва азнавсозӣ ба ёд оварда, ман қарор додам, ки коди бештари инсониро тавлид кунам, алахусус азбаски он ба таври таҷрибавӣ Маълум шуд, ки ягона нуқтаи воридшавӣ ба блоки тарҷума Оғози он аст. Пас аз чанде мо як тавлидкунандаи код доштем, ки кодро бо ifs тавлид мекард (ҳарчанд бидуни ҳалқаҳо). Аммо бадбахтӣ, он суқут кард ва паём дод, ки дастурҳо дарозии нодуруст доранд. Гузашта аз ин, дастури охирин дар ин сатҳи рекурсия буд brcond. Хуб, ман чеки якхеларо ба тавлиди ин дастур пеш аз занги рекурсивӣ ва баъд аз он илова мекунам ва... на яке аз онҳо иҷро шуд, аммо пас аз гузариши тасдиқ онҳо ҳанӯз ноком шуданд. Дар охир, пас аз омӯхтани рамзи тавлидшуда, ман фаҳмидам, ки пас аз гузариш нишондод ба дастури ҷорӣ аз стек дубора бор карда мешавад ва эҳтимол бо коди тавлидшудаи JavaScript аз нав навишта мешавад. Ва хамин тавр маълум шуд. Аз як мегабайт ба дах зиёд кардани буфер ба хеч чиз оварда нарасонд ва маълум шуд, ки генератори код дар доирахо кор мекунад. Мо бояд тафтиш мекардем, ки мо аз ҳудуди сили ҳозира берун нарафтем ва агар гузаштем, суроғаи бемории силро бо аломати минус бидиҳем, то иҷрои иҷрои онро идома диҳем. Илова бар ин, ин масъаларо ҳал мекунад, ки "агар ин пораи байт код тағир ёбад, кадом функсияҳои тавлидшуда бояд беэътибор шаванд?" — танҳо функсияе, ки ба ин блоки тарҷума мувофиқ аст, бояд беэътибор дониста шавад. Дар омади гап, гарчанде ки ман ҳама чизро дар Chromium ислоҳ кардам (зеро ман Firefox-ро истифода мебарам ва истифодаи браузери алоҳида барои таҷрибаҳо бароям осонтар аст), Firefox ба ман кӯмак кард, ки номувофиқатиро бо стандарти 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 протсессори виртуалӣ кор мекунад
  • Як прототипи кории генератори коди JIT аз рамзи мошин то JavaScript мавҷуд аст
  • Шаблон барои васл кардани дигар меъмории меҳмонони 32-бит вуҷуд дорад: дар айни замон шумо метавонед Linuxро барои ях кардани меъмории MIPS дар браузер дар марҳилаи боркунӣ ҳайронед.

Боз чӣ кор карда метавонед

  • Суръати эмулятсия. Ҳатто дар реҷаи JIT он назар ба Virtual x86 сусттар кор мекунад (аммо эҳтимолан як Qemu тамоми дорои сахтафзор ва меъмории тақлидшуда мавҷуд аст)
  • Барои сохтани интерфейси муқаррарӣ - ошкоро, ман таҳиягари хуби веб нестам, бинобар ин ман то ҳадди имкон қабати стандартии Emscripten-ро аз нав сохтаам.
  • Кӯшиш кунед, ки функсияҳои мураккабтари Qemu - шабакасозӣ, муҳоҷирати VM ва ғайраро оғоз кунед.
  • ИПҶ - ИТТИҲОДИ ПОЧТАИ ҶАҲОНИ: ба шумо лозим меояд, ки чанд таҳаввулот ва гузоришҳои хатогиҳои худро ба Emscripten upstream пешниҳод кунед, чуноне ки портерҳои қаблии Qemu ва дигар лоиҳаҳо кардаанд. Ташаккур ба онҳо, ки тавонистанд саҳми худро дар Emscripten ҳамчун як қисми вазифаи ман истифода баранд.

Манбаъ: will.com

Илова Эзоҳ