Qemu.js nga adunay suporta sa JIT: ang pagpuno mahimo pa nga ibalik

Pipila ka tuig ang milabay Fabrice Bellard gisulat ni jslinux usa ka PC emulator nga gisulat sa JavaScript. Human niana adunay labing menos labaw pa Virtual nga x86. Apan silang tanan, sa akong nahibal-an, mga tighubad, samtang ang Qemu, nga gisulat sa sayo pa sa parehas nga Fabrice Bellard, ug, tingali, bisan unsang nagtahod sa kaugalingon nga modernong emulator, naggamit sa JIT nga paghugpong sa guest code ngadto sa host system code. Para nako, panahon na nga ipatuman ang kaatbang nga buluhaton nga may kalabotan sa usa nga gisulbad sa mga browser: JIT compilation sa machine code ngadto sa JavaScript, diin kini daw labing makataronganon sa port Qemu. Morag, nganong ang Qemu, adunay mas simple ug user-friendly nga mga emulator - parehas nga VirtualBox, pananglitan - na-install ug nagtrabaho. Apan ang Qemu adunay daghang makapaikag nga mga bahin

  • bukas nga tinubdan
  • abilidad sa pagtrabaho nga walay kernel driver
  • abilidad sa pagtrabaho sa interpreter mode
  • suporta alang sa daghang gidaghanon sa host ug bisita nga mga arkitektura

Mahitungod sa ikatulo nga punto, mahimo na nako ipasabut nga sa tinuud, sa TCI mode, dili ang mga panudlo sa guest machine mismo ang gihubad, apan ang bytecode nga nakuha gikan kanila, apan wala kini magbag-o sa esensya - aron matukod ug makadagan. Qemu sa usa ka bag-ong arkitektura, kung swerte ka, igo na ang A C compiler - ang pagsulat sa usa ka code generator mahimong ma-postpone.

Ug karon, pagkahuman sa duha ka tuig nga lingaw nga pag-usisa sa Qemu source code sa akong libre nga oras, usa ka nagtrabaho nga prototype ang nagpakita, diin mahimo ka na nga modagan, pananglitan, Kolibri OS.

Unsa ang Emscripten

Karong panahona, daghang mga compiler ang nagpakita, ang katapusan nga resulta mao ang JavaScript. Ang uban, sama sa Type Script, orihinal nga gituyo aron mahimong labing maayong paagi sa pagsulat alang sa web. Sa samang higayon, ang Emscripten usa ka paagi sa pagkuha sa kasamtangan nga C o C++ code ug pag-compile niini ngadto sa usa ka browser-readable nga porma. Sa kini nga panid Nakakolekta kami og daghang mga pantalan sa iladong mga programa: dinhiPananglitan, mahimo nimong tan-awon ang PyPy - sa ingon, giangkon nila nga naa na sila JIT. Sa tinuud, dili tanan nga programa mahimo’g ma-compile ug modagan sa usa ka browser - adunay usa ka numero mga dagway, nga kinahanglan nimong antoson, bisan pa, ingon nga ang inskripsiyon sa parehas nga panid nag-ingon "Ang Emscripten mahimong magamit sa pag-compile sa hapit bisan unsang portable C/C++ code ngadto sa JavaScript". Sa ato pa, adunay ubay-ubay nga mga operasyon nga dili matino nga kinaiya sumala sa sumbanan, apan kasagaran nagtrabaho sa x86 - pananglitan, wala'y linya nga pag-access sa mga variable, nga sa kasagaran gidili sa pipila ka mga arkitektura. Sa kinatibuk-an , Ang Qemu usa ka cross-platform nga programa ug , Gusto kong motuo, ug wala pa kini naglangkob sa daghang dili matino nga kinaiya - kuhaa kini ug pag-compile, dayon pag-tinker og gamay sa JIT - ug nahuman ka! Apan dili kana ang kaso...

Unang pagsulay

Sa kinatibuk-an, dili ako ang una nga tawo nga nakahunahuna sa pag-port sa Qemu sa JavaScript. Adunay pangutana nga gipangutana sa forum sa ReactOS kung posible ba kini gamit ang Emscripten. Bisan sa sayo pa, adunay mga hungihong nga si Fabrice Bellard mismo ang nagbuhat niini, apan naghisgot kami bahin sa jslinux, nga, sa akong nahibal-an, usa lamang ka pagsulay nga mano-mano nga makab-ot ang igo nga pasundayag sa JS, ug gisulat gikan sa wala. Sa ulahi, gisulat ang Virtual x86 - ang wala mailhi nga mga gigikanan gi-post alang niini, ug, ingon sa giingon, ang labi ka dako nga "realismo" sa pagsundog nagpaposible sa paggamit sa SeaBIOS ingon firmware. Dugang pa, adunay labing menos usa ka pagsulay sa pag-port sa Qemu gamit ang Emscripten - Gisulayan nako kini socketpair, apan ang kalamboan, sa akong nasabtan, nagyelo.

Mao nga, ingon og, ania ang mga gigikanan, ania ang Emscripten - kuhaa kini ug i-compile. Apan adunay usab mga librarya diin ang Qemu nagsalig, ug mga librarya diin ang mga librarya nagsalig, ug uban pa, ug usa niini mao ang libffi, nga glib nagdepende sa. Adunay mga hungihong sa Internet nga adunay usa sa dako nga koleksyon sa mga pantalan sa mga librarya alang sa Emscripten, apan kini lisud tuohan: una, wala kini gituyo nga mahimong bag-ong compiler, ikaduha, kini ubos kaayo nga lebel a librarya nga kuhaon lang, ug i-compile sa JS. Ug dili lamang kini usa ka butang sa pagsal-ot sa asembliya - tingali, kung imong i-twist kini, alang sa pipila nga mga kombensiyon sa pagtawag mahimo nimo nga makamugna ang kinahanglan nga mga argumento sa stack ug tawagan ang function nga wala sila. Apan ang Emscripten usa ka malisud nga butang: aron mahimo nga pamilyar ang nahimo nga code sa browser nga JS engine optimizer, gigamit ang pipila ka mga limbong. Sa partikular, ang gitawag nga relooping - usa ka generator sa code gamit ang nadawat nga LLVM IR nga adunay pipila nga abstract nga mga panudlo sa pagbalhin naningkamot sa paghimo pag-usab sa katuohan nga kung, mga galong, ug uban pa. Aw, giunsa ang mga argumento nga gipasa sa function? Natural, ingon nga mga argumento sa mga gimbuhaton sa JS, kana, kung mahimo, dili pinaagi sa stack.

Sa sinugdanan adunay usa ka ideya nga isulat lamang ang usa ka kapuli sa libffi sa JS ug magpadagan sa mga standard nga pagsulay, apan sa katapusan naglibog ko kung unsaon paghimo sa akong mga file sa header aron sila magtrabaho sa kasamtangan nga code - unsa ang akong mahimo, ingon sila, "Makomplikado ba ang mga buluhaton "Gago ba kaayo ta?" Kinahanglan kong i-port ang libffi sa laing arkitektura, ingnon ta - maayo na lang, ang Emscripten adunay duha ka macros alang sa inline nga asembliya (sa Javascript, oo - maayo, bisan unsa ang arkitektura, mao nga ang assembler), ug ang abilidad sa pagpadagan sa code nga namugna sa langaw. Sa kinatibuk-an, pagkahuman sa pag-tinker sa mga tipik sa libffi nga nagsalig sa platform sa pila ka panahon, nakakuha ako usa ka compilable code ug gipadagan kini sa una nga pagsulay nga akong nakit-an. Sa akong katingala, malampuson ang pagsulay. Nahingangha sa akong henyo - walay komedya, kini nagtrabaho gikan sa unang paglansad - Ako, nga wala gihapon makatuo sa akong mga mata, miadto sa pagtan-aw sa resulta nga code pag-usab, aron sa pagtimbang-timbang kung asa ang sunod nga pagkalot. Dinhi ako nabuang sa ikaduhang higayon - ang bugtong butang nga nahimo sa akong gimbuhaton ffi_call - kini nagreport sa usa ka malampuson nga tawag. Walay tawag mismo. Mao nga gipadala nako ang akong una nga hangyo sa pagbitad, nga nagtul-id sa usa ka sayup sa pagsulay nga klaro sa bisan kinsa nga estudyante sa Olympiad - ang tinuud nga mga numero dili kinahanglan itandi sa a == b ug bisan unsaon a - b < EPS - kinahanglan nimo usab nga hinumdoman ang module, kung dili ang 0 mahimong parehas kaayo sa 1/3 ... Sa kinatibuk-an, nakakuha ako usa ka piho nga pantalan sa libffi, nga gipasa ang pinakasimple nga mga pagsulay, ug kung diin ang glib compiled - Nakahukom ko nga gikinahanglan, idugang ko kini sa ulahi. Sa pagtan-aw sa unahan, akong isulti nga, ingon nga kini nahimo, ang compiler wala gani naglakip sa libffi function sa katapusan nga code.

Apan, sama sa giingon ko na, adunay pipila nga mga limitasyon, ug taliwala sa libre nga paggamit sa lainlaing dili matino nga pamatasan, usa ka labi ka dili maayo nga bahin ang gitago - ang JavaScript sa disenyo wala nagsuporta sa multithreading nga adunay gipaambit nga panumduman. Sa prinsipyo, kini kasagaran matawag nga usa ka maayong ideya, apan dili alang sa porting code kansang arkitektura nahigot sa C thread. Sa kinatibuk-an, ang Firefox nag-eksperimento sa pagsuporta sa gipaambit nga mga trabahante, ug ang Emscripten adunay pthread nga pagpatuman alang kanila, apan dili ko gusto nga magsalig niini. Kinahanglan nakong hinay-hinay nga tangtangon ang multithreading gikan sa Qemu code - nga mao, hibal-i kung diin nagdagan ang mga hilo, ibalhin ang lawas sa loop nga nagdagan sa kini nga hilo ngadto sa usa ka lahi nga function, ug tawagan ang ingon nga mga gimbuhaton nga tagsa-tagsa gikan sa main loop.

Ikaduhang pagsulay

Sa usa ka punto, kini nahimong tin-aw nga ang problema anaa pa, ug nga ang wala tuyoa nga pagduso sa mga saklay sa palibot sa code dili mosangpot sa bisan unsa nga kaayohan. Panapos: kinahanglan natong i-systematize ang proseso sa pagdugang sa crutches. Busa, ang bersyon 2.4.1, nga bag-o pa niadtong panahona, gikuha (dili 2.5.0, tungod kay, wala ka mahibalo, adunay mga bug sa bag-ong bersyon nga wala pa madakpi, ug ako adunay igo sa akong kaugalingon. mga bug), ug ang una nakong gibuhat mao ang pagsulat pag-usab niini nga luwas thread-posix.c. Aw, kana, ingon nga luwas: kung adunay usa nga misulay sa paghimo sa usa ka operasyon nga nagdala sa pag-block, ang function gitawag dayon abort() - siyempre, wala kini makasulbad sa tanan nga mga problema sa usa ka higayon, apan labing menos kini mas makapahimuot kay sa hilom nga pagdawat sa dili managsama nga datos.

Sa kinatibuk-an, ang mga kapilian sa Emscripten makatabang kaayo sa pag-port sa code sa JS -s ASSERTIONS=1 -s SAFE_HEAP=1 - nakuha nila ang pipila ka mga matang sa wala mahibal-an nga pamatasan, sama sa mga tawag sa usa ka wala nahiangay nga adres (nga dili gyud parehas sa code alang sa gi-type nga mga arrays sama sa HEAP32[addr >> 2] = 1) o pagtawag sa usa ka function nga adunay sayop nga gidaghanon sa mga argumento.

Pinaagi sa dalan, ang mga sayup sa pag-align usa ka lahi nga isyu. Sama sa giingon ko na, ang Qemu adunay "degenerate" interpretive backend alang sa code generation TCI (gamay nga code interpreter), ug sa pagtukod ug pagpadagan sa Qemu sa usa ka bag-ong arkitektura, kung ikaw swerte, ang usa ka C compiler igo na. "kung sinuswerte ka". Wala ako'y swerte, ug kini nahimo nga ang TCI naggamit sa dili managsama nga pag-access kung gi-parse ang bytecode niini. Kana mao, sa tanan nga mga matang sa ARM ug uban pang mga arkitektura nga adunay kinahanglan nga lebel nga pag-access, ang Qemu nag-compile tungod kay sila adunay usa ka normal nga backend sa TCG nga nagpatunghag lumad nga code, apan kung ang TCI molihok sa kanila usa pa ka pangutana. Bisan pa, ingon nga kini nahimo, ang dokumentasyon sa TCI tin-aw nga nagpakita sa usa ka butang nga parehas. Ingon usa ka sangputanan, ang mga tawag sa function alang sa dili managsama nga pagbasa gidugang sa code, nga nakit-an sa laing bahin sa Qemu.

Tapok nga pagkaguba

Ingon usa ka sangputanan, ang dili managsama nga pag-access sa TCI gitul-id, usa ka punoan nga loop ang gihimo nga sa baylo gitawag nga processor, RCU ug uban pang gagmay nga mga butang. Ug busa gilusad nako ang Qemu nga adunay kapilian -d exec,in_asm,out_asm, nga nagpasabut nga kinahanglan nimo isulti kung unsang mga bloke sa code ang gipatuman, ug usab sa panahon sa pagsibya aron isulat kung unsa ang code sa bisita, kung unsa ang nahimo nga code sa host (sa kini nga kaso, bytecode). Nagsugod kini, nagpatuman sa daghang mga bloke sa paghubad, nagsulat sa mensahe sa pag-debug nga akong gibiyaan nga magsugod na ang RCU ug ... nahagsa abort() sulod sa usa ka function free(). Pinaagi sa tinkering sa function free() Nahimo namon nga mahibal-an nga sa ulohan sa heap block, nga nahimutang sa walo ka bytes nga nag-una sa gigahin nga panumduman, imbis sa gidak-on sa bloke o susama nga butang, adunay basura.

Ang pagkaguba sa tapok - unsa ka cute... Sa ingon nga kaso, adunay usa ka mapuslanon nga tambal - gikan sa (kung mahimo) sa parehas nga mga gigikanan, pagpundok sa usa ka lumad nga binary ug pagdagan kini sa ilawom sa Valgrind. Human sa pipila ka panahon, ang binary andam na. Gilunsad nako kini nga adunay parehas nga mga kapilian - kini nag-crash bisan sa panahon sa pagsugod, sa wala pa maabot ang pagpatuman. Kini dili makapahimuot, siyempre - dayag, ang mga tinubdan dili eksakto sa sama nga, nga dili ikatingala, tungod kay configure scouted sa gamay lain-laing mga kapilian, apan ako adunay Valgrind - una ako ayuhon kini nga bug, ug unya, kon ako lucky , makita ang orihinal. Gipadagan nako ang parehas nga butang ubos sa Valgrind ... Y-y-y, y-y-y, uh-uh, nagsugod kini, miagi sa initialization nga normal ug mibalhin sa milabay nga orihinal nga bug nga walay usa ka pasidaan mahitungod sa sayop nga pag-access sa memorya, wala pay labot ang mahitungod sa pagkahulog. Ang kinabuhi, ingon sa ilang giingon, wala mag-andam kanako alang niini - ang usa ka nag-crash nga programa mihunong sa pag-crash kung gilunsad ubos sa Walgrind. Unsa kadto usa ka misteryo. Ang akong pangagpas mao nga kausa sa palibot sa karon nga panudlo pagkahuman sa pagkahagsa sa panahon sa pagsugod, ang gdb nagpakita sa trabaho memset-a nga adunay balido nga pointer gamit ang bisan asa mmx, o xmm mga rehistro, unya tingali kini usa ka matang sa sayup sa pag-align, bisan kung lisud pa tuohan.

Okay, si Valgrind daw dili makatabang dinhi. Ug dinhi nagsugod ang labing dulumtanan nga butang - ang tanan ingon og nagsugod pa, apan nahagsa sa hingpit nga wala mahibal-an nga mga hinungdan tungod sa usa ka panghitabo nga mahimo’g nahitabo milyon-milyon nga mga panudlo ang milabay. Sulod sa dugay nga panahon, dili gani klaro kung unsaon pagduol. Sa katapusan, kinahanglan pa kong molingkod ug mag-debug. Ang pag-imprenta kung unsa ang gisulat pag-usab sa header nagpakita nga kini dili sama sa usa ka numero, apan usa ka matang sa binary data. Ug, tan-awa, kini nga binary string nakit-an sa BIOS file - nga mao, karon posible nga isulti uban ang makatarunganon nga pagsalig nga kini usa ka buffer overflow, ug klaro pa nga kini gisulat sa kini nga buffer. Aw, unya usa ka butang nga sama niini - sa Emscripten, maayo na lang, wala’y randomization sa address space, wala usab mga lungag niini, aron mahimo ka magsulat sa usa ka lugar sa tunga-tunga sa code aron ma-output ang data pinaagi sa pointer gikan sa katapusan nga paglansad, tan-awa ang datos, tan-awa ang pointer, ug , kon kini wala mausab, pagkuha og pagkaon alang sa paghunahuna. Tinuod, nagkinahanglan ug pipila ka minuto aron masumpay human sa bisan unsang kausaban, apan unsay imong mahimo? Ingon usa ka sangputanan, usa ka piho nga linya ang nakit-an nga nagkopya sa BIOS gikan sa temporaryo nga buffer hangtod sa panumduman sa bisita - ug, sa tinuud, wala’y igo nga wanang sa buffer. Ang pagpangita sa tinubdan sa katingad-an nga buffer address miresulta sa usa ka function qemu_anon_ram_alloc sa file oslib-posix.c - ang lohika adunay kini: usahay mahimo’g mapuslanon ang pag-align sa adres sa usa ka dako nga panid nga 2 MB ang gidak-on, alang niini kami mangutana mmap una og gamay pa, ug unya atong ibalik ang sobra uban sa tabang munmap. Ug kung dili kinahanglan ang ingon nga pag-align, nan ipakita namon ang resulta imbes nga 2 MB getpagesize() - mmap mohatag gihapon kini ug aligned address... Busa sa Emscripten mmap tawag lang malloc, apan siyempre dili kini mohaum sa panid. Sa kinatibuk-an, ang usa ka bug nga nakapapakyas kanako sulod sa pipila ka bulan gitul-id pinaagi sa pagbag-o sa двух mga linya.

Mga bahin sa mga function sa pagtawag

Ug karon ang processor nag-ihap sa usa ka butang, ang Qemu wala mag-crash, apan ang screen wala mag-on, ug ang processor dali nga moadto sa mga galong, nga gihukman sa output. -d exec,in_asm,out_asm. Mitumaw ang usa ka pangagpas: ang mga paghunong sa timer (o, sa kinatibuk-an, ang tanan nga mga paghunong) wala moabut. Ug sa tinuud, kung imong i-unscrew ang mga interrupts gikan sa lumad nga asembliya, nga sa usa ka hinungdan nagtrabaho, nakakuha ka usa ka parehas nga litrato. Apan dili kini ang tubag: ang pagtandi sa mga pagsubay nga gi-isyu sa opsyon sa ibabaw nagpakita nga ang mga trajectory sa pagpatay sayo kaayo. Dinhi kinahanglan nga giingon nga pagtandi kung unsa ang natala gamit ang launcher emrun debugging output uban sa output sa lumad nga asembliya dili usa ka bug-os nga mekanikal nga proseso. Wala ko mahibal-an kung giunsa ang usa ka programa nga nagdagan sa usa ka browser nagkonektar emrun, apan ang pipila ka mga linya sa output nahimo nga gihan-ay pag-usab, mao nga ang kalainan sa diff dili pa usa ka rason sa paghunahuna nga ang mga trajectory nagkatibulaag. Sa kinatibuk-an, kini nahimong tin-aw nga sumala sa mga instruksyon ljmpl adunay usa ka transisyon ngadto sa lain-laing mga adres, ug ang bytecode nga namugna mao ang sukaranan nga lain-laing mga: ang usa naglangkob sa usa ka instruksyon sa pagtawag sa usa ka helper function, ang usa wala. Pagkahuman sa pag-goog sa mga panudlo ug pagtuon sa code nga naghubad sa kini nga mga panudlo, nahimo’g klaro nga, una, sa wala pa kini sa rehistro cr0 gihimo ang usa ka pagrekord - gamit usab ang usa ka katabang - nga nagbalhin sa processor ngadto sa protected mode, ug ikaduha, nga ang js nga bersyon wala gayud mobalhin sa protected mode. Apan ang tinuod mao nga ang laing bahin sa Emscripten mao ang pagdumili sa pagtugot sa code sama sa pagpatuman sa mga instruksyon. call sa TCI, nga ang bisan unsang function pointer moresulta sa tipo long long f(int arg0, .. int arg9) - Ang mga gimbuhaton kinahanglan nga tawagan nga adunay husto nga gidaghanon sa mga argumento. Kung kini nga lagda malapas, depende sa mga setting sa pag-debug, ang programa ma-crash (nga maayo) o motawag sa sayup nga function (nga makapasubo sa pag-debug). Adunay usab usa ka ikatulo nga kapilian - makapahimo sa henerasyon sa mga wrapper nga nagdugang / nagtangtang sa mga argumento, apan sa kinatibuk-an kini nga mga wrapper nagkinahanglan og daghang luna, bisan pa sa kamatuoran nga sa pagkatinuod ako nagkinahanglan lamang og gamay nga labaw sa usa ka gatos nga mga wrapper. Kini lamang usa ka makapasubo kaayo, apan adunay usa ka mas seryoso nga problema: sa namugna nga code sa mga function sa wrapper, ang mga argumento nakabig ug nakabig, apan usahay ang function sa mga namugna nga argumento wala gitawag - maayo, sama sa akong pagpatuman sa libffi. Sa ato pa, ang ubang mga katabang wala lang gipatay.

Maayo na lang, ang Qemu adunay mga lista nga mabasa sa makina sa mga katabang sa porma sa usa ka file sa header

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

Gigamit kini nga medyo kataw-anan: una, ang mga macro gi-redefined sa labing katingad-an nga paagi DEF_HELPER_n, ug dayon i-on helper.h. Sa gidak-on nga ang macro gipalapdan ngadto sa usa ka istruktura nga initializer ug usa ka comma, ug unya usa ka array ang gihubit, ug imbes nga mga elemento - #include <helper.h> Ingon usa ka sangputanan, sa katapusan nakahigayon ako nga sulayan ang librarya sa trabahoan pyparsing, ug gisulat ang usa ka script nga nagmugna sa eksakto nga mga wrapper alang sa eksakto nga mga gimbuhaton diin kini gikinahanglan.

Ug busa, pagkahuman ang processor daw nagtrabaho. Ingon og tungod kay ang screen wala pa gisugdan, bisan kung ang memtest86+ nakahimo sa pagdagan sa lumad nga asembliya. Dinhi kinahanglan nga ipatin-aw nga ang Qemu block I/O code gisulat sa mga coroutine. Ang Emscripten adunay kaugalingon nga malisud kaayo nga pagpatuman, apan kinahanglan gihapon nga suportahan sa Qemu code, ug mahimo nimong i-debug ang processor karon: Gisuportahan sa Qemu ang mga kapilian -kernel, -initrd, -append, diin mahimo nimong ma-boot ang Linux o, pananglitan, memtest86+, nga wala gyud mogamit mga block device. Apan ania ang problema: sa lumad nga asembliya ang usa makakita sa Linux kernel output sa console nga adunay kapilian -nographic, ug walay output gikan sa browser ngadto sa terminal diin kini gilusad emrun, wala niabot. Kana mao, dili kini klaro: ang processor wala magtrabaho o ang graphics output wala magtrabaho. Ug unya misantop sa akong hunahuna nga maghulat og gamay. Nahibal-an nga "ang processor wala matulog, apan hinay nga nagkidlap," ug pagkahuman sa mga lima ka minuto ang kernel naglabay sa daghang mga mensahe sa console ug nagpadayon sa pagbitay. Nahimong tin-aw nga ang processor, sa kinatibuk-an, nagtrabaho, ug kinahanglan namon nga magkalot sa code alang sa pagtrabaho kauban ang SDL2. Ikasubo, wala ako kahibalo kung unsaon paggamit kini nga librarya, mao nga sa pipila ka mga lugar kinahanglan kong molihok nga random. Sa usa ka punto, ang linya parallel0 mikidlap sa screen sa usa ka asul nga background, nga nagsugyot sa pipila ka mga hunahuna. Sa katapusan, nahimo nga ang problema mao nga ang Qemu nagbukas sa daghang mga virtual windows sa usa ka pisikal nga bintana, diin mahimo ka magbalhin gamit ang Ctrl-Alt-n: kini molihok sa lumad nga pagtukod, apan dili sa Emscripten. Human makuha ang wala kinahanglana nga mga bintana gamit ang mga kapilian -monitor none -parallel none -serial none ug mga instruksyon sa kusog nga pagdrowing sa tibuok screen sa matag frame, ang tanan kalit nga nagtrabaho.

Mga Coroutine

Busa, ang emulation sa browser naglihok, apan dili ka makadagan sa bisan unsa nga makapaikag nga single-floppy niini, tungod kay walay block I/O - kinahanglan nimo nga ipatuman ang suporta alang sa mga coroutine. Ang Qemu aduna nay daghang coroutine backend, apan tungod sa kinaiyahan sa JavaScript ug sa Emscripten code generator, dili ka makasugod sa pag-juggling sa mga stack. Mopatim-aw nga "wala na ang tanan, gitangtang ang plaster," apan giatiman na sa mga developer sa Emscripten ang tanan. Kini gipatuman medyo kataw-anan: tawgon nato ang usa ka function call sama niini nga kadudahan emscripten_sleep ug ubay-ubay nga uban pa nga naggamit sa mekanismo sa Asyncify, ingon man ang mga tawag sa pointer ug mga tawag sa bisan unsang function diin ang usa sa miaging duha ka mga kaso mahimong mahitabo sa unahan sa stack. Ug karon, sa wala pa ang matag kadudahang tawag, magpili kami usa ka konteksto nga async, ug pagkahuman dayon sa tawag, susihon namon kung adunay nahitabo nga asynchronous nga tawag, ug kung naa na, i-save namon ang tanan nga lokal nga mga variable sa kini nga konteksto sa async, ipasabut kung unsang function. sa pagbalhin sa kontrol sa diha nga kita kinahanglan nga magpadayon sa pagpatay , ug exit sa kasamtangan nga function. Dinhi adunay kasangkaran sa pagtuon sa epekto pagwaldas - alang sa mga panginahanglan sa pagpadayon sa pagpatuman sa code human sa pagbalik gikan sa usa ka asynchronous nga tawag, ang compiler makamugna og "stubs" sa function sugod human sa usa ka kadudahan nga tawag - sama niini: kung adunay n kadudahang mga tawag, nan ang function mapalapad sa usa ka dapit n/2 mga panahon — mao gihapon kini, kung dili Hinumdumi nga pagkahuman sa matag usa nga mahimo’g asynchronous nga tawag, kinahanglan nimo nga idugang ang pagtipig sa pipila ka mga lokal nga variable sa orihinal nga function. Pagkahuman, kinahanglan pa nga magsulat ako usa ka yano nga script sa Python, nga, pinasukad sa usa ka gihatag nga hugpong sa labi nga gigamit nga mga function nga kuno "dili tugutan ang asynchrony nga moagi sa ilang kaugalingon" (nga mao, ang promosyon sa stack ug tanan nga akong gihulagway dili. pagtrabaho sa kanila), nagpaila sa mga tawag pinaagi sa mga pointer diin ang mga gimbuhaton kinahanglan nga ibalewala sa compiler aron kini nga mga gimbuhaton dili isipon nga asynchronous. Ug unya ang mga file sa JS nga ubos sa 60 MB klaro kaayo - ingnon ta nga labing menos 30. Bisan pa, sa higayon nga nag-set up ako usa ka script sa asembliya, ug wala tuyoa nga gilabay ang mga kapilian sa linker, diin ang -O3. Gipadagan nako ang nahimo nga code, ug ang Chromium mokaon sa memorya ug nahagsa. Ako dayon aksidenteng mitan-aw kon unsa ang iyang gipaningkamutan nga i-download ... Aw, unsa ang akong isulti, ako unta nagyelo usab kon ako gihangyo sa mahunahunaon nga pagtuon ug optimize sa usa ka 500+ MB Javascript.

Ikasubo, ang mga tseke sa Asyncify support library code dili hingpit nga mahigalaon longjmp-s nga gigamit sa virtual processor code, apan human sa usa ka gamay nga patch nga nagpugong niini nga mga tseke ug kusog nga nagpasig-uli sa mga konteksto ingon nga ang tanan maayo, ang code nagtrabaho. Ug unya nagsugod ang usa ka katingad-an nga butang: usahay ang mga pagsusi sa code sa pag-synchronize na-trigger - ang parehas nga nag-crash sa code kung, sumala sa lohika sa pagpatuman, kinahanglan nga babagan - adunay misulay sa pag-ilog sa usa ka nakuha nga mutex. Maayo na lang, kini nahimo nga dili usa ka lohikal nga problema sa serialized code - Gigamit ra nako ang standard main loop functionality nga gihatag sa Emscripten, apan usahay ang asynchronous nga tawag bug-os nga maablihan ang stack, ug nianang higayona mapakyas kini. setTimeout gikan sa main loop - sa ingon, ang code misulod sa main loop iteration nga dili mobiya sa miaging iteration. Gisulat pag-usab sa usa ka walay katapusan nga loop ug emscripten_sleep, ug ang mga problema sa mutex mihunong. Ang code nahimo pa nga labi ka lohikal - pagkahuman, sa tinuud, wala akoy pipila nga code nga nag-andam sa sunod nga frame sa animation - gikalkula lang sa processor ang usa ka butang ug ang screen kanunay nga gi-update. Bisan pa, ang mga problema wala mohunong didto: usahay ang pagpatay sa Qemu mohunong ra sa hilom nga wala’y mga eksepsiyon o mga sayup. Nianang higayona mihunong ko niini, apan, sa pagtan-aw sa unahan, akong isulti nga ang problema mao kini: ang coroutine code, sa pagkatinuod, wala mogamit setTimeout (o labing menos dili kanunay sama sa imong gihunahuna): function emscripten_yield nagbutang lang sa asynchronous nga bandila sa tawag. Ang tibuok punto mao kana emscripten_coroutine_next dili usa ka asynchronous function: sa sulod gisusi niini ang bandila, gi-reset kini ug gibalhin ang kontrol sa kung diin kini kinahanglan. Sa ato pa, ang promosyon sa stack natapos didto. Ang problema mao nga tungod sa paggamit-pagkahuman-libre, nga mitungha sa dihang ang coroutine pool na-disable tungod sa kamatuoran nga wala ko gikopya ang usa ka importante nga linya sa code gikan sa kasamtangan nga coroutine backend, ang function qemu_in_coroutine mibalik nga tinuod kung sa pagkatinuod kini unta mibalik nga bakak. Kini misangpot sa usa ka tawag emscripten_yield, nga sa ibabaw niini walay usa sa stack emscripten_coroutine_next, ang stack mibukhad sa pinakataas, apan wala setTimeout, sa ako nang giingon, wala gipakita.

Pagmugna sa JavaScript code

Ug ania, sa pagkatinuod, mao ang gisaad nga "ibalik ang tinadtad nga karne." Dili gyud. Siyempre, kon kita modagan Qemu sa browser, ug Node.js niini, nan, natural, human sa code generation sa Qemu kita sa bug-os nga sayop JavaScript. Apan sa gihapon, usa ka matang sa reverse transformation.

Una, gamay kung giunsa ang Qemu nagtrabaho. Palihug pasayloa ako dayon: Dili ako propesyonal nga developer sa Qemu ug ang akong mga konklusyon mahimong sayup sa pipila ka mga lugar. Sama sa giingon nila, "ang opinyon sa estudyante dili kinahanglan nga motakdo sa opinyon sa magtutudlo, ang axiomatics ug sentido komon ni Peano." Ang Qemu adunay usa ka piho nga gidaghanon sa gisuportahan nga mga arkitektura sa bisita ug alang sa matag usa adunay usa ka direktoryo nga sama target-i386. Kung nagtukod, mahimo nimong ipiho ang suporta alang sa daghang mga arkitektura sa bisita, apan ang sangputanan mahimong daghang mga binary. Ang code aron suportahan ang arkitektura sa bisita, sa baylo, nagpatunghag pipila ka mga internal nga operasyon sa Qemu, diin ang TCG (Tiny Code Generator) nahimo na nga code sa makina alang sa arkitektura sa host. Ingon sa gipahayag sa readme file nga nahimutang sa tcg directory, kini orihinal nga bahin sa usa ka regular nga C compiler, nga sa ulahi gipahaom alang sa JIT. Busa, pananglitan, ang target nga arkitektura sa mga termino niini nga dokumento dili na usa ka bisita nga arkitektura, apan usa ka host nga arkitektura. Sa usa ka punto, usa pa ka bahin ang nagpakita - Tiny Code Interpreter (TCI), nga kinahanglan ipatuman ang code (halos parehas nga internal nga operasyon) kung wala ang usa ka generator sa code alang sa usa ka piho nga arkitektura sa host. Sa tinuud, ingon sa gipahayag sa dokumentasyon niini, kini nga tighubad mahimo nga dili kanunay nga molihok ingon usa ka generator sa JIT code, dili lamang sa quantitatively sa mga termino sa katulin, apan usab sa qualitatively. Bisan tuod dili ko sigurado nga ang iyang paghulagway hingpit nga may kalabutan.

Sa sinugdan gisulayan nako ang paghimo sa usa ka hingpit nga backend sa TCG, apan dali nga naglibog sa source code ug usa ka dili hingpit nga tin-aw nga paghulagway sa mga panudlo sa bytecode, mao nga nakahukom ko nga iputos ang tighubad sa TCI. Naghatag kini daghang mga bentaha:

  • kung nag-implementar sa usa ka code generator, dili nimo tan-awon ang paghulagway sa mga panudlo, apan sa code sa tighubad
  • makahimo ka og mga function dili alang sa matag block sa paghubad nga nasugatan, apan, pananglitan, pagkahuman lamang sa ika-gatos nga pagpatay
  • kung ang namugna nga code mausab (ug kini daw posible, sa paghukom sa mga function nga adunay mga ngalan nga adunay sulod nga pulong nga patch), kinahanglan nako nga dili balido ang namugna nga JS code, apan labing menos aduna akoy butang nga i-regenerate kini gikan sa

Mahitungod sa ikatulo nga punto, dili ako sigurado nga ang pag-patch posible human mapatuman ang code sa unang higayon, apan ang unang duha ka punto igo na.

Sa sinugdan, ang code gihimo sa porma sa usa ka dako nga switch sa adres sa orihinal nga bytecode nga instruksyon, apan unya, sa paghinumdom sa artikulo mahitungod sa Emscripten, pag-optimize sa namugna nga JS ug relooping, nakahukom ko nga makamugna og dugang nga tawhanong code, ilabi na kay sa empirically kini. nahimo nga ang bugtong entry point sa block sa paghubad mao ang Start. Sa wala madugay gisulti kay nahuman, sa wala madugay kami adunay usa ka code generator nga nakamugna og code nga adunay ifs (bisan walay mga galong). Apan dili maayo nga kapalaran, kini nahagsa, nga naghatag usa ka mensahe nga ang mga panudlo adunay dili husto nga gitas-on. Dugang pa, ang katapusan nga panudlo sa kini nga lebel sa recursion mao brcond. Okay, idugang nako ang parehas nga tseke sa henerasyon sa kini nga panudlo sa wala pa ug pagkahuman sa recursive nga tawag ug ... wala’y usa kanila ang gipatay, apan pagkahuman sa pag-angkon nga switch napakyas gihapon sila. Sa katapusan, human sa pagtuon sa namugna nga code, akong naamgohan nga human sa switch, ang pointer sa kasamtangan nga instruksyon gi-reload gikan sa stack ug lagmit gisapawan sa namugna nga JavaScript code. Ug mao kini ang nahitabo. Ang pagdugang sa buffer gikan sa usa ka megabyte ngadto sa napulo wala magdala sa bisan unsa, ug kini nahimong tin-aw nga ang code generator nagdagan sa mga lingin. Kinahanglan namon nga susihon nga wala kami molapas sa mga utlanan sa karon nga TB, ug kung among buhaton, unya i-isyu ang adres sa sunod nga TB nga adunay minus nga timaan aron kami makapadayon sa pagpatay. Dugang pa, gisulbad niini ang problema "unsa nga mga nahimo nga mga gimbuhaton ang kinahanglan nga dili balido kung kini nga piraso sa bytecode nabag-o?" — ang function lang nga katumbas sa kini nga block sa paghubad kinahanglan nga dili balido. Pinaagi sa dalan, bisan kung gi-debug nako ang tanan sa Chromium (tungod kay gigamit nako ang Firefox ug mas sayon ​​​​alang kanako ang paggamit sa usa ka bulag nga browser alang sa mga eksperimento), gitabangan ako sa Firefox nga matul-id ang mga incompatibilities sa asm.js standard, pagkahuman ang code nagsugod sa pagtrabaho nga mas paspas sa Chromium.

Pananglitan sa namugna nga code

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

konklusyon

Busa, ang trabaho wala gihapon mahuman, apan gikapoy ako sa sekreto nga pagdala niining dugay nga pagtukod ngadto sa kahingpitan. Busa, nakahukom ko nga imantala ang naa nako karon. Ang code medyo makahadlok sa mga lugar, tungod kay kini usa ka eksperimento, ug dili kini klaro nga abante kung unsa ang kinahanglan buhaton. Tingali, kinahanglan nga mag-isyu og normal nga atomic commit sa ibabaw sa pipila ka mas modernong bersyon sa Qemu. Sa kasamtangan, adunay usa ka hilo sa Gita sa usa ka format sa blog: alang sa matag "ang-ang" nga bisan sa usa ka paagi gipasa, usa ka detalyado nga komentaryo sa Russian ang gidugang. Sa tinuud, kini nga artikulo sa kadaghanan usa ka pagsaysay pag-usab sa konklusyon git log.

Mahimo nimong sulayan ang tanan dinhi (Pagbantay sa trapiko).

Unsa na ang nagtrabaho:

  • x86 virtual processor nga nagdagan
  • Adunay usa ka nagtrabaho nga prototype sa usa ka JIT code generator gikan sa machine code ngadto sa JavaScript
  • Adunay usa ka template alang sa pag-assemble sa uban pang 32-bit nga mga arkitektura sa bisita: karon mahimo nimong dayegon ang Linux alang sa arkitektura sa MIPS nga nagyelo sa browser sa yugto sa pagkarga.

Unsa pa ang imong mahimo

  • Pagpadali sa emulation. Bisan sa JIT mode daw mas hinay ang pagdagan kaysa Virtual x86 (apan adunay potensyal nga usa ka tibuuk nga Qemu nga adunay daghang gisundog nga hardware ug arkitektura)
  • Aron makahimo og usa ka normal nga interface - prangka, dili ako usa ka maayo nga web developer, mao nga sa pagkakaron akong gi-remake ang standard nga Emscripten shell kutob sa akong mahimo
  • Sulayi ang paglansad sa mas komplikado nga Qemu function - networking, VM migration, etc.
  • UPS: kinahanglan nimo nga isumite ang imong pipila ka mga pag-uswag ug mga taho sa bug sa Emscripten upstream, sama sa gibuhat sa nangaging mga porter sa Qemu ug uban pang mga proyekto. Salamat sa ila sa paggamit sa ilang kontribusyon sa Emscripten isip kabahin sa akong buluhaton.

Source: www.habr.com

Idugang sa usa ka comment