Qemu.js le tacaíocht JIT: is féidir leat an mince a iompú fós

Cúpla bliain ó shin Fabrice Bellard Scríofa ag jslinux is aithriseoir ríomhaire atá scríofa i JavaScript. Ina dhiaidh sin bhí ar a laghad níos mó Fíorúil x86. Ach bhí gach ceann acu, chomh fada agus is eol dom, ina ateangairí, agus úsáideann Qemu, scríofa i bhfad níos luaithe ag an Fabrice Bellard céanna, agus, is dócha, aon aithriseoir nua-aimseartha féin-urramú, cód aoi JIT a thiomsú isteach i gcód an chórais óstach. Chonacthas dom go raibh sé in am a mhalairt de chúram a chur i bhfeidhm maidir leis an gceann a réitíonn brabhsálaithe: tiomsú JIT de chód meaisín isteach i JavaScript, a raibh an chuma air gur loighciúil é chun Qemu a phortáil. Bheadh ​​​​sé cosúil, cén fáth Qemu, tá emulators níos simplí agus éasca le húsáid - an VirtualBox céanna, mar shampla - suiteáilte agus oibríonn sé. Ach tá roinnt gnéithe suimiúla ag Qemu

  • Foinse oscailte
  • Cumas a bheith ag obair gan tiománaí eithne
  • cumas oibriú i mód ateangaire
  • tacaíocht do líon mór ailtireachtaí óstaigh agus aoi araon

Maidir leis an tríú pointe, is féidir liom a mhíniú anois go deimhin, i mód TCI, nach iad na treoracha meaisín aoi iad féin a léirmhínítear, ach an bytecode a fhaightear uathu, ach ní athraíonn sé seo an bunúsach - d'fhonn a thógáil agus a reáchtáil. Qemu ar ailtireacht nua, má tá tú t-ádh, is leor A tiomsaitheoir C - scríobh gineadóir cód is féidir a chur siar.

Agus anois, tar éis dhá bhliain de tinkering leisurely le cód foinse Qemu i mo chuid ama saor in aisce, tháinig fréamhshamhail ag obair, inar féidir leat a reáchtáil cheana féin, mar shampla, Kolibri OS.

Cad é Emscripten

Sa lá atá inniu ann, tá go leor tiomsaitheoirí le feiceáil, agus is é JavaScript an toradh deiridh. Bhí cuid acu, cosúil le Cineál Script, ceaptha ar dtús mar an bealach is fearr chun scríobh don ngréasán. Ag an am céanna, is bealach é Emscripten chun cód C nó C ++ atá ann cheana a ghlacadh agus é a thiomsú i bhfoirm atá inléite ag brabhsálaí. Ar an leathanach seo Tá go leor poirt de chláracha aitheanta bailithe againn: anseoMar shampla, is féidir leat breathnú ar PyPy - dála an scéil, éilíonn siad go bhfuil JIT acu cheana féin. Go deimhin, ní féidir gach clár a thiomsú go simplí agus a reáchtáil i mbrabhsálaí - tá roinnt ann gnéithe, a chaithfidh tú cur suas leis, áfach, mar a deir an inscríbhinn ar an leathanach céanna “Is féidir Emscripten a úsáid chun beagnach aon iniompartha Cód C/C++ chuig JavaScript". Is é sin, tá roinnt oibríochtaí ann ar iompar neamhshainithe iad de réir an chaighdeáin, ach a oibríonn de ghnáth ar x86 - mar shampla, rochtain neamhailínithe ar athróga, rud a thoirmisctear go ginearálta i roinnt ailtireachtaí. , Is clár tras-ardán é Qemu agus , bhí mé ag iarraidh a chreidiúint, agus níl mórán iompair neamhshainithe ann cheana féin - tóg é agus cuir le chéile é, ansin tinker beagán le JIT - agus tá tú críochnaithe! cás...

An chéad iarracht

Go ginearálta, ní mise an chéad duine a tháinig ar an smaoineamh Qemu a aistriú go JavaScript. Cuireadh ceist ar fhóram ReactOS an bhféadfaí é seo a úsáid le Emscripten. Níos luaithe fós, bhí ráflaí ann go ndearna Fabrice Bellard é seo go pearsanta, ach bhíomar ag caint faoi jslinux, rud nach bhfuil, chomh fada agus is eol dom, ach iarracht chun feidhmíocht leordhóthanach a bhaint amach de láimh i JS, agus scríobhadh é ón tús. Níos déanaí, scríobhadh Virtual x86 - postáladh foinsí gan bhac dó, agus, mar a dúradh, de bharr “réalachas” níos mó an aithrise bhíothas in ann SeaBIOS a úsáid mar fhirmware. Ina theannta sin, rinneadh iarracht amháin ar a laghad chun Qemu a phortáil ag baint úsáide as Emscripten - rinne mé iarracht é seo a dhéanamh soicéad, ach bhí an fhorbairt, chomh fada agus a thuigim, reoite.

Mar sin, is cosúil, seo iad na foinsí, seo é Emscripten - tóg é agus tiomsaigh. Ach tá leabharlanna ann freisin ar a mbraitheann Qemu, agus leabharlanna ar a mbraitheann na leabharlanna sin, etc., agus tá ceann acu libffi, a glib ag brath ar. Bhí ráflaí ar an Idirlíon go raibh ceann sa bhailiúchán mór de phoirt leabharlanna do Emscripten, ach ba dheacair a chreidiúint ar bhealach éigin: ar an gcéad dul síos, ní raibh sé i gceist gur tiomsaitheoir nua a bhí ann, sa dara háit, bhí sé ró-íseal-leibhéal a leabharlann le piocadh suas, agus a thiomsú chuig JS. Agus ní ceist ionchuir tionóil amháin é - is dócha, má casadh tú air, i gcás roinnt coinbhinsiúin glaonna is féidir leat na hargóintí riachtanacha a ghiniúint ar an gcruach agus an fheidhm a ghlaoch gan iad. Ach is deacair an rud é Emscripten: chun an cód ginte a chur in aithne don bhrabhsálaí optamaithe inneall JS, úsáidtear roinnt cleasanna. Go háirithe, déanann an relooping mar a thugtar air - gineadóir cód ag baint úsáide as an LLVM IR faighte le roinnt treoracha aistrithe teibí iarracht ifs sochreidte, lúba, etc. Bhuel, conas a chuirtear na hargóintí ar aghaidh chuig an bhfeidhm? Ar ndóigh, mar argóintí le feidhmeanna JS, is é sin, más féidir, ní tríd an stack.

Ag an tús bhí an smaoineamh go simplí a scríobh in ionad libffi le JS agus tástálacha caighdeánacha a rith, ach sa deireadh tháinig mearbhall orm faoi conas a dhéanamh ar mo chomhaid ceanntásc ionas go n-oibreoidh siad leis an gcód reatha - cad is féidir liom a dhéanamh, mar a deir siad, "An bhfuil na tascanna chomh casta "An bhfuil muid chomh dúr?" Bhí orm libffi a aistriú chuig ailtireacht eile, mar a déarfá - go fortunately, tá an dá mhacra ag Emscripten le haghaidh cóimeála inlíne (i Javascript, yeah - go maith, is cuma cén ailtireacht, mar sin an cóimeálaí), agus an cumas cód a ghintear ar an eitilt a rith. Go ginearálta, tar éis dom a bheith ag tinkering le blúirí libffi atá ag brath ar an ardán ar feadh tamaill, fuair mé cód in-chomhshóite agus rith mé é ar an gcéad tástáil ar tháinig mé trasna air. Chun mo choinne, d'éirigh leis an tástáil. Stunned ag mo genius - gan aon joke, d'oibrigh sé ón gcéad seoladh - mé, fós gan a chreidiúint mo shúile, chuaigh chun breathnú ar an cód mar thoradh air arís, chun meastóireacht a dhéanamh nuair a tochailt seo chugainn. Seo chuaigh mé cnónna don dara huair - an rud amháin a rinne mo fheidhm ffi_call - thuairiscigh sé seo gur éirigh leis an nglao. Ní raibh aon ghlaoch féin. Mar sin chuir mé mo chéad iarratas tarraingte, rud a cheartaigh earráid sa triail atá soiléir d'aon dalta san Oilimpiad - níor cheart fíoruimhreacha a chur i gcomparáid mar a == b agus fiú conas a - b < EPS - ní mór duit cuimhneamh ar an modúl freisin, nó beidh 0 cothrom go mór le 1/3... Go ginearálta, tháinig mé suas le calafort libffi áirithe, a théann thar na tástálacha is simplí, agus a bhfuil glib ann. le chéile - chinn mé go mbeadh sé riachtanach, cuirfidh mé é níos déanaí. Ag féachaint amach romhainn, déarfaidh mé, mar a d'éirigh sé amach, nár áirigh an tiomsaitheoir an fheidhm libffi fiú sa chód deiridh.

Ach, mar a dúirt mé cheana, tá roinnt teorainneacha, agus i measc úsáid saor in aisce ar iompar neamhshainithe éagsúla, tá gné níos míthaitneamhach i bhfolach - ní thacaíonn JavaScript de réir dearadh le multithreading le cuimhne roinnte. I bprionsabal, is féidir smaoineamh maith a thabhairt air seo de ghnáth, ach ní le haghaidh cód aistrithe a bhfuil a ailtireacht ceangailte le snáitheanna C. Go ginearálta, tá Firefox ag tástáil le tacú le hoibrithe comhroinnte, agus tá cur i bhfeidhm snáitheanna ag Emscripten dóibh, ach ní raibh mé ag iarraidh brath air. Bhí orm multithreading a fhréamh go mall ón gcód Qemu - is é sin, a fháil amach cá bhfuil na snáitheanna ag rith, corp an lúb atá ag rith sa snáithe seo a bhogadh isteach i bhfeidhm ar leith, agus feidhmeanna den sórt sin a ghlaoch ceann ar cheann ón bpríomhlúb.

Dara iarracht

Ag pointe éigin, bhí sé soiléir go raibh an fhadhb fós ann, agus nach mbeadh aon mhaitheas mar thoradh ar magairlí a bhrú go randamach timpeall an chóid. Conclúid: ní mór dúinn córas a chur ar bhealach éigin ar an bpróiseas chun crutches a chur leis. Mar sin, tógadh leagan 2.4.1, a bhí úr ag an am sin, (ní 2.5.0, mar, cé a fhios, beidh fabhtanna sa leagan nua nach bhfuil gafa go fóill, agus tá mé go leor de mo fabhtanna féin. ), agus ba é an chéad rud é a athscríobh go sábháilte thread-posix.c. Bhuel, is é sin, chomh sábháilte: má rinne duine iarracht oibríocht a dhéanamh as a dtiocfaidh blocáil, glaodh an fheidhm láithreach abort() - ar ndóigh, níor réitigh sé seo na fadhbanna go léir ag an am céanna, ach ar a laghad bhí sé níos taitneamhaí ná sonraí neamhréireacha a fháil go ciúin.

Go ginearálta, tá roghanna Emscripten an-chabhrach chun cód a aistriú chuig JS -s ASSERTIONS=1 -s SAFE_HEAP=1 - glacann siad roinnt cineálacha iompair neamhshainithe, amhail glaonna chuig seoladh neamhailínithe (nach bhfuil ag teacht ar chor ar bith leis an gcód d’eagair chlóscríofa amhail HEAP32[addr >> 2] = 1) nó feidhm a ghlaoch leis an líon mícheart argóintí.

Dála an scéil, is saincheist ar leith iad earráidí ailínithe. Mar a dúirt mé cheana, tá inneall léirmhínithe “degenerate” ag Qemu do ghiniúint cód TCI (teangaire cód beag bídeach), agus chun Qemu a thógáil agus a rith ar ailtireacht nua, má tá an t-ádh leat, is leor tiomsaitheoir C. Eochairfhocail "má tá an t-ádh ort". Bhí an t-ádh orm, agus tharla sé go n-úsáideann TCI rochtain neamhailínithe agus a bytecode á pharsáil. Is é sin, ar gach cineál ARM agus ailtireachtaí eile a bhfuil rochtain leibhéalta riachtanach acu, tiomsaíonn Qemu toisc go bhfuil gnáth-inneall TCG acu a ghineann cód dúchais, ach is ceist eile í an n-oibreoidh TCI orthu. Mar a tharla, áfach, léirigh doiciméadú TCI rud éigin cosúil leis go soiléir. Mar thoradh air sin, cuireadh glaonna feidhme ar léamh neamhailínithe leis an gcód, a fuarthas i gcuid eile de Qemu.

Scriosadh gcarn

Mar thoradh air sin, ceartaíodh rochtain neamhailínithe ar TCI, cruthaíodh príomh-lúb ar a dtugtar an próiseálaí, RCU agus roinnt rudaí beaga eile. Agus mar sin seolaim Qemu leis an rogha -d exec,in_asm,out_asm, rud a chiallaíonn gur gá duit a rá cad iad na bloic cód atá á bhforghníomhú, agus freisin ag an am craolta a scríobh cén cód aoi a bhí ann, cén cód óstach a tháinig chun bheith (sa chás seo, bytecode). Tosaíonn sé, cuireann sé roinnt bloic aistriúcháin i gcrích, scríobhann sé an teachtaireacht dífhabhtaithe a d'fhág mé go dtosóidh RCU anois agus... tuairteanna abort() taobh istigh d'fheidhm free(). Trí tinkering leis an fheidhm free() D'éirigh linn a fháil amach go raibh truflais ann i gceanntásc an bhloic chairn, atá suite sna hocht mbeart roimh an gcuimhne leithdháilte.

Scriosadh an gcarn - cé chomh gleoite ... I gcás den sórt sin, tá leigheas úsáideach - ó (más féidir) na foinsí céanna, assemble dénártha dúchais agus é a reáchtáil faoi Valgrind. Tar éis roinnt ama, bhí an dénártha réidh. Seoladh mé leis na roghanna céanna - tuairteanna sé fiú le linn initialization, sula sroicheann i ndáiríre a fhorghníomhú. Tá sé míthaitneamhach, ar ndóigh - is cosúil, ní raibh na foinsí díreach mar an gcéanna, rud nach bhfuil iontas, mar gheall ar chumrú scouted amach roghanna beagán difriúil, ach tá mé Valgrind - an chéad beidh mé a shocrú fabht seo, agus ansin, má tá mé ádh. , beidh an ceann bunaidh le feiceáil. Tá an rud céanna á rith agam faoi Valgrind ... Y-y-y, y-y-y, uh-uh, thosaigh sé, chuaigh sé trí thúsú de ghnáth agus bhog sé ar aghaidh thar an bhfabht bunaidh gan rabhadh amháin faoi rochtain chuimhne mícheart, gan trácht ar thiteann. Níor ullmhaigh an saol, mar a deir siad, mé le haghaidh seo - ní stopann clár tuairteála nuair a seoladh é faoi Walgrind. Is rúndiamhair an rud a bhí ann. Is é mo hipitéis ná gur léirigh gdb obair uair amháin i gcóngar an teagaisc reatha tar éis tuairteála le linn tosaithe memset-a le pointeoir bailí ag baint úsáide as ceachtar acu mmx, nó xmm cláir, ansin b’fhéidir gur earráid ailínithe de chineál éigin a bhí ann, cé go bhfuil sé deacair fós a chreidiúint.

Ceart go leor, ní cosúil go gcabhraíonn Valgrind anseo. Agus anseo thosaigh an rud is disgusting - is cosúil go bhfuil gach rud ag tosú fiú, ach tuairteanna ar chúiseanna anaithnid go hiomlán mar gheall ar imeacht a d'fhéadfadh a tharla na milliúin treoracha ó shin. Ar feadh i bhfad, ní raibh sé soiléir fiú conas cur chuige. Sa deireadh, bhí orm fós suí síos agus dífhabhtú. Léirigh priontáil a raibh an ceanntásc athscríofa leis nach raibh cuma uimhir air, ach sonraí dénártha de chineál éigin. Agus, féach agus féach, fuarthas an teaghrán dénártha seo sa chomhad BIOS - is é sin, anois is féidir a rá le muinín réasúnta gur ró-shreabhadh maolánach a bhí ann, agus tá sé soiléir fiú gur scríobhadh chuig an maolán seo é. Bhuel, ansin rud éigin mar seo - in Emscripten, go fortunately, níl aon randamú ar an spás seoltaí, níl aon phoill ann ach an oiread, ionas gur féidir leat scríobh áit éigin i lár an chóid chun sonraí a aschur le pointeoir ón seoladh deireanach, breathnú ar na sonraí, breathnú ar an pointeoir, agus, más rud é nach bhfuil sé athraithe, a fháil le haghaidh machnaimh. Fíor, tógann sé cúpla nóiméad nasc a dhéanamh tar éis aon athrú, ach cad is féidir leat a dhéanamh? Mar thoradh air sin, fuarthas líne ar leith a chóipeáil an BIOS ón maolán sealadach go dtí an chuimhne aoi - agus, go deimhin, ní raibh go leor spáis sa maolán. Bhí feidhm mar thoradh ar fhoinse an tseolta maoláin aisteach sin qemu_anon_ram_alloc i gcomhad oslib-posix.c - an loighic a bhí ann: uaireanta is féidir go mbeadh sé úsáideach an seoladh a ailíniú le leathanach ollmhór de 2 MB i méid, iarrfaimid chuige seo mmap ar dtús beagán níos mó, agus ansin cuirfimid an farasbarr ar ais le cabhair munmap. Agus mura bhfuil ailíniú den sórt sin ag teastáil, ansin cuirfimid an toradh in iúl in ionad 2 MB getpagesize() - mmap tabharfaidh sé seoladh ailínithe fós... Mar sin in Emscripten mmap ach glaonna malloc, ach ar ndóigh ní ailíníonn sé ar an leathanach. Go ginearálta, ceartaíodh fabht a chuir frustrachas orm ar feadh cúpla mí trí athrú ar dhá línte.

Gnéithe feidhmeanna glaonna

Agus anois tá an próiseálaí ag comhaireamh rud éigin, ní dhéanann Qemu tuairteála, ach ní chuireann an scáileán ar siúl, agus téann an próiseálaí go tapa isteach i lúb, ag breith ar an aschur -d exec,in_asm,out_asm. Tá hipitéis tagtha chun cinn: ní thagann idirbhriseadh an lasc ama (nó, go ginearálta, gach idirbhriseadh). Agus go deimhin, má dhíscaoileann tú na hidirghabhálacha ón gcomhthionól dúchais, rud a d'oibrigh ar chúis éigin, gheobhaidh tú pictiúr den chineál céanna. Ach níorbh é seo an freagra ar chor ar bith: léirigh comparáid idir na rianta a eisíodh leis an rogha thuas go raibh éagsúlachtaí idir na conairí forghníomhaithe an-luath. Anseo ní mór a rá go bhfuil comparáid idir an méid a taifeadadh ag baint úsáide as an tosaitheoir emrun ní próiseas iomlán meicniúil é aschur dífhabhtaithe le haschur an tionóil dúchais. Níl a fhios agam go díreach conas a nascann clár a ritheann i mbrabhsálaí leis emrun, ach déantar roinnt línte san aschur a atheagrú, mar sin níl an difríocht sa difríocht fós ina chúis le glacadh leis go bhfuil éagsúlachtaí idir na conairí. Go ginearálta, ba léir gur de réir na dtreoracha ljmpl tá aistriú go seoltaí éagsúla, agus tá an bytecode a ghintear difriúil go bunúsach: tá ceann amháin treoir chun glaoch ar fheidhm cúntóir, ní dhéanann an ceann eile. Tar éis googling na treoracha agus staidéar a dhéanamh ar an gcód a aistríonn na treoracha seo, ba léir, ar an gcéad dul síos, díreach roimhe sin sa chlár cr0 rinneadh taifeadadh - ag baint úsáide as cúntóir freisin - a d'aistrigh an próiseálaí go mód cosanta, agus sa dara háit, nár aistrigh an leagan js go mód cosanta riamh. Ach is é fírinne an scéil gur gné eile de Emscripten ná an drogall atá air cód a fhulaingt ar nós treoracha a chur i bhfeidhm. call in TCI, a mbíonn cineál mar thoradh ar aon phointeoir feidhme long long f(int arg0, .. int arg9) - ní mór feidhmeanna a ghlaoch leis an líon ceart argóintí. Má sháraítear an riail seo, ag brath ar na socruithe debugging, beidh an clár tuairteála (rud atá go maith) nó glaoch ar an fheidhm mícheart ar chor ar bith (a bheidh brónach chun dífhabhtaithe). Tá an tríú rogha ann freisin - cumhdaigh a chumasú a chuireann / a bhaint as argóintí, ach san iomlán glacann na fillteáin seo go leor spáis, in ainneoin nach gá dom ach beagán níos mó ná céad cumhdach. Tá sé seo ina n-aonar an-brónach, ach d'éirigh fadhb níos tromchúisí: i gcód ginte na bhfeidhmeanna fillteáin, rinneadh na hargóintí a thiontú agus a thiontú, ach uaireanta níor tugadh an fheidhm leis na hargóintí a ghintear ar a dtugtar - go maith, díreach mar atá i mo chur i bhfeidhm libffi. Is é sin, níor cuireadh roinnt cúntóirí chun báis.

Ar ámharaí an tsaoil, tá liostaí cúntóirí meaisín-inléite ag Qemu i bhfoirm comhad ceanntásca mar

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

Úsáidtear iad go leor greannmhar: ar dtús, déantar macraí a athshainiú ar an mbealach is aisteach DEF_HELPER_n, agus ansin casadh ar helper.h. A mhéid a dhéantar an macra a leathnú isteach i dtosaitheoir struchtúir agus camóg, agus ansin sainmhínítear eagar, agus in ionad eilimintí - #include <helper.h> Mar thoradh air sin, bhí deis agam sa deireadh triail a bhaint as an leabharlann ag an obair piparsáil, agus scríobhadh script a ghineann go díreach na cumhdaigh sin do na feidhmeanna go díreach a bhfuil gá leo.

Agus mar sin, tar éis an chuma ar an próiseálaí a bheith ag obair. Is cosúil gur tharla sé mar nár cuireadh tús leis an scáileán riamh, cé go raibh memtest86+ in ann rith sa tionól dúchais. Anseo is gá a shoiléiriú go bhfuil an cód I/O bloc Qemu scríofa i coroutines. Tá a fhorfheidhmiú an-deacair ag Emscripten, ach bhí gá fós le tacaíocht a thabhairt dó sa chód Qemu, agus is féidir leat an próiseálaí a dhífhabhtú anois: tacaíonn Qemu le roghanna -kernel, -initrd, -append, leis ar féidir leat Linux a thosú nó, mar shampla, memtest86+, gan úsáid a bhaint as feistí bloc ar chor ar bith. Ach seo an fhadhb: sa tionól dúchais d'fhéadfadh duine an t-aschur eithne Linux chuig an consól a fheiceáil leis an rogha -nographic, agus gan aon aschur ón mbrabhsálaí go dtí an teirminéal ón áit ar seoladh é emrun, níor tháinig. Is é sin, níl sé soiléir: níl an próiseálaí ag obair nó níl an t-aschur grafaicí ag obair. Agus ansin tharla sé dom fanacht beagán. D'éirigh sé amach nach bhfuil "an próiseálaí ag codladh, ach go simplí ag blinking go mall," agus tar éis thart ar cúig nóiméad chaith an eithne a bunch teachtaireachtaí ar an consól agus lean sé ar crochadh. Ba léir go n-oibríonn an próiseálaí, go ginearálta, agus ní mór dúinn tochailt isteach sa chód le haghaidh oibriú le SDL2. Ar an drochuair, níl a fhios agam conas an leabharlann seo a úsáid, mar sin bhí orm gníomhú go randamach in áiteanna áirithe. Ag pointe éigin, flashed an líne comhthreomhar0 ar an scáileán ar chúlra gorm, a mhol roinnt smaointe. Sa deireadh, d'éirigh sé amach gurb é an fhadhb a bhí ann ná go n-osclaíonn Qemu roinnt fuinneoga fíorúla i bhfuinneog fhisiceach amháin, idir ar féidir leat Ctrl-Alt-n a úsáid: oibríonn sé sa tógáil dhúchasach, ach ní in Emscripten. Tar éis fáil réidh le fuinneoga gan ghá ag baint úsáide as roghanna -monitor none -parallel none -serial none agus treoracha chun an scáileán iomlán a tharraingt go láidir ar gach fráma, d'oibrigh gach rud go tobann.

Coroutines

Mar sin, oibríonn aithrise sa bhrabhsálaí, ach ní féidir leat aon rud suimiúil aonfhlapach a reáchtáil ann, toisc nach bhfuil aon bhloc I/O ann - ní mór duit tacaíocht a chur i bhfeidhm le haghaidh coroutines. Tá roinnt aislínte coroutine ag Qemu cheana féin, ach mar gheall ar nádúr JavaScript agus gineadóir cód Emscripten, ní féidir leat tosú ag juggling stacks. Is cosúil go bhfuil “gach rud imithe, tá an plástar á bhaint,” ach tá forbróirí Emscripten tar éis aire a thabhairt do gach rud cheana féin. Cuirtear é seo i bhfeidhm go leor greannmhar: déanaimis glaoch feidhm mar seo amhrasach emscripten_sleep agus go leor eile ag baint úsáide as an meicníocht Asyncify, chomh maith le glaonna pointeoir agus glaonna chuig aon fheidhm ina bhféadfadh ceann amháin den dá chás roimhe seo tarlú níos faide síos an chruach. Agus anois, roimh gach glao amhrasach, roghnóimid comhthéacs asincrónach, agus díreach tar éis an ghlao, seiceálfaimid cibé an bhfuil glao asincrónach tar éis tarlú, agus má tá, sábhálfaimid gach athróg áitiúil sa chomhthéacs async seo, cuir in iúl cén fheidhm chun rialú a aistriú chuig nuair is gá dúinn leanúint ar aghaidh le forghníomhú , agus scoir an fheidhm reatha. Seo an áit a bhfuil scóip ann staidéar a dhéanamh ar an éifeacht ag cur amú — maidir leis na riachtanais a bhaineann le forghníomhú leanúnach an chóid tar éis filleadh ó ghlao asincrónach, gineann an tiomsaitheoir “stubs” den fheidhm a thosaíonn tar éis glao amhrasach — mar seo: má tá n glaoch amhrasach ann, leathnófar an fheidhm áit éigin n/2 amanna - tá sé seo fós, más rud é nach Coinnigh i gcuimhne go bhfuil tar éis gach glao a d'fhéadfadh a bheith asynchronous, ní mór duit a shábháil ar roinnt athróg áitiúil a chur leis an fheidhm bhunaidh. Ina dhiaidh sin, bhí orm fiú script shimplí a scríobh i Python, rud a bhí bunaithe ar shraith áirithe d’fheidhmeanna go háirithe a bhfuil ró-úsáid á baint astu nach gceadaíonn an asincrónach dul tríothu féin” (is é sin, cur chun cinn cruachta agus gach rud a ndearna mé cur síos air nach bhfuil. obair iontu), cuireann sé glaonna in iúl trí threo inar cheart don tiomsaitheoir neamhaird a dhéanamh d’fheidhmeanna ionas nach mbreathnaítear ar na feidhmeanna seo a bheith asincrónach. Agus ansin is léir go bhfuil comhaid JS faoi 60 MB i bhfad ró - déanaimis a rá ar a laghad 30. Cé, nuair a bhí mé ag bunú script tionóil, agus chaith mé amach na roghanna nascóirí de thaisme, ina measc bhí -O3. Ritheann mé an cód a ghintear, agus itheann Cróimiam suas cuimhne agus tuairteanna. D'fhéach mé ansin de thaisme ar cad a bhí sé ag iarraidh a íoslódáil ... Bhuel, cad is féidir liom a rá, ba mhaith liom a bheith reoite freisin dá iarradh orm staidéar a dhéanamh go tuisceanach agus a bharrfheabhsú Javascript 500+ MB.

Ar an drochuair, ní raibh na seiceálacha i gcód leabharlainne tacaíochta Asyncify cairdiúil go hiomlán leo longjmp-s a úsáidtear sa chód próiseálaí fíorúil, ach tar éis paiste beag a dhíchumasaíonn na seiceálacha seo agus a athbhunú go láidir comhthéacsanna amhail is dá mba rud é go raibh gach rud go breá, d'oibrigh an cód. Agus ansin thosaigh rud aisteach: uaireanta spreagtar seiceálacha sa chód sioncrónaithe - na cinn chéanna a thuairteann an cód más rud é, de réir an loighic fhorghníomhaithe, ba cheart bac a chur air - rinne duine iarracht greim a fháil ar mutex a gabhadh cheana féin. Ar ámharaí an tsaoil, níor fadhb loighciúil é seo sa chód sraitheach - ní raibh mé ach ag baint úsáide as feidhmiúlacht chaighdeánach an phríomhlúb a chuir Emscripten ar fáil, ach uaireanta dhéanfadh an glao asincrónach an cruach a dhífhilleadh go hiomlán, agus ag an nóiméad sin theipfeadh air. setTimeout ón bpríomhlúb - dá bhrí sin, chuaigh an cód isteach sa phríomh-atriall lúb gan an atriall roimhe seo a fhágáil. Athscríobh ar lúb gan teorainn agus emscripten_sleep, agus stop na fadhbanna le mutexes. Tá an cód tar éis éirí níos loighciúla fós - tar éis an tsaoil, i ndáiríre, níl cód éigin agam a ullmhaíonn an chéad fhráma beochana eile - ní dhéanann an próiseálaí ach rud éigin a ríomh agus déantar an scáileán a nuashonrú go tréimhsiúil. Mar sin féin, níor stop na fadhbanna ansin: uaireanta chuirfí deireadh le forghníomhú Qemu go ciúin gan aon eisceachtaí nó earráidí. Ag an nóiméad sin thug mé suas é, ach, ag breathnú amach romhainn, déarfaidh mé gurb é seo an fhadhb: ní úsáideann an cód coroutine, i ndáiríre, setTimeout (nó ar a laghad ní chomh minic agus a shílfeá): feidhm emscripten_yield go simplí socraíonn sé an bhratach glaonna asincrónach. Is é an pointe ar fad go bhfuil emscripten_coroutine_next nach feidhm asincrónach í: seiceálann sé an bhratach go hinmheánach, athshocraíonn sé í agus aistríonn sé rialú go dtí an áit a bhfuil sé ag teastáil. Is é sin, críochnaíonn cur chun cinn an chruach ansin. Ba í an fhadhb ná mar gheall ar úsáid-iar-saor in aisce, a bhí le feiceáil nuair a bhí an linn coroutine díchumasaithe mar gheall ar an bhfíric nár chóipeáil mé líne tábhachtach de chód ón inneall coroutine atá ann cheana féin, an fheidhm qemu_in_coroutine ar ais fíor nuair ba cheart go mbeadh sé bréagach ar ais. Cuireadh glaoch mar thoradh air seo emscripten_yield, thuas ní raibh aon duine ar an chruaich emscripten_coroutine_next, an chairn neamhfhillte go dtí an barr an-, ach níl setTimeout, mar a dúirt mé cheana, nach raibh ar taispeáint.

Giniúint cód javascript

Agus anseo, i ndáiríre, tá an gealladh "an fheoil mhionaithe a iompú ar ais." Níl i ndáiríre. Ar ndóigh, má ritheann muid Qemu sa bhrabhsálaí, agus Node.js ann, ansin, go nádúrtha, tar éis giniúint cód i Qemu gheobhaidh muid JavaScript go hiomlán mícheart. Ach fós féin, claochlú droim ar ais de shaghas éigin.

Ar dtús, beagán faoi conas a oibríonn Qemu. Le do thoil logh dom ar an bpointe boise: ní forbróir Qemu gairmiúil mé agus d'fhéadfadh mo chonclúidí a bheith earráideach in áiteanna áirithe. Mar a deir siad, “ní gá go mbeadh tuairim an dalta ag teacht le tuairim an mhúinteora, le haiseolaíocht agus le tuiscint choiteann Peano.” Tá líon áirithe ailtireachtaí aoi-thacaithe ag Qemu agus tá eolaire cosúil le gach ceann acu target-i386. Agus tú ag tógáil, is féidir leat tacaíocht a shonrú do roinnt ailtireachtaí aoi, ach beidh an toradh díreach ar roinnt dénártha. Gineann an cód chun tacú leis an ailtireacht aoi, ar a seal, roinnt oibríochtaí inmheánacha Qemu, a bhfuil an TCG (Gineadóir Cód Tiny) ag casadh cheana féin isteach i gcód meaisín don ailtireacht óstach. Mar a dúradh sa chomhad readme atá san eolaire tcg, ba chuid de thiomsaitheoir C rialta é seo ar dtús, a cuireadh in oiriúint níos déanaí do JIT. Mar sin, mar shampla, ní ailtireacht aoi a thuilleadh í sprioc-ailtireacht i dtéarmaí an doiciméid seo, ach ailtireacht óstach. Ag pointe éigin, bhí comhpháirt eile le feiceáil - Ateangaire Cód Tiny (TCI), ar cheart dó cód a fhorghníomhú (beagnach na hoibríochtaí inmheánacha céanna) in éagmais gineadóir cód le haghaidh ailtireacht óstach ar leith. Go deimhin, mar a deir a dhoiciméadú, b'fhéidir nach bhfeidhmeoidh an t-ateangaire seo i gcónaí chomh maith le gineadóir cód JIT, ní hamháin go cainníochtúil i dtéarmaí luais, ach freisin go cáilíochtúil. Cé nach bhfuil mé cinnte go bhfuil a chur síos go hiomlán ábhartha.

Ar dtús rinne mé iarracht inneall TCG lán-chuimsitheach a dhéanamh, ach tháinig mearbhall orm go tapa sa chód foinse agus cur síos nach raibh iomlán soiléir ar na treoracha bytecode, mar sin shocraigh mé ateangaire TCI a fhilleadh. Thug sé seo roinnt buntáistí:

  • agus gineadóir cód á chur i bhfeidhm agat, d'fhéadfá breathnú ní ar an gcur síos ar na treoracha, ach ar an gcód ateangaire
  • is féidir leat feidhmeanna a ghiniúint nach bhfuil do gach bloc aistriúcháin a aimsítear, ach, mar shampla, tar éis an céadú forghníomhú
  • má athraíonn an cód ginte (agus is cosúil go bhfuil sé seo indéanta, ag meas na bhfeidhmeanna le hainmneacha ina bhfuil an paiste focal), beidh orm an cód JS ginte a neamhbhailiú, ach ar a laghad beidh rud éigin agam chun é a athghiniúint ó

Maidir leis an tríú pointe, níl mé cinnte go bhfuil paistí indéanta tar éis an cód a fhorghníomhú den chéad uair, ach is leor an chéad dá phointe.

Ar dtús, gineadh an cód i bhfoirm lasc mór ag seoladh an teagaisc bytecode bunaidh, ach ansin, ag cuimhneamh ar an alt faoi Emscripten, leas iomlán a bhaint as JS ginte agus relooping, chinn mé cód daonna níos mó a ghiniúint, go háirithe ós rud é go empirically. iompaigh sé amach gurb é an t-aon phointe iontrála isteach sa bhloc aistriúcháin ná a Tosaigh. Ní túisce a dúradh ná a rinneadh, tar éis tamaill bhí gineadóir cóid againn a ghin cód le ifs (cé nach raibh lúba ann). Ach droch-ádh, thuairt sé, ag tabhairt teachtaireacht go raibh na treoracha fad éigin mícheart. Thairis sin, ba é an teagasc deireanach ag an leibhéal athchúrsála seo brcond. Ceart go leor, cuirfidh mé seiceáil comhionann le giniúint an teagaisc seo roimh an nglao athchúrsach agus ina dhiaidh agus... níor cuireadh aon cheann acu i gcrích, ach tar éis an lasc dearbhaithe theip orthu fós. Sa deireadh, tar éis staidéar a dhéanamh ar an gcód ginte, thuig mé, tar éis an lasc, go ndéantar an pointeoir don teagasc reatha a athlódáil ón gcruach agus is dócha go bhfuil sé ró-scríofa ag an gcód JavaScript a ghintear. Agus mar sin d'éirigh sé amach. Níor tháinig rud ar bith ar an maolán a mhéadú ó mheigibheart amháin go deich, agus ba léir go raibh an gineadóir cód ag rith i gciorcail. Bhí orainn a sheiceáil nach ndeachaigh muid thar theorainneacha na heitinne reatha, agus má rinneamar, ansin seoladh an chéad TB eile a eisiúint le comhartha lúide ionas gur féidir linn leanúint ar aghaidh lena fhorghníomhú. Ina theannta sin, réitíonn sé seo an fhadhb “cé na feidhmeanna a ghintear ar cheart iad a neamhbhailiú má tá an píosa seo de sheachchód athraithe?” — ní gá ach an fheidhm a fhreagraíonn don bhloc aistriúcháin seo a neamhbhailiú. Dála an scéil, cé gur dhífhabhtaigh mé gach rud i gCróimiam (ós rud é go n-úsáideann mé Firefox agus go bhfuil sé níos éasca dom brabhsálaí ar leith a úsáid le haghaidh turgnaimh), chabhraigh Firefox liom neamh-chomhoiriúnacht leis an gcaighdeán asm.js a cheartú, agus ina dhiaidh sin thosaigh an cód ag obair níos tapúla i Cróimiam.

Sampla de chód ginte

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

Conclúid

Mar sin, níl an obair críochnaithe go fóill, ach tá mé tuirseach as an tógáil fadtéarmach seo a thabhairt chun foirfeachta go rúnda. Mar sin, chinn mé a bhfuil agam faoi láthair a fhoilsiú. Tá an cód beagán scanrúil in áiteanna, toisc gur turgnamh é seo, agus níl sé soiléir roimh ré cad is gá a dhéanamh. Is dócha, mar sin is fiú gnáth-ghealltanais adamhach a eisiúint anuas ar leagan níos nua-aimseartha de Qemu. Idir an dá linn, tá snáithe sa Gita i bhformáid bhlag: do gach “leibhéal” a ritheadh ​​ar bhealach éigin ar a laghad, tá tráchtaireacht mhionsonraithe i Rúisis curtha leis. I ndáiríre, tá an t-alt seo den chuid is mó ina athinsint ar an gconclúid git log.

Is féidir leat triail a bhaint as go léir anseo (bí san airdeall ar thrácht).

Cad atá ag obair cheana féin:

  • X86 próiseálaí fíorúil ag rith
  • Tá fréamhshamhail oibre de ghineadóir cód JIT ó chód meaisín go JavaScript
  • Tá teimpléad ann chun ailtireachtaí aoi 32-giotán eile a chur le chéile: faoi láthair is féidir leat Linux a mheas as reo ailtireacht MIPS sa bhrabhsálaí ag an gcéim lódála

Cad eile is féidir leat a dhéanamh

  • Luas suas aithrise. Fiú i mód JIT is cosúil go n-imíonn sé níos moille ná Virtual x86 (ach d'fhéadfadh go mbeadh Qemu iomlán ann le go leor crua-earraí agus ailtireachtaí aithrise)
  • Chun gnáthchomhéadan a dhéanamh - le fírinne, ní forbróir gréasáin maith mé, mar sin faoi láthair tá mé tar éis an bhlaosc caighdeánach Emscripten a athdhéanamh mar is fearr is féidir liom
  • Déan iarracht feidhmeanna níos casta Qemu a sheoladh - líonrú, imirce VM, etc.
  • Suas chun dáta: beidh ort do chúpla forbairtí agus tuairiscí fabht a chur faoi bhráid Emscripten réamhtheachtacha, mar a rinne póirtéirí Qemu agus tionscadail eile roimhe seo. Gabhaim buíochas leo as a bheith in ann a gcion le Emscripten a úsáid go hintuigthe mar chuid de mo thasc.

Foinse: will.com

Add a comment