Qemu.js ak sipò JIT: ou ka toujou vire filange a bak

Sa gen kèk ane Fabrice Bellard ekri pa jslinux se yon Emulation PC ekri nan JavaScript. Apre sa te gen omwen plis Virtuel x86. Men, tout nan yo, jan mwen konnen, yo te entèprèt, pandan y ap Qemu, ekri pi bonè pa menm Fabrice Bellard la, ak, pwobableman, nenpòt ki respekte pwòp tèt ou modèn Emulation, sèvi ak konpilasyon JIT nan kòd envite nan kòd sistèm lame. Li te sanble m 'ke li te tan yo aplike travay opoze a an relasyon ak sa a ke navigatè rezoud: JIT konpilasyon nan kòd machin nan JavaScript, pou ki li te sanble pi lojik nan pò Qemu. Li ta sanble, poukisa Qemu, gen emulateur ki pi senp ak itilizatè-zanmitay - menm VirtualBox la, pou egzanp - enstale ak travay. Men, Qemu gen plizyè karakteristik enteresan

  • sous louvri
  • kapasite pou travay san yon chofè nwayo
  • kapasite pou travay nan mòd entèprèt
  • sipò pou yon gwo kantite achitekti lame ak envite

Konsènan twazyèm pwen an, mwen kapab kounye a eksplike ke an reyalite, nan mòd TCI, se pa enstriksyon yo machin envite tèt yo ki entèprete, men bytecode a jwenn nan men yo, men sa a pa chanje sans nan - yo nan lòd yo bati ak kouri. Qemu sou yon achitekti nouvo, si w gen chans, yon du C ase - ekri yon dèlko kòd ka ranvwaye.

Epi, koulye a, apre dezan nan brikolaj lantèman ak kòd sous Qemu nan tan lib mwen an, yon pwototip k ap travay parèt, nan ki ou ka deja kouri, pou egzanp, Kolibri OS.

Ki sa ki Emscripten

Sèjousi, anpil konpilateur te parèt, rezilta final la se JavaScript. Gen kèk, tankou Type Script, yo te orijinèlman gen entansyon yo dwe pi bon fason yo ekri pou entènèt la. An menm tan an, Emscripten se yon fason pou pran kòd ki egziste deja C oswa C++ epi konpile li nan yon fòm navigatè-lizib. Sou paj sa a Nou te kolekte anpil pò nan pwogram ki byen koni: isit laPou egzanp, ou ka gade nan PyPy - nan chemen an, yo reklame yo deja gen JIT. An reyalite, se pa tout pwogram yo ka tou senpleman konpile epi kouri nan yon navigatè - gen yon nimewo karakteristik, ki ou dwe sipòte, sepandan, jan enskripsyon an sou menm paj la di "Emscripten ka itilize pou konpile prèske nenpòt pòtab Kòd C/C++ nan JavaScript". Sa vle di, gen yon kantite operasyon ki pa defini konpòtman dapre estanda a, men anjeneral travay sou x86 - pou egzanp, aksè ki pa aliye nan varyab, ki se jeneralman entèdi sou kèk achitekti. An jeneral , Qemu se yon pwogram kwa-platfòm ak , mwen te vle kwè, epi li pa deja genyen yon anpil nan konpòtman endefini - pran li epi konpile, Lè sa a, tinker yon ti kras ak JIT - epi w ap fini! Men, se pa sa a. ka...

Premye eseye

Anjeneral pale, mwen pa premye moun ki vini ak lide pòtaj Qemu nan JavaScript. Te gen yon kesyon yo te poze sou fowòm ReactOS la si sa te posib lè l sèvi avèk Emscripten. Menm pi bonè, te gen rimè ke Fabrice Bellard te fè sa pèsonèlman, men nou te pale sou jslinux, ki, jan mwen konnen, se jis yon tantativ manyèlman reyalize ase pèfòmans nan JS, e li te ekri nan grate. Apre sa, Virtual x86 te ekri - sous yo te afiche pou li, epi, jan sa di, pi gwo "reyalis" nan imitasyon an te fè li posib yo sèvi ak SeaBIOS kòm firmwèr. Anplis de sa, te gen omwen yon tantativ pò Qemu lè l sèvi avèk Emscripten - mwen te eseye fè sa socketpair, men devlopman, jan mwen konprann, te jele.

Se konsa, li ta sanble, isit la se sous yo, isit la se Emscripten - pran li epi konpile. Men, gen tou bibliyotèk sou ki Qemu depann, ak bibliyotèk sou ki bibliyotèk sa yo depann, elatriye, e youn nan yo se libffi, ki glib depann sou. Te gen rimè sou entènèt la ke te gen youn nan koleksyon an gwo nan pò nan bibliyotèk pou Emscripten, men li te yon jan kanmenm difisil yo kwè: premye, li pa te gen entansyon yo dwe yon nouvo du, dezyèmman, li te twò ba-nivo yon. bibliyotèk jis ranmase, epi konpile nan JS. Epi li pa jis yon kesyon de foure asanble - pwobableman, si ou tòde li, pou kèk konvansyon apèl ou ka jenere agiman ki nesesè yo sou chemine a epi rele fonksyon an san yo pa yo. Men, Emscripten se yon bagay ki difisil: yo nan lòd yo fè kòd la pwodwi sanble abitye nan navigatè JS motè optimize a, gen kèk ke trik nouvèl yo itilize. An patikilye, sa yo rele relooping la - yon dèlko kòd lè l sèvi avèk LLVM IR resevwa ak kèk enstriksyon tranzisyon abstrè eseye rkree si plauzib, bouk, elatriye. Oke, ki jan agiman yo pase nan fonksyon an? Natirèlman, kòm agiman nan fonksyon JS, se sa ki, si sa posib, pa nan chemine a.

Nan kòmansman an te gen yon lide tou senpleman ekri yon ranplasman pou libffi ak JS epi kouri tès estanda, men nan fen a mwen te konfonn sou ki jan yo fè dosye header mwen yo pou yo ta travay ak kòd ki egziste deja - kisa mwen ka fè, jan yo di, "Èske travay yo tèlman konplèks "Èske nou sòt konsa?" Mwen te oblije pò libffi nan yon lòt achitekti, se konsa pale - erezman, Emscripten gen tou de makro pou asanble inline (nan Javascript, wi - byen, kèlkeswa achitekti a, kidonk asanblaj la), ak kapasite nan kouri kòd ki te pwodwi sou vole. An jeneral, apre yo fin brikolaj ak fragman libffi ki depann de platfòm pou kèk tan, mwen te resevwa kèk kòd konpilasyon epi kouri li sou premye tès mwen te rankontre. Nan sipriz mwen, tès la te reyisi. Sezi pa jeni mwen an - pa gen blag, li te travay depi premye lansman an - mwen, toujou pa kwè je m ', te ale nan gade nan kòd la ki kapab lakòz ankò, evalye ki kote yo fouye pwochen. Isit la mwen te ale nwa pou yon dezyèm fwa - bagay la sèlman fonksyon mwen te fè te ffi_call - sa a rapòte yon apèl siksè. Pa te gen okenn apèl tèt li. Se konsa, mwen te voye premye demann mwen an, ki te korije yon erè nan tès la ki klè pou nenpòt elèv Olympiad - nimewo reyèl pa ta dwe konpare kòm a == b e menm ki jan a - b < EPS - Ou bezwen sonje tou modil la, otreman 0 pral vin trè egal a 1/3... An jeneral, mwen te vini ak yon pò sèten nan libffi, ki pase tès ki pi senp yo, ak ki glib se konpile - Mwen deside li ta nesesè, mwen pral ajoute li pita. Gade pi devan, mwen pral di ke, jan li te tounen soti, du a pa t 'menm enkli fonksyon libffi nan kòd final la.

Men, jan mwen te deja di, gen kèk limit, ak nan mitan itilizasyon gratis nan divès kalite konpòtman endefini, yo te kache yon karakteristik ki pi dezagreyab - JavaScript pa konsepsyon pa sipòte multithreading ak memwa pataje. Nan prensip, sa a ka anjeneral menm rele yon bon lide, men se pa pou pòtaj kòd ki gen achitekti mare nan C fil. Anjeneral pale, Firefox ap fè eksperyans ak sipòte travayè pataje, ak Emscripten gen yon aplikasyon pthread pou yo, men mwen pa t 'vle depann sou li. Mwen te oblije tou dousman derasinen multithreading nan kòd Qemu a - se sa ki, chèche konnen ki kote fil yo ap kouri, deplase kò a nan bouk la kouri nan fil sa a nan yon fonksyon separe, epi rele fonksyon sa yo youn pa youn nan bouk prensipal la.

Dezyèm eseye

Nan kèk pwen, li te vin klè ke pwoblèm nan te toujou la, e ke azar pouse beki nan kòd la pa ta mennen nan okenn bon. Konklizyon: nou bezwen yon jan kanmenm sistematize pwosesis pou ajoute beki. Se poutèt sa, vèsyon 2.4.1, ki te fre nan epòk sa a, te pran (pa 2.5.0, paske, ki moun ki konnen, pral gen pinèz nan nouvo vèsyon an ki poko te kenbe, epi mwen gen ase nan pwòp pinèz mwen yo. ), ak premye bagay la se te reekri li san danje thread-posix.c. Oke, se sa ki an sekirite: si yon moun te eseye fè yon operasyon ki mennen nan bloke, fonksyon an te imedyatman rele abort() - Natirèlman, sa a pa t 'rezoud tout pwoblèm yo nan yon fwa, men omwen li te yon jan kanmenm pi bèl pase tou dousman resevwa done konsistan.

An jeneral, opsyon Emscripten yo trè itil nan pòtaj kòd nan JS -s ASSERTIONS=1 -s SAFE_HEAP=1 - yo trape kèk kalite konpòtman endefini, tankou apèl nan yon adrès ki pa aliye (ki pa ditou ki konsistan avèk kòd la pou etalaj tape tankou HEAP32[addr >> 2] = 1) oswa rele yon fonksyon ki gen move kantite agiman.

By wout la, erè aliyman yo se yon pwoblèm separe. Kòm mwen te deja di, Qemu gen yon backend entèpretasyon "dejenere" pou jenerasyon kòd TCI (ti entèprèt kòd), ak bati ak kouri Qemu sou yon nouvo achitekti, si w gen chans, yon du C ase. "si ou gen chans". Mwen pa t gen chans, epi li te tounen soti ke TCI itilize aksè ki pa aliye lè analize bytecode li yo. Sa vle di, sou tout kalite ARM ak lòt achitekti ak aksè nesesèman nivo, Qemu konpile paske yo gen yon backend nòmal TCG ki jenere kòd natif natal, men si TCI pral travay sou yo se yon lòt kesyon. Sepandan, jan li te tounen soti, dokiman TCI a klèman endike yon bagay ki sanble. Kòm yon rezilta, apèl fonksyon pou lekti ki pa aliye yo te ajoute nan kòd la, ki te dekouvri nan yon lòt pati nan Qemu.

Destriksyon pil

Kòm yon rezilta, aksè ki pa aliye nan TCI te korije, yo te kreye yon bouk prensipal ki an vire rele processeur a, RCU ak kèk lòt ti bagay. Se konsa, mwen lanse Qemu ak opsyon an -d exec,in_asm,out_asm, ki vle di ke ou bezwen di ki blòk nan kòd yo te egzekite, epi tou nan moman an nan emisyon yo ekri ki kòd envite te, ki kòd lame te vin (nan ka sa a, bytecode). Li kòmanse, egzekite plizyè blòk tradiksyon, ekri mesaj debogaj mwen te kite ke RCU pral kòmanse kounye a epi... aksidan. abort() andedan yon fonksyon free(). Pa brikolaj ak fonksyon an free() Nou jere yo chèche konnen ke nan tèt la nan blòk pil pil, ki bay manti nan uit bytes anvan memwa a atribye ba, olye pou yo gwosè a blòk oswa yon bagay ki sanble, te gen fatra.

Destriksyon pil wòch la - ki jan bèl ... Nan yon ka konsa, gen yon remèd itil - soti nan (si sa posib) sous yo menm, rasanble yon binè natif natal epi kouri li anba Valgrind. Apre kèk tan, binè a te pare. Mwen lanse li ak opsyon yo menm - li aksidan menm pandan inisyalizasyon, anvan aktyèlman rive nan ekzekisyon. Li dezagreyab, nan kou - aparamman, sous yo pa t 'egzakteman menm bagay la tou, ki se pa etone, paske configured scouted soti opsyon yon ti kras diferan, men mwen gen Valgrind - premye mwen pral ranje ensèk sa a, ak Lè sa a, si mwen gen chans. , orijinal la ap parèt. Mwen kouri menm bagay la anba Valgrind... Y-y-y, y-y-y, uh-uh, li te kòmanse, te ale nan inisyalizasyon nòmalman epi li te deplase sou pase ensèk orijinal la san yo pa yon sèl avètisman sou aksè memwa kòrèk, nou pa mansyone sou tonbe. Lavi, jan yo di, pa t prepare m pou sa a - yon pwogram ekraze sispann ekraze lè yo te lanse anba Walgrind. Ki sa li te ye se yon mistè. Ipotèz mwen an se ke yon fwa nan vwazinaj enstriksyon aktyèl la apre yon aksidan pandan inisyalizasyon, gdb te montre travay. memset-a ak yon konsèy valab lè l sèvi avèk swa mmx, oswa xmm anrejistre, Lè sa a, petèt li te yon kalite erè aliyman, byenke li toujou difisil a kwè.

Oke, Valgrind sanble pa ede isit la. Ak isit la bagay ki pi degoutan yo te kòmanse - tout bagay sanble yo menm kòmanse, men aksidan pou rezon absoliman enkoni akòz yon evènman ki te kapab rive dè milyon de enstriksyon de sa. Pou yon tan long, li pa t 'menm klè ki jan yo apwòch. Nan fen a, mwen toujou te oblije chita epi debug. Enprime sa ki te reekri header la te montre ke li pa sanble ak yon nimewo, men pito yon kalite done binè. Epi, gade ak gade, yo te jwenn fisèl binè sa a nan dosye BIOS la - se sa ki, kounye a li te posib yo di ak yon konfyans rezonab ke li te yon debòde tanpon, e li menm klè ke li te ekri nan tanpon sa a. Oke, Lè sa a, yon bagay tankou sa a - nan Emscripten, erezman, pa gen okenn randomization nan espas adrès la, pa gen okenn twou nan li tou, kidonk, ou ka ekri yon kote nan mitan an nan kòd la pwodiksyon done pa pointeur soti nan dènye lansman an, gade nan done yo, gade nan konsèy la, epi, si li pa te chanje, jwenn manje pou reflechi. Se vre, li pran yon koup de minit pou konekte apre nenpòt chanjman, men kisa ou ka fè? Kòm yon rezilta, yo te jwenn yon liy espesifik ki kopye BIOS la soti nan tanpon tanporè a nan memwa envite - epi, tout bon, pa te gen ase espas nan tanpon an. Jwenn sous adrès tanpon etranj sa a te lakòz yon fonksyon qemu_anon_ram_alloc nan dosye oslib-posix.c - lojik la te gen sa a: pafwa li ka itil aliman adrès la nan yon paj gwo 2 MB nan gwosè, pou sa nou pral mande. mmap premye yon ti kras plis, ak Lè sa a, nou pral retounen depase an avèk èd la munmap. Men, si aliyman sa yo pa obligatwa, Lè sa a, nou pral endike rezilta a olye pou yo 2 MB getpagesize() - mmap li ap toujou bay yon adrès ki aliye... Se konsa, nan Emscripten mmap jis rele malloc, men nan kou li pa aliman sou paj la. An jeneral, yon ensèk ki fristre m 'pou yon koup de mwa te korije pa yon chanjman nan de liy.

Karakteristik nan fonksyon apèl

Epi, koulye a processeur a ap konte yon bagay, Qemu pa aksidan, men ekran an pa vire sou, ak processeur a byen vit antre nan bouk, jije pa pwodiksyon an. -d exec,in_asm,out_asm. Yon ipotèz parèt: entèwonp revèy (oswa, an jeneral, tout entèwonp) pa rive. Ak tout bon, si ou devise entèwonp yo soti nan asanble natif natal la, ki pou kèk rezon te travay, ou jwenn yon foto ki sanble. Men, sa a pa t 'repons la nan tout: yon konparezon nan tras yo bay ak opsyon ki anwo a te montre ke trajectoire yo ekzekisyon diverge trè bonè. Isit la li dwe di ke konparezon nan sa ki te anrejistre lè l sèvi avèk lans la emrun pwodiksyon debogaj ak pwodiksyon an nan asanble natif natal la se pa yon pwosesis konplètman mekanik. Mwen pa konnen egzakteman ki jan yon pwogram kap kouri nan yon navigatè konekte ak emrun, men kèk liy nan pwodiksyon an vire soti yo dwe rearanje, kidonk diferans lan nan diferans lan se poko yon rezon ki fè yo asime ke trajectoire yo te divèje. An jeneral, li te vin klè ke selon enstriksyon yo ljmpl gen yon tranzisyon nan adrès diferan, ak bytecode ki te pwodwi a se fondamantalman diferan: youn gen yon enstriksyon yo rele yon fonksyon asistan, lòt la pa fè sa. Apre google enstriksyon yo ak etidye kòd la ki tradui enstriksyon sa yo, li te vin klè ke, premyèman, imedyatman anvan li nan rejis la. cr0 te fè yon anrejistreman - tou lè l sèvi avèk yon asistan - ki chanje processeur a nan mòd pwoteje, ak dezyèmman, ke vèsyon an js pa janm chanje nan mòd pwoteje. Men, reyalite a se ke yon lòt karakteristik nan Emscripten se repiyans li yo tolere kòd tankou aplikasyon an nan enstriksyon yo call nan TCI, ki nenpòt pwent fonksyon rezilta nan kalite long long f(int arg0, .. int arg9) - fonksyon yo dwe rele ak kantite kòrèk agiman yo. Si règ sa a vyole, tou depann de anviwònman yo debogaj, pwogram nan pral swa aksidan (ki se bon) oswa rele fonksyon an mal nan tout (ki pral tris debogaj). Genyen tou yon twazyèm opsyon - pèmèt jenerasyon an nan anbalaj ki ajoute / retire agiman, men nan total sa yo anbalaj pran anpil espas, malgre lefèt ke an reyalite mwen sèlman bezwen yon ti kras plis pase yon santèn anbalaj. Sa a pou kont li trè tris, men te vin tounen yon pwoblèm ki pi grav: nan kòd la pwodwi nan fonksyon yo wrapper, agiman yo te konvèti ak konvèti, men pafwa fonksyon an ak agiman yo pwodwi pa te rele - byen, jis tankou nan aplikasyon libffi mwen an. Sa vle di, kèk èd tou senpleman pa te egzekite.

Erezman, Qemu gen lis machin-lizib nan moun k'ap ede nan fòm lan nan yon dosye header tankou

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

Yo itilize yo byen komik: premye, makro yo redefini nan fason ki pi ra DEF_HELPER_n, ak Lè sa a vire sou helper.h. Nan limit ke makro a elaji nan yon inisyalize estrikti ak yon vigil, ak Lè sa a, yo defini yon etalaj, ak olye pou yo eleman - #include <helper.h> Kòm yon rezilta, mwen finalman te gen yon chans eseye bibliyotèk la nan travay pyparsing, epi yo te ekri yon script ki jenere egzakteman sa yo anbalaj pou egzakteman fonksyon yo pou yo bezwen yo.

Se konsa, apre sa processeur a te sanble travay. Li sanble se paske ekran an pa janm te inisyalize, byenke memtest86 + te kapab kouri nan asanble natif natal la. Isit la li nesesè pou klarifye ke kòd Qemu blòk I/O ekri nan coroutines. Emscripten gen pwòp aplikasyon li trè difisil, men li toujou bezwen sipòte nan kòd Qemu a, epi ou ka debug processeur a kounye a: Qemu sipòte opsyon. -kernel, -initrd, -append, ak ki ou ka bòt Linux oswa, pou egzanp, memtest86+, san yo pa itilize aparèy blòk ditou. Men, isit la nan pwoblèm nan: nan asanble natif natal la yon moun te kapab wè pwodiksyon nwayo Linux nan konsole a ak opsyon an. -nographic, epi pa gen okenn pwodiksyon soti nan navigatè a nan tèminal la soti nan kote li te lanse emrun, pa t 'vini. Sa vle di, li pa klè: processeur a pa travay oswa pwodiksyon grafik la pa travay. Lè sa a, li te panse mwen tann yon ti kras. Li te tounen soti ke "prosesè a pa dòmi, men tou senpleman kliyote tou dousman," epi apre apeprè senk minit nwayo a jete yon pakèt moun sou konsole a epi li kontinye pann. Li te vin klè ke processeur a, an jeneral, travay, epi nou bezwen fouye nan kòd la pou travay ak SDL2. Malerezman, mwen pa konnen ki jan yo sèvi ak bibliyotèk sa a, kidonk nan kèk kote mwen te oblije aji o aza. Nan kèk pwen, liy paralèl la te klere sou ekran an sou yon background ble, ki sijere kèk panse. Nan fen a, li te tounen soti ke pwoblèm nan te ke Qemu louvri plizyè fenèt vityèl nan yon sèl fenèt fizik, ant ki ou ka chanje lè l sèvi avèk Ctrl-Alt-n: li travay nan bati natif natal, men se pa nan Emscripten. Apre debarase m de fenèt ki pa nesesè lè l sèvi avèk opsyon -monitor none -parallel none -serial none ak enstriksyon yo fòse redesine ekran an antye sou chak ankadreman, tout bagay toudenkou te travay.

Coroutines

Se konsa, emulasyon nan navigatè a ap travay, men ou pa ka kouri anyen enteresan yon sèl-diskèt ladan l, paske pa gen okenn blòk I/O - ou bezwen aplike sipò pou coroutines. Qemu deja gen plizyè backends coroutine, men akòz nati JavaScript ak dèlko kòd Emscripten, ou pa ka jis kòmanse jungle pil. Li ta sanble ke "tout bagay ale, yo te retire lacho a," men devlopè Emscripten yo te deja pran swen tout bagay. Sa a se aplike byen komik: se pou yo rele yon apèl fonksyon tankou sa a sispèk emscripten_sleep ak plizyè lòt lè l sèvi avèk mekanis Asyncify la, osi byen ke konsèy konsèy ak apèl nan nenpòt fonksyon kote youn nan de ka anvan yo ka rive pi lwen pile a. Epi, koulye a, anvan chak apèl sispèk, nou pral chwazi yon kontèks async, epi imedyatman apre apèl la, nou pral tcheke si yon apèl asynchrone te fèt, epi si li gen, nou pral sove tout varyab lokal yo nan kontèks async sa a, endike ki fonksyon. transfere kontwòl nan lè nou bezwen kontinye ekzekisyon, epi sòti fonksyon aktyèl la. Sa a se kote gen sijè ki abòde pou etidye efè a gaspiye — pou bezwen kontinye ekzekisyon kòd apre retounen soti nan yon apèl asynchrone, du a jenere "souch" nan fonksyon an kòmanse apre yon apèl sispèk - tankou sa a: si gen n apèl sispèk, Lè sa a, fonksyon an pral elaji yon kote n/2 fwa - sa a toujou, si se pa Kenbe nan tèt ou ke apre chak apèl potansyèlman asynchrone, ou bezwen ajoute ekonomize kèk varyab lokal nan fonksyon orijinal la. Imedyatman, mwen menm te oblije ekri yon script senp nan Python, ki, ki baze sou yon seri fonksyon patikilyèman twò itilize ki swadizan "pa pèmèt asynchrony pase nan tèt yo" (ki vle di, pwomosyon pil ak tout sa mwen jis dekri pa fè sa. travay nan yo), endike apèl atravè endikasyon nan ki fonksyon yo ta dwe inyore pa du a pou ke fonksyon sa yo pa konsidere kòm asynchrone. Lè sa a, dosye JS ki poko gen 60 MB yo klèman twòp - an nou di omwen 30. Malgre ke, yon fwa mwen te mete kanpe yon script asanble, ak aksidantèlman jete opsyon yo linker, nan mitan ki te -O3. Mwen kouri kòd la pwodwi, ak Chromium manje moute memwa ak aksidan. Lè sa a, mwen aksidantèlman gade nan sa li te ap eseye telechaje ... Oke, ki sa mwen ka di, mwen ta jele tou si yo te mande m 'ak reflechi etidye ak optimize yon 500 + MB Javascript.

Malerezman, chèk yo nan kòd bibliyotèk sipò Asyncify la pa t antyèman zanmitay ak longjmp-s ke yo te itilize nan kòd la processeur vityèl, men apre yon ti plak ki enfim chèk sa yo ak fòs restore kontèks kòm si tout bagay te byen, kòd la te travay. Lè sa a, yon bagay etranj te kòmanse: pafwa chèk nan kòd la senkronizasyon yo te deklanche - sa yo menm ki aksidan kòd la si, dapre lojik nan ekzekisyon, li ta dwe bloke - yon moun te eseye gen tan pwan yon mutex deja kaptire. Erezman, sa a te tounen soti nan pa yon pwoblèm ki lojik nan kòd la serialize - mwen te tou senpleman itilize fonksyonalite estanda bouk prensipal Emscripten bay, men pafwa apèl la asynchrone ta konplètman devlope chemine a, ak nan moman sa a li ta echwe. setTimeout soti nan bouk prensipal la - konsa, kòd la te antre nan iterasyon nan bouk prensipal san yo pa kite iterasyon anvan an. Reekri sou yon bouk enfini ak emscripten_sleep, ak pwoblèm yo ak mutex te sispann. Kòd la menm vin pi lojik - apre tout, an reyalite, mwen pa gen kèk kòd ki prepare pwochen ankadreman animasyon an - processeur a jis kalkile yon bagay ak ekran an detanzantan mete ajou. Sepandan, pwoblèm yo pa t 'sispann la: pafwa Qemu ekzekisyon ta tou senpleman mete fen nan an silans san okenn eksepsyon oswa erè. Nan moman sa a mwen te abandone sou li, men, gade pi devan, mwen pral di ke pwoblèm nan te sa a: kòd la coroutine, an reyalite, pa sèvi ak setTimeout (oswa omwen pa osi souvan ke ou ta ka panse): fonksyon emscripten_yield tou senpleman mete drapo a apèl asynchrone. Pwen an antye se sa emscripten_coroutine_next se pa yon fonksyon asynchrone: intern li tcheke drapo a, reset li epi transfere kontwòl kote li nesesè. Sa vle di, pwomosyon pil la fini la. Pwoblèm lan se te ke akòz itilize-apre-gratis, ki te parèt lè pisin nan coroutine te andikape akòz lefèt ke mwen pa t 'kopiye yon liy enpòtan nan kòd soti nan backend nan coroutine ki deja egziste, fonksyon an qemu_in_coroutine tounen vre lè an reyalite li ta dwe tounen fo. Sa a te mennen nan yon apèl emscripten_yield, pi wo a ki pa t gen pèsonn sou pil la emscripten_coroutine_next, chemine a depliye nan tèt la anpil, men non setTimeout, jan mwen te deja di, pa te ekspoze.

Jenerasyon kòd JavaScript

Ak isit la, an reyalite, se te pwomèt la "retounen vyann mens la tounen." Pa vrèman. Natirèlman, si nou kouri Qemu nan navigatè a, ak Node.js nan li, Lè sa a, natirèlman, apre jenerasyon kòd nan Qemu nou pral jwenn JavaScript konplètman mal. Men, toujou, kèk kalite transfòmasyon ranvèse.

Premyèman, yon ti kras sou fason Qemu travay. Tanpri padone m touswit: Mwen pa yon pwomotè Qemu pwofesyonèl epi konklizyon mwen yo ka erè nan kèk kote. Kòm yo di, "opinyon elèv la pa oblije kowenside ak opinyon pwofesè a, aksyomatik Peano a ak bon sans." Qemu gen yon sèten kantite achitekti envite sipòte ak pou chak gen yon anyè tankou target-i386. Lè w ap bati, ou ka presize sipò pou plizyè achitekti envite, men rezilta a pral jis plizyè binè. Kòd pou sipòte achitekti envite, nan vire, jenere kèk operasyon entèn Qemu, ki TCG a (Tiny Code Generator) deja vire nan kòd machin pou achitekti lame a. Jan sa di nan fichye readme ki sitiye nan anyè tcg, sa a te orijinèlman yon pati nan yon du C regilye, ki te pita adapte pou JIT. Se poutèt sa, pou egzanp, achitekti sib an tèm de dokiman sa a se pa yon achitekti envite, men yon achitekti lame. Nan kèk pwen, yon lòt eleman parèt - Tiny Code Interpreter (TCI), ki ta dwe egzekite kòd (prèske menm operasyon entèn yo) nan absans yon dèlko kòd pou yon achitekti lame espesifik. An reyalite, jan dokiman li yo di, entèprèt sa a pa ka toujou fè kòm byen ke yon dèlko kòd JIT, pa sèlman quantitatively an tèm de vitès, men tou kalitatif. Malgre ke mwen pa sèten ke deskripsyon li se konplètman enpòtan.

Okòmansman, mwen te eseye fè yon backend TCG plen véritable, men byen vit te konfonn nan kòd sous la ak yon deskripsyon pa totalman klè nan enstriksyon bytecode yo, se konsa mwen deside vlope entèprèt la TCI. Sa a te bay plizyè avantaj:

  • lè w ap aplike yon dèlko kòd, ou ta ka gade pa nan deskripsyon enstriksyon yo, men nan kòd entèprèt la
  • ou ka jenere fonksyon pa pou chak blòk tradiksyon rankontre, men, pou egzanp, sèlman apre ekzekisyon santyèm lan
  • si kòd la pwodwi chanje (e sa sanble posib, jije pa fonksyon yo ak non ki gen patch mo a), mwen pral bezwen anile kòd JS pwodwi a, men omwen mwen pral gen yon bagay yo rejenere li soti nan.

Konsènan twazyèm pwen an, mwen pa sèten ke patching posib apre yo fin egzekite kòd la pou premye fwa, men de premye pwen yo ase.

Okòmansman, kòd la te pwodwi nan fòm lan nan yon switch gwo nan adrès la nan enstriksyon bytecode orijinal la, men Lè sa a, sonje atik la sou Emscripten, optimize nan pwodwi JS ak relooping, mwen deside jenere plis kòd imen, espesyalman depi anpirik li te tounen soti ke pwen an antre sèlman nan blòk tradiksyon an se Kòmanse li yo. Pa pi bonè di pase fè, apre yon ti tan nou te gen yon dèlko kòd ki te pwodwi kòd ak ifs (kwake san pasan). Men, move chans, li te fè aksidan, bay yon mesaj ke enstriksyon yo te nan kèk longè kòrèk. Anplis, dènye ansèyman nan nivo retou sa a te brcond. Oke, mwen pral ajoute yon chèk ki idantik nan jenerasyon an nan enstriksyon sa a anvan ak apre apèl la recursive ak ... pa youn nan yo te egzekite, men apre switch la afime yo toujou echwe. Nan fen a, apre yo fin etidye kòd la pwodwi, mwen reyalize ke apre switch la, konsèy la nan enstriksyon aktyèl la rechaje soti nan chemine a epi li se pwobableman ranplase pa kòd JavaScript pwodwi a. Se konsa, li te tounen soti. Ogmante tanpon a soti nan yon megaocte a dis pa t 'mennen nan anyen, epi li te vin klè ke dèlko kòd la te kouri nan ti sèk. Nou te oblije tcheke ke nou pa t ale pi lwen pase limit TB aktyèl la, epi si nou te fè sa, Lè sa a, bay adrès pwochen TB a ak yon siy mwens pou nou te kapab kontinye egzekisyon an. Anplis de sa, sa a rezoud pwoblèm nan "ki fonksyon pwodwi yo ta dwe anile si moso bytecode sa a te chanje?" — se sèlman fonksyon ki koresponn ak blòk tradiksyon sa a ki bezwen anile. By wout la, byenke mwen debogaj tout bagay nan Chromium (depi mwen itilize Firefox epi li pi fasil pou mwen sèvi ak yon navigatè separe pou eksperyans), Firefox te ede m korije enkonpatibilite ak estanda asm.js la, apre sa kòd la te kòmanse travay pi vit nan Kwòm.

Egzanp kòd pwodwi

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

Konklizyon

Kidonk, travay la toujou pa fini, men mwen fatige an kachèt pote konstriksyon sa a alontèm nan pèfeksyon. Se poutèt sa, mwen deside pibliye sa mwen genyen pou kounye a. Kòd la se yon ti kras pè nan kote, paske sa a se yon eksperyans, epi li pa klè davans sa ki bezwen fè. Pwobableman, Lè sa a, li vo bay nòmal atomik komèt sou tèt kèk vèsyon ki pi modèn nan Qemu. Nan entre-temps la, gen yon fil nan Gita a nan yon fòma blog: pou chak "nivo" ki te omwen yon jan kanmenm pase, yo te ajoute yon kòmantè detaye nan Larisi. Aktyèlman, atik sa a se nan yon gwo limit yon rakonte konklizyon an git log.

Ou ka eseye li tout isit la (fè atansyon ak trafik).

Ki sa ki deja travay:

  • x86 vityèl processeur kouri
  • Gen yon pwototip k ap travay nan yon dèlko kòd JIT soti nan kòd machin nan JavaScript
  • Gen yon modèl pou rasanble lòt achitekti envite 32-bit: kounye a ou ka admire Linux pou konjelasyon achitekti MIPS nan navigatè a nan etap chaj la.

Ki lòt bagay ou ka fè

  • Akselere imitasyon. Menm nan mòd JIT li sanble kouri pi dousman pase Virtual x86 (men gen potansyèlman yon Qemu antye ak yon anpil nan pyès ki nan konpitè emule ak achitekti)
  • Pou fè yon koòdone nòmal - franchman, mwen pa yon bon devlopè entènèt, kidonk pou kounye a mwen te refè estanda Emscripten kokiy la jan mwen kapab.
  • Eseye lanse fonksyon Qemu ki pi konplèks - rezo, migrasyon VM, elatriye.
  • UPS: w ap bezwen soumèt kèk devlopman ak rapò ensèk ou yo bay Emscripten en, menm jan ansyen pòtay Qemu ak lòt pwojè yo te fè. Mèsi a yo paske yo te kapab implicitement itilize kontribisyon yo nan Emscripten kòm yon pati nan travay mwen an.

Sous: www.habr.com

Add nouvo kòmantè