Qemu.js b'appoġġ JIT: xorta tista' ddawwar il-kapuljat lura

Ftit snin ilu Fabrice Bellard miktub minn jslinux huwa emulatur tal-PC miktub f'JavaScript. Wara kien hemm mill-inqas aktar Virtwali x86. Iżda kollha kemm huma, sa fejn naf jien, kienu interpreti, filwaqt li Qemu, miktub ħafna qabel mill-istess Fabrice Bellard, u, probabbilment, kwalunkwe emulator modern li jirrispetta lilu nnifsu, juża l-kumpilazzjoni JIT ta 'kodiċi mistieden fil-kodiċi tas-sistema ospitanti. Deherli li kien wasal iż-żmien li timplimenta l-kompitu oppost fir-rigward ta 'dak li jsolvu l-browsers: kumpilazzjoni JIT ta' kodiċi tal-magni f'JavaScript, li għalih deher l-aktar loġiku li port Qemu. Jidher, għaliex Qemu, hemm emulaturi aktar sempliċi u faċli għall-utent - l-istess VirtualBox, pereżempju - installat u jaħdem. Iżda Qemu għandu diversi karatteristiċi interessanti

  • sors miftuħ
  • abbiltà li taħdem mingħajr sewwieq tal-qalba
  • abbiltà li taħdem fil-modalità ta' interpretu
  • appoġġ għal numru kbir ta' arkitetturi kemm ospitanti kif ukoll mistiedna

Rigward it-tielet punt, issa nista 'nispjega li fil-fatt, fil-modalità TCI, mhumiex l-istruzzjonijiet tal-magni mistiedna nfushom li huma interpretati, iżda l-bytecode miksub minnhom, iżda dan ma jbiddilx l-essenza - sabiex tinbena u tmexxi. Qemu fuq arkitettura ġdida, jekk int xortik tajba, kompilatur A C huwa biżżejjed - il-kitba ta 'ġeneratur tal-kodiċi tista' tiġi posposta.

U issa, wara sentejn ta 'tbagħbis bil-mimduda mal-kodiċi tas-sors Qemu fil-ħin liberu tiegħi, deher prototip tax-xogħol, li fih diġà tista' tmexxi, pereżempju, Kolibri OS.

X'inhu Emscripten

Illum il-ġurnata, dehru ħafna kompilaturi, li r-riżultat aħħari tagħhom huwa JavaScript. Xi wħud, bħal Type Script, oriġinarjament kienu maħsuba biex ikunu l-aħjar mod biex tikteb għall-web. Fl-istess ħin, Emscripten huwa mod kif tieħu kodiċi C jew C++ eżistenti u tikkompilaha f'forma li tinqara mill-browser. Fuq Din il-paġna Ġbarna ħafna portijiet ta 'programmi magħrufa sew: hawnPer eżempju, tista 'tħares lejn PyPy - mill-mod, huma jsostnu li diġà għandhom JIT. Fil-fatt, mhux kull programm jista 'jiġi kkumpilat u mmexxi f'browser - hemm numru karatteristiċi, li trid tlaħħaq magħha, madankollu, kif tgħid l-iskrizzjoni fl-istess paġna “Emscripten jista’ jintuża biex jinġabar kważi kull portabbli Kodiċi C/C++ għal JavaScript". Jiġifieri, hemm numru ta' operazzjonijiet li huma mġieba mhux definita skont l-istandard, iżda ġeneralment jaħdmu fuq x86 - pereżempju, aċċess mhux allinjat għal varjabbli, li ġeneralment huwa pprojbit fuq xi arkitetturi. B'mod ġenerali , Qemu huwa programm cross-platform u , ridt nemmen, u ma fihiex diġà ħafna imġieba mhux definita - teħodha u ikkumpila, imbagħad tpinġi ftit bil-JIT - u lest! Imma dak mhux il- każ...

L-ewwel ipprova

B'mod ġenerali, jien mhux l-ewwel persuna li toħroġ bl-idea li ttrasferixxi Qemu għal JavaScript. Kien hemm mistoqsija fuq il-forum ReactOS jekk dan kienx possibbli bl-użu ta 'Emscripten. Anke qabel, kien hemm xnigħat li Fabrice Bellard għamel dan personalment, iżda konna nitkellmu dwar jslinux, li, sa fejn naf jien, huwa biss tentattiv biex tinkiseb manwalment prestazzjoni suffiċjenti f'JS, u nkiteb mill-bidu. Aktar tard, Virtwali x86 inkiteb - sorsi mhux imċaħħda ġew stazzjonati għaliha, u, kif intqal, ir-"realiżmu" akbar tal-emulazzjoni għamilha possibbli li jintuża SeaBIOS bħala firmware. Barra minn hekk, kien hemm mill-inqas tentattiv wieħed biex port Qemu bl-użu ta 'Emscripten - ippruvajt nagħmel dan socketpar, iżda l-iżvilupp, sa fejn nifhem, kien iffriżat.

Allura, jidher, hawn huma s-sorsi, hawn Emscripten - ħuha u kkumpila. Iżda hemm ukoll libreriji li fuqhom jiddependi Qemu, u libreriji li fuqhom jiddependu dawk il-libreriji, eċċ., u waħda minnhom hija libffi, liema glib jiddependi fuq. Kien hemm xnigħat fuq l-Internet li kien hemm wieħed fil-kollezzjoni kbira ta 'portijiet ta' libreriji għal Emscripten, iżda kien b'xi mod diffiċli biex wieħed jemmen: l-ewwelnett, ma kienx maħsub li jkun kompilatur ġdid, it-tieni, kien ta 'livell baxx wisq. librerija biex tiġbor biss, u tikkompila għal JS. U mhix biss kwistjoni ta 'inserzjonijiet ta' assemblaġġ - probabbilment, jekk iddawwarha, għal xi konvenzjonijiet ta 'sejħa tista' tiġġenera l-argumenti meħtieġa fuq il-munzell u ssejjaħ il-funzjoni mingħajrhom. Iżda Emscripten hija ħaġa delikata: sabiex il-kodiċi ġenerat jidher familjari għall-ottimizzatur tal-magna JS tal-browser, jintużaw xi tricks. B'mod partikolari, l-hekk imsejjaħ relooping - ġeneratur tal-kodiċi li juża l-LLVM IR riċevut b'xi struzzjonijiet ta 'tranżizzjoni astratta jipprova jerġa' joħloq ifs plawżibbli, loops, eċċ. Ukoll, kif huma l-argumenti mgħoddija lill-funzjoni? Naturalment, bħala argumenti għall-funzjonijiet JS, jiġifieri, jekk possibbli, mhux permezz tal-munzell.

Fil-bidu kien hemm idea li sempliċement tikteb sostituzzjoni għal libffi b'JS u nagħmel testijiet standard, iżda fl-aħħar tħawwadt dwar kif nagħmel il-fajls tal-header tiegħi sabiex jaħdmu bil-kodiċi eżistenti - x'nista 'nagħmel, kif jgħidu, "Il-kompiti huma daqshekk kumplessi "Are we so stupid?" Kelli ntrasferixxi libffi għal arkitettura oħra, biex ngħidu hekk - fortunatament, Emscripten għandu kemm macros għall-assemblaġġ inline (f'Javascript, yeah - ukoll, tkun xi tkun l-arkitettura, allura l-assemblatur), u l-abbiltà li tmexxi kodiċi ġġenerat fuq il-fly. B'mod ġenerali, wara t-tbagħbis ma 'frammenti libffi dipendenti fuq il-pjattaforma għal xi żmien, sibt xi kodiċi kompilabbli u għamilt fuq l-ewwel test li ltqajt magħha. B'sorpriża tiegħi, it-test kien suċċess. Stordid bil-ġenju tiegħi - l-ebda ċajta, ħadmet mill-ewwel tnedija - jien, għadni ma nemminx għajnejja, erġajt mort inħares lejn il-kodiċi li rriżultat, biex nevalwa fejn iħaffer wara. Hawnhekk mort il-ġewż għat-tieni darba - l-unika ħaġa li għamlet il-funzjoni tiegħi kienet ffi_call - dan irrapporta sejħa b'suċċess. Ma kien hemm ebda sejħa nnifisha. Allura bgħatt l-ewwel talba tiegħi tal-ġibda, li kkoreġiet żball fit-test li huwa ċar għal kull student tal-Olympiad - in-numri reali m'għandhomx jitqabblu bħala a == b u anke kif a - b < EPS - trid tiftakar ukoll il-modulu, inkella 0 jirriżulta li jkun ferm ugwali għal 1/3... B'mod ġenerali, ħriġt b'ċertu port ta' libffi, li jgħaddi mill-aktar testijiet sempliċi, u li bih huwa glib miġbura - Iddeċidejt li jkun meħtieġ, ser inżidha aktar tard. B'ħarsa 'l quddiem, se ngħid li, kif irriżulta, il-kompilatur lanqas biss inkluda l-funzjoni libffi fil-kodiċi finali.

Iżda, kif diġà għedt, hemm xi limitazzjonijiet, u fost l-użu ħieles ta 'diversi mġieba mhux definita, karatteristika aktar spjaċevoli ġiet moħbija - JavaScript bid-disinn ma jappoġġjax multithreading b'memorja kondiviża. Fil-prinċipju, dan normalment jista 'saħansitra jissejjaħ idea tajba, iżda mhux għall-porting tal-kodiċi li l-arkitettura tiegħu hija marbuta ma' ħjut C. B'mod ġenerali, Firefox qed jesperimenta bl-appoġġ tal-ħaddiema kondiviżi, u Emscripten għandu implimentazzjoni pthread għalihom, imma jien ma ridtx niddependi fuqha. Kelli bil-mod tneħħi l-multithreading mill-kodiċi Qemu - jiġifieri, issir taf fejn qed jaħdmu l-ħjut, iċċaqlaq il-korp tal-linja li taħdem f'din il-ħajta f'funzjoni separata, u sejjaħ funzjonijiet bħal dawn wieħed wieħed mil-linja prinċipali.

It-tieni attentat

F'xi punt, deher ċar li l-problema kienet għadha hemm, u li l-krozzi bl-addoċċ shoving madwar il-kodiċi ma jwassal għall-ebda ġid. Konklużjoni: għandna bżonn b'xi mod nissistematizzaw il-proċess taż-żieda tal-krozzi. Għalhekk, ittieħdet il-verżjoni 2.4.1, li kienet friska dak iż-żmien (mhux 2.5.0, għax, qatt ma tkun taf, se jkun hemm bugs fil-verżjoni l-ġdida li għadhom ma nqabdux, u għandi biżżejjed minn tiegħi stess bugs), u l-ewwel ħaġa li għamilt kienet niktebha b'mod sikur thread-posix.c. Ukoll, jiġifieri, bħala sigur: jekk xi ħadd ipprova jwettaq operazzjoni li twassal għall-imblukkar, il-funzjoni kienet immedjatament imsejħa abort() - ovvjament, dan ma solvix il-problemi kollha f'daqqa, iżda għall-inqas kien b'xi mod aktar pjaċevoli milli bil-kwiet li tirċievi dejta inkonsistenti.

B'mod ġenerali, l-għażliet Emscripten huma utli ħafna fil-porting tal-kodiċi għal JS -s ASSERTIONS=1 -s SAFE_HEAP=1 - jaqbdu xi tipi ta' mġieba mhux definita, bħal sejħiet għal indirizz mhux allinjat (li xejn mhu konsistenti mal-kodiċi għal arrays ittajpjati bħal HEAP32[addr >> 2] = 1) jew issejjaħ funzjoni b'numru ħażin ta 'argumenti.

Mill-mod, l-iżbalji tal-allinjament huma kwistjoni separata. Kif diġà għedt, Qemu għandu backend interpretattiv "deġenerat" għall-ġenerazzjoni tal-kodiċi TCI (interpretu tal-kodiċi ċkejkna), u biex tibni u tħaddem Qemu fuq arkitettura ġdida, jekk int xortik tajba, kompilatur C huwa biżżejjed. "jekk int xortik tajba". I kien sfortunat, u rriżulta li TCI juża aċċess mhux allinjat meta parsing bytecode tiegħu. Jiġifieri, fuq kull xorta ta 'ARM u arkitetturi oħra b'aċċess neċessarjament livellat, Qemu jikkompila minħabba li għandhom backend TCG normali li jiġġenera kodiċi nattiv, iżda jekk TCI se taħdem fuqhom hija mistoqsija oħra. Madankollu, kif irriżulta, id-dokumentazzjoni tat-TCI indikat b'mod ċar xi ħaġa simili. Bħala riżultat, sejħiet ta 'funzjoni għal qari mhux allinjat ġew miżjuda mal-kodiċi, li ġew skoperti f'parti oħra ta' Qemu.

Qerda tal-borġ

Bħala riżultat, l-aċċess mhux allinjat għal TCI ġie kkoreġut, inħoloq linja prinċipali li mbagħad imsejjaħ il-proċessur, RCU u xi affarijiet żgħar oħra. U għalhekk inniedi Qemu bl-għażla -d exec,in_asm,out_asm, Li jfisser li għandek bżonn tgħid liema blokki ta 'kodiċi qed jiġu esegwiti, u wkoll fil-ħin tax-xandir biex tikteb liema kodiċi tal-mistieden kien, liema kodiċi ospitanti sar (f'dan il-każ, bytecode). Jibda, jesegwixxi diversi blokki ta’ traduzzjoni, jikteb il-messaġġ ta’ debugging li ħallejt li RCU issa se jibda u... jiġġarraf abort() ġewwa funzjoni free(). Billi tbiddel il-funzjoni free() Irnexxielna nsib li fil-header tal-blokka tal-borġ, li tinsab fit-tmien bytes ta 'qabel il-memorja allokata, minflok id-daqs tal-blokk jew xi ħaġa simili, kien hemm żibel.

Qerda tal-borġ - kemm ħelu... F'każ bħal dan, hemm rimedju utli - minn (jekk possibbli) l-istess sorsi, għaqqad binarju indiġenu u mexxih taħt Valgrind. Wara xi żmien, il-binarju kien lest. Inniediha bl-istess għażliet - tiġġarraf anke waqt l-inizjalizzazzjoni, qabel ma fil-fatt tilħaq l-eżekuzzjoni. Huwa spjaċevoli, ovvjament - apparentement, is-sorsi ma kinux eżattament l-istess, li mhux sorprendenti, minħabba li kkonfigurat għażliet kemmxejn differenti, imma għandi Valgrind - l-ewwel nirranġa dan il-bug, u mbagħad, jekk jien xortik tajba , se tidher dik oriġinali. Qed inmexxi l-istess ħaġa taħt Valgrind... Y-y-y, y-y-y, uh-uh, beda, għadda mill-inizjalizzazzjoni b'mod normali u għadda mill-bug oriġinali mingħajr twissija waħda dwar aċċess għall-memorja mhux korrett, biex ma nsemmux dwar waqgħat. Il-ħajja, kif jgħidu, ma ppreparatnix għal dan - programm ta 'ħabta jieqaf jiġġarraf meta tnieda taħt Walgrind. Dak li kien huwa misteru. L-ipoteżi tiegħi hija li darba fil-viċinanza tal-istruzzjoni attwali wara ħabta waqt l-inizjalizzazzjoni, gdb wera xogħol memset-a b'pointer validu li juża jew mmx, jew xmm reġistri, allura forsi kien xi tip ta 'żball ta' allinjament, għalkemm għadu diffiċli biex temmen.

Tajjeb, Valgrind ma jidhirx li jgħin hawn. U hawn bdiet l-aktar ħaġa disgusting - kollox jidher li saħansitra jibda, iżda jiġġarraf għal raġunijiet assolutament mhux magħrufa minħabba avveniment li seta' ġara miljuni ta 'struzzjonijiet ilu. Għal żmien twil, lanqas kien ċar kif approċċ. Fl-aħħar, xorta kelli noqgħod bilqiegħda u niddibaggja. L-istampar b'liema l-header inkiteb mill-ġdid wera li ma kienx qisu numru, iżda pjuttost xi tip ta 'data binarja. U, ara u ara, din is-sekwenza binarja nstabet fil-fajl tal-BIOS - jiġifieri, issa kien possibbli li wieħed jgħid b'kunfidenza raġonevoli li kien overflow tal-buffer, u huwa saħansitra ċar li nkiteb f'dan il-buffer. Ukoll, allura xi ħaġa bħal din - f'Emscripten, fortunatament, m'hemm l-ebda randomizzazzjoni ta 'l-ispazju ta' l-indirizzi, m'hemm l-ebda toqob fiha lanqas, sabiex tkun tista 'tikteb x'imkien fin-nofs tal-kodiċi biex toħroġ dejta b'indikatur mill-aħħar tnedija, ħares lejn id-dejta, ħares lejn il-pointer, u , jekk ma nbidlitx, agħmel ħsieb. Veru, tieħu ftit minuti biex torbot wara kwalunkwe bidla, imma x'tista 'tagħmel? Bħala riżultat, instabet linja speċifika li kkupjat il-BIOS mill-buffer temporanju għall-memorja tal-mistieden - u, tabilħaqq, ma kienx hemm biżżejjed spazju fil-buffer. Is-sejba tas-sors ta 'dak l-indirizz tal-buffer stramba rriżultat f'funzjoni qemu_anon_ram_alloc fil-fajl oslib-posix.c - il-loġika kien hemm din: xi kultant jista 'jkun utli li allinjat l-indirizz ma' paġna enormi ta 'daqs ta' 2 MB, għal dan aħna nistaqsu mmap l-ewwel ftit aktar, u mbagħad nirritornaw l-eċċess bl-għajnuna munmap. U jekk tali allinjament ma jkunx meħtieġ, allura aħna se nindikaw ir-riżultat minflok 2 MB getpagesize() - mmap xorta se tagħti indirizz allinjat... Mela f'Emscripten mmap sejħiet biss malloc, iżda ovvjament ma tallinjax fuq il-paġna. B'mod ġenerali, bug li frustratni għal ftit xhur ġie kkoreġut b'bidla двух linji.

Karatteristiċi tal-funzjonijiet tas-sejħa

U issa l-proċessur qed jgħodd xi ħaġa, Qemu ma jikkraxxjax, iżda l-iskrin ma jixgħelx, u l-proċessur malajr imur f'linji, ġġudikati mill-output -d exec,in_asm,out_asm. Ħarġet ipoteżi: l-interruzzjonijiet tat-timer (jew, b'mod ġenerali, l-interruzzjonijiet kollha) ma jaslux. U tabilħaqq, jekk ħoll l-interruzzjonijiet mill-assemblaġġ nattiv, li għal xi raġuni ħadem, ikollok stampa simili. Iżda din ma kinitx it-tweġiba għal kollox: paragun tat-traċċi maħruġa bl-għażla ta 'hawn fuq wera li t-trajettorji ta' eżekuzzjoni diverġew kmieni ħafna. Hawnhekk għandu jingħad li l-paragun ta 'dak li ġie rreġistrat bl-użu tal-lanċjar emrun output ta 'debugging bl-output ta' l-assemblaġġ nattiv mhuwiex proċess kompletament mekkaniku. Ma nafx eżatt kif programm li jaħdem fil-browser jgħaqqad emrun, iżda xi linji fl-output jirriżultaw li jiġu rranġati mill-ġdid, għalhekk id-differenza fid-diff għadha mhix raġuni biex wieħed jassumi li t-trajettorji diverġew. B'mod ġenerali, deher ċar li skond l-istruzzjonijiet ljmpl hemm tranżizzjoni għal indirizzi differenti, u l-bytecode iġġenerat huwa fundamentalment differenti: wieħed fih struzzjoni biex issejjaħ funzjoni helper, l-ieħor le. Wara li ħarġet fuq Google l-istruzzjonijiet u studjat il-kodiċi li jittraduċi dawn l-istruzzjonijiet, deher ċar li, l-ewwelnett, immedjatament qabel fir-reġistru cr0 sar reġistrazzjoni - bl-użu wkoll ta' helper - li qaleb il-proċessur għal mod protett, u t-tieni, li l-verżjoni js qatt ma qalbet għall-modalità protetta. Iżda l-fatt hu li karatteristika oħra ta 'Emscripten hija r-riluttanza tiegħu li jittollera kodiċi bħall-implimentazzjoni ta' struzzjonijiet call f'TCI, li kwalunkwe punter tal-funzjoni jirriżulta fit-tip long long f(int arg0, .. int arg9) - il-funzjonijiet għandhom jissejħu bin-numru korrett ta' argumenti. Jekk din ir-regola tinkiser, skont is-settings tad-debugging, il-programm jew jikkraxxja (li huwa tajjeb) jew isejjaħ il-funzjoni ħażina (li tkun imdejjaq biex tiddibaggja). Hemm ukoll it-tielet għażla - tippermetti l-ġenerazzjoni ta 'wrappers li jżidu / ineħħu argumenti, iżda b'kollox dawn il-wrappers jieħdu ħafna spazju, minkejja l-fatt li fil-fatt għandi bżonn biss ftit aktar minn mitt wrappers. Dan waħdu huwa imdejjaq ħafna, iżda rriżulta li kien hemm problema aktar serja: fil-kodiċi ġġenerat tal-funzjonijiet tat-tgeżwir, l-argumenti ġew konvertiti u kkonvertiti, iżda xi drabi l-funzjoni bl-argumenti ġġenerati ma kinitx imsejħa - ukoll, bħal f' implimentazzjoni libffi tiegħi. Jiġifieri, xi helpers sempliċement ma ġewx eżegwiti.

Fortunatament, Qemu għandu listi ta 'helpers li jinqraw mill-magni fil-forma ta' fajl header simili

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

Jintużaw pjuttost umoristiċi: l-ewwel, macros huma definiti mill-ġdid bl-aktar mod stramb DEF_HELPER_n, u mbagħad jixgħel helper.h. Sal-punt li l-makro hija estiża f'inizjalizzatur ta 'struttura u virgola, u mbagħad tiġi definita firxa, u minflok elementi - #include <helper.h> Bħala riżultat, fl-aħħar kelli ċ-ċans li nipprova l-librerija fuq ix-xogħol pyparsing, u nkiteb skript li jiġġenera eżattament dawk it-tgeżwir eżattament għall-funzjonijiet li għalihom huma meħtieġa.

U għalhekk, wara li l-proċessur deher li jaħdem. Jidher li huwa minħabba li l-iskrin qatt ma ġie inizjalizzat, għalkemm memtest86 + kien kapaċi jaħdem fl-assemblaġġ nattiv. Hawnhekk huwa meħtieġ li jiġi ċċarat li l-kodiċi I/O tal-blokk Qemu huwa miktub f'coroutines. Emscripten għandu l-implimentazzjoni delikata ħafna tiegħu stess, iżda xorta kien jeħtieġ li jiġi appoġġjat fil-kodiċi Qemu, u tista 'tiddibaggja l-proċessur issa: Qemu jappoġġja għażliet -kernel, -initrd, -append, li biha tista 'tibbutja Linux jew, pereżempju, memtest86+, mingħajr ma tuża apparati blokk xejn. Imma hawn il-problema: fl-assemblaġġ nattiv wieħed jista 'jara l-output tal-kernel Linux lill-console bl-għażla -nographic, u l-ebda output mill-browser għat-terminal minn fejn ġie mniedi emrun, ma ġewx. Jiġifieri, mhuwiex ċar: il-proċessur mhux qed jaħdem jew l-output tal-grafika mhux qed jaħdem. U mbagħad ġieli nistenna ftit. Irriżulta li "il-proċessur mhux jorqod, iżda sempliċiment teptip bil-mod," u wara madwar ħames minuti l-qalba tefa 'mazz ta' messaġġi fuq il-console u kompliet tistrieħ. Deher ċar li l-proċessur, b'mod ġenerali, jaħdem, u jeħtieġ li nħaffru fil-kodiċi biex naħdmu ma 'SDL2. Sfortunatament, ma nafx kif nuża din il-librerija, għalhekk f'xi postijiet kelli naġixxi bl-addoċċ. F'xi punt, il-linja parallel0 flashed fuq l-iskrin fuq sfond blu, li ssuġġeriet xi ħsibijiet. Fl-aħħar, irriżulta li l-problema kienet li Qemu tiftaħ diversi twieqi virtwali f'tieqa fiżika waħda, li bejniethom tista 'taqleb billi tuża Ctrl-Alt-n: taħdem fil-bini nattiv, iżda mhux f'Emscripten. Wara li teħles minn twieqi mhux meħtieġa bl-użu ta 'għażliet -monitor none -parallel none -serial none u struzzjonijiet biex tfassal mill-ġdid bil-qawwa l-iskrin kollu fuq kull frejm, kollox ħadem f'daqqa.

Coroutines

Allura, l-emulazzjoni fil-browser taħdem, imma ma tistax tmexxi xi ħaġa interessanti single-floppy fiha, minħabba li m'hemm l-ebda blokk I/O - għandek bżonn timplimenta appoġġ għal coroutines. Qemu diġà għandu diversi backends ta 'coroutine, iżda minħabba n-natura ta' JavaScript u l-ġeneratur tal-kodiċi Emscripten, ma tistax tibda tiggling stacks. Jidher li "kollox spiċċa, il-ġibs qed jitneħħa," iżda l-iżviluppaturi Emscripten diġà ħadu ħsieb kollox. Dan huwa implimentat pjuttost umoristiċi: ejja sejħa funzjoni sejħa bħal din suspettużi emscripten_sleep u bosta oħrajn li jużaw il-mekkaniżmu Asyncify, kif ukoll sejħiet pointer u sejħiet għal kwalunkwe funzjoni fejn wieħed miż-żewġ każijiet preċedenti jista 'jseħħ aktar 'l isfel fil-munzell. U issa, qabel kull sejħa suspettuża, aħna se nagħżlu kuntest asinkroniku, u immedjatament wara s-sejħa, aħna niċċekkjaw jekk seħħitx sejħa asinkronika, u jekk seħħet, se nissejvjaw il-varjabbli lokali kollha f'dan il-kuntest asinkroniku, indika liema funzjoni. biex tittrasferixxi l-kontroll għal meta għandna bżonn inkomplu l-eżekuzzjoni, u noħorġu mill-funzjoni attwali. Dan huwa fejn hemm lok għall-istudju tal-effett ħela — għall-ħtiġijiet ta’ eżekuzzjoni kontinwa tal-kodiċi wara li jirritorna minn sejħa asinkronika, il-kompilatur jiġġenera “stubs” tal-funzjoni li jibdew wara sejħa suspettuża — bħal dan: jekk ikun hemm n sejħiet suspettużi, allura l-funzjoni tiġi estiża x’imkien n/2 darbiet — dan għadu, jekk le Żomm f'moħħok li wara kull sejħa potenzjalment asinkronika, għandek bżonn iżżid issalva xi varjabbli lokali mal-funzjoni oriġinali. Sussegwentement, kelli saħansitra nikteb skript sempliċi f'Python, li, ibbażat fuq sett partikolari ta' funzjonijiet partikolarment użati żżejjed li allegatament "ma jippermettux li l-asinkronija tgħaddi minnha nfushom" (jiġifieri, promozzjoni tal-munzell u dak kollu li għadni kif iddeskrivejt ma jagħmlux dan. jaħdmu fihom), jindika sejħiet permezz ta' indikaturi li fihom il-funzjonijiet għandhom jiġu injorati mill-kompilatur sabiex dawn il-funzjonijiet ma jitqiesux bħala asinkroniċi. U allura l-fajls JS taħt 60 MB huma ċarament wisq - ejja ngħidu mill-inqas 30. Għalkemm, ladarba kont qed inwaqqaf script ta 'assemblaġġ, u aċċidentalment warrabt l-għażliet tal-linker, fosthom -O3. I run-kodiċi ġenerat, u Chromium jiekol il-memorja u crashes. Imbagħad aċċidentalment ħarist lejn dak li kien qed jipprova tniżżel... Tajjeb, x'nista 'ngħid, jien kont ffriżat ukoll kieku ġejt mitlub biex nistudja bil-ħsieb u nottimizza Javascript ta' 500+ MB.

Sfortunatament, il-kontrolli fil-kodiċi tal-librerija ta 'appoġġ Asyncify ma kinux kompletament faċli magħhom longjmp-s li jintużaw fil-kodiċi tal-proċessur virtwali, iżda wara garża żgħira li tiddiżattiva dawn il-kontrolli u tirrestawra bil-qawwa l-kuntesti bħallikieku kollox kien tajjeb, il-kodiċi ħadem. U mbagħad bdiet ħaġa stramba: xi drabi ġew attivati ​​kontrolli fil-kodiċi ta 'sinkronizzazzjoni - l-istess dawk li jikkraxxjaw il-kodiċi jekk, skont il-loġika tal-eżekuzzjoni, għandu jiġi mblukkat - xi ħadd ipprova jaqbad mutex diġà maqbud. Fortunatament, dan irriżulta li ma kienx problema loġika fil-kodiċi serialized - kont sempliċement qed nuża l-funzjonalità standard tal-linja prinċipali pprovduta minn Emscripten, iżda xi drabi s-sejħa asinkronika kienet tinfetaħ kompletament il-munzell, u f'dak il-mument kienet tfalli setTimeout mil-linja prinċipali - għalhekk, il-kodiċi daħal fl-iterazzjoni tal-linja prinċipali mingħajr ma ħalla l-iterazzjoni ta 'qabel. Inkiteb mill-ġdid fuq linja infinita u emscripten_sleep, u l-problemi bil-mutex waqfu. Il-kodiċi saħansitra sar aktar loġiku - wara kollox, fil-fatt, m'għandix xi kodiċi li jipprepara l-qafas ta 'animazzjoni li jmiss - il-proċessur sempliċement jikkalkula xi ħaġa u l-iskrin jiġi aġġornat perjodikament. Madankollu, il-problemi ma waqfux hemm: xi kultant l-eżekuzzjoni ta 'Qemu kienet sempliċement tittermina skiet mingħajr ebda eċċezzjoni jew żbalji. F'dak il-mument rrinunzja għaliha, iżda, inħares 'il quddiem, ngħid li l-problema kienet din: il-kodiċi coroutine, fil-fatt, ma jużax setTimeout (jew għall-inqas mhux ta’ spiss kemm tista’ taħseb): funzjoni emscripten_yield sempliċement jistabbilixxi l-bandiera tas-sejħa asinkronika. Il-punt kollu huwa li emscripten_coroutine_next mhijiex funzjoni asinkronika: internament tiċċekkja l-bandiera, tirresetha u tittrasferixxi l-kontroll fejn tkun meħtieġa. Jiġifieri, il-promozzjoni tal-munzell tispiċċa hemm. Il-problema kienet li minħabba l-użu wara l-ħieles, li deher meta l-grupp tal-coroutine kien diżattivat minħabba l-fatt li ma kkopjajtx linja importanti ta 'kodiċi mill-backend tal-coroutine eżistenti, il-funzjoni qemu_in_coroutine rritornat veru meta fil-fatt imissha rritorna falza. Dan wassal għal sejħa emscripten_yield, li fuqu ma kien hemm ħadd fuq il-munzell emscripten_coroutine_next, il-munzell żvolġi sal-quċċata, iżda le setTimeout, kif diġà għedt, ma kienx esebit.

Ġenerazzjoni tal-kodiċi JavaScript

U hawnhekk, fil-fatt, huwa l-imwiegħed "dawran il-laħam ikkapuljat lura." Mhux ezatt. Naturalment, jekk inħaddmu Qemu fil-browser, u Node.js fih, allura, naturalment, wara l-ġenerazzjoni tal-kodiċi f'Qemu se nġibu JavaScript kompletament żbaljat. Iżda xorta waħda, xi tip ta 'trasformazzjoni inversa.

L-ewwel, ftit dwar kif jaħdem Qemu. Jekk jogħġbok aħfruni mill-ewwel: m'iniex żviluppatur Qemu professjonali u l-konklużjonijiet tiegħi jistgħu jkunu żbaljati f'xi postijiet. Kif jgħidu, "l-opinjoni tal-istudent m'għandhiex għalfejn tikkoinċidi mal-opinjoni tal-għalliem, l-assisjomatika u s-sens komun ta' Peano." Qemu għandu ċertu numru ta 'arkitetturi mistiedna appoġġjati u għal kull wieħed hemm direttorju simili target-i386. Meta tibni, tista 'tispeċifika appoġġ għal diversi arkitetturi mistiedna, iżda r-riżultat se jkun biss diversi binarji. Il-kodiċi biex jappoġġja l-arkitettura mistieden, min-naħa tiegħu, jiġġenera xi operazzjonijiet interni Qemu, li t-TCG (Tiny Code Generator) diġà jbiddel f'kodiċi tal-magni għall-arkitettura ospitanti. Kif iddikjarat fil-fajl readme li jinsab fid-direttorju tcg, dan kien oriġinarjament parti minn kompilatur C regolari, li aktar tard ġie adattat għal JIT. Għalhekk, pereżempju, l-arkitettura fil-mira f'termini ta 'dan id-dokument m'għadhiex arkitettura mistieden, iżda arkitettura ospitanti. F'xi punt, deher komponent ieħor - Tiny Code Interpreter (TCI), li għandu jesegwixxi kodiċi (kważi l-istess operazzjonijiet interni) fin-nuqqas ta 'ġeneratur tal-kodiċi għal arkitettura ospitanti speċifika. Fil-fatt, kif tgħid id-dokumentazzjoni tiegħu, dan l-interpretu jista 'mhux dejjem jaħdem tajjeb daqs ġeneratur tal-kodiċi JIT, mhux biss kwantitattivament f'termini ta' veloċità, iżda wkoll kwalitattivament. Għalkemm m'inix ċert li d-deskrizzjoni tiegħu hija kompletament rilevanti.

Għall-ewwel ippruvajt nagħmel backend TCG sħiħ, iżda malajr tħawwad fil-kodiċi tas-sors u deskrizzjoni mhux kompletament ċara tal-istruzzjonijiet tal-bytecode, għalhekk iddeċidejt li nagħlaq l-interpretu TCI. Dan ta diversi vantaġġi:

  • meta timplimenta ġeneratur tal-kodiċi, tista 'tħares mhux lejn id-deskrizzjoni tal-istruzzjonijiet, iżda lejn il-kodiċi tal-interpretu
  • tista 'tiġġenera funzjonijiet mhux għal kull blokk ta' traduzzjoni li jiltaqgħu magħhom, iżda, pereżempju, biss wara l-mitt eżekuzzjoni
  • jekk il-kodiċi ġġenerat jinbidel (u dan jidher li huwa possibbli, meta wieħed jiġġudika mill-funzjonijiet bl-ismijiet li fihom il-kelma garża), ikolli bżonn ninvalida l-kodiċi JS iġġenerat, iżda tal-inqas ikolli xi ħaġa minn fejn nirriġenera.

Rigward it-tielet punt, m'inix ċert li l-patching huwa possibbli wara li l-kodiċi jiġi esegwit għall-ewwel darba, iżda l-ewwel żewġ punti huma biżżejjed.

Inizjalment, il-kodiċi ġie ġġenerat fil-forma ta 'swiċċ kbir fl-indirizz ta' l-istruzzjoni bytecode oriġinali, iżda mbagħad, ftakar l-artikolu dwar Emscripten, ottimizzazzjoni ta 'JS iġġenerat u relooping, iddeċidejt li tiġġenera aktar kodiċi uman, speċjalment peress li empirikament hija irriżulta li l-uniku punt tad-dħul fil-blokka tat-traduzzjoni huwa l-Bidu tagħha. Mhux aktar kmieni qal milli jsir, wara xi żmien kellna ġeneratur tal-kodiċi li ġġenera kodiċi bl-ifs (għalkemm mingħajr loops). Iżda xorti ħażina, ġġarraf, u ta messaġġ li l-istruzzjonijiet kienu ta 'xi tul mhux korrett. Barra minn hekk, l-aħħar istruzzjoni f'dan il-livell ta' rikorsi kienet brcond. Tajjeb, ser inżid verifika identika mal-ġenerazzjoni ta 'din l-istruzzjoni qabel u wara s-sejħa rikorsiva u... ebda waħda minnhom ma ġiet eżegwita, iżda wara l-iswiċċ tal-asserzjoni xorta fallew. Fl-aħħar, wara li studja l-kodiċi iġġenerat, indunajt li wara l-iswiċċ, il-pointer għall-istruzzjoni attwali jiġi mgħobbi mill-ġdid mill-munzell u probabbilment jinkiteb mill-ġdid mill-kodiċi JavaScript iġġenerat. U għalhekk irriżulta. Żieda fil-buffer minn megabyte wieħed għal għaxra ma wassal għal xejn, u deher ċar li l-ġeneratur tal-kodiċi kien qed jaħdem f'ċrieki. Kellna niċċekkjaw li ma mornax lil hinn mill-konfini tat-TB attwali, u jekk għamilna, imbagħad noħorġu l-indirizz tat-TB li jmiss b'sinjal minus sabiex inkunu nistgħu nkomplu l-eżekuzzjoni. Barra minn hekk, dan isolvi l-problema "liema funzjonijiet iġġenerati għandhom jiġu invalidati jekk din il-biċċa bytecode tkun inbidlet?" — il-funzjoni li tikkorrispondi għal din il-blokka tat-traduzzjoni biss trid tiġi invalidata. Mill-mod, għalkemm debugged kollox fil-Chromium (billi nuża Firefox u huwa aktar faċli għalija li nuża browser separat għall-esperimenti), Firefox għeni nikkoreġi l-inkompatibbiltajiet mal-istandard asm.js, u wara dan il-kodiċi beda jaħdem aktar malajr fl- Kromju.

Eżempju ta' kodiċi ġġenerat

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"]

Konklużjoni

Allura, ix-xogħol għadu mhux lest, imma jien għajjien li b'mod sigriet inġib din il-kostruzzjoni fit-tul għall-perfezzjoni. Għalhekk, iddeċidejt li nippubblika dak li għandi għalissa. Il-kodiċi huwa ftit tal-biża 'f'postijiet, minħabba li dan huwa esperiment, u mhux ċar minn qabel x'għandu jsir. Probabbilment, allura ta 'min joħroġ impenji atomiċi normali fuq xi verżjoni aktar moderna ta' Qemu. Sadanittant, hemm ħajta fil-Gita f'format ta 'blog: għal kull "livell" li mill-inqas ikun għadda b'xi mod, ġie miżjud kummentarju dettaljat bir-Russu. Fil-fatt, dan l-artikolu huwa fil-biċċa l-kbira rakkont tal-konklużjoni git log.

Tista 'tipprova dan kollu hawn (oqgħod attent mit-traffiku).

Dak li diġà qed jaħdem:

  • proċessur virtwali x86 qed jaħdem
  • Hemm prototip ta 'ħidma ta' ġeneratur tal-kodiċi JIT minn kodiċi tal-magni għal JavaScript
  • Hemm mudell għall-assemblaġġ ta' arkitetturi mistiedna oħra ta' 32 bit: issa tista' tammira Linux għall-iffriżar tal-arkitettura MIPS fil-browser fl-istadju tat-tagħbija

X'iktar tista' tagħmel

  • Tħaffef l-emulazzjoni. Anke fil-modalità JIT jidher li jaħdem aktar bil-mod minn Virtual x86 (iżda hemm potenzjalment Qemu sħiħ b'ħafna ħardwer u arkitetturi emulati)
  • Biex tagħmel interface normali - franchement, m'inix żviluppatur tal-web tajjeb, għalhekk għalissa għamilt mill-ġdid il-qoxra standard ta' Emscripten mill-aħjar li nista'
  • Ipprova tniedi funzjonijiet Qemu aktar kumplessi - netwerking, migrazzjoni tal-VM, eċċ.
  • AĠĠORNAMENT: ser ikollok bżonn tissottometti l-ftit żviluppi u rapporti tal-bug tiegħek lil Emscripten upstream, kif għamlu l-porters preċedenti ta 'Qemu u proġetti oħra. Grazzi lilhom talli kapaċi jużaw b'mod impliċitu l-kontribut tagħhom lil Emscripten bħala parti mill-kompitu tiegħi.

Sors: www.habr.com

Żid kumment