Qemu.js JIT աջակցությամբ. դուք դեռ կարող եք շրջել աղացած միսը հետ

Մի քանի տարի առաջ Ֆաբրիս Բելարդ գրված է jslinux-ի կողմից ԱՀ էմուլյատոր է՝ գրված JavaScript-ով: Դրանից հետո գոնե ավելին էր Վիրտուալ x86. Բայց նրանք բոլորը, որքան ես գիտեմ, թարգմանիչներ էին, մինչդեռ Qemu-ն, որը շատ ավելի վաղ գրել է նույն Ֆաբրիս Բելարդը, և, հավանաբար, ցանկացած իրեն հարգող ժամանակակից էմուլատոր, օգտագործում է հյուրի կոդերի JIT կոմպիլյացիան հյուրընկալող համակարգի կոդի մեջ: Ինձ թվում էր, որ ժամանակն է իրականացնելու հակառակ առաջադրանքը՝ կապված այն բանի հետ, որը լուծում են բրաուզերները. JIT մեքենայական կոդի կոմպիլյացիան JavaScript-ում, որի համար ամենատրամաբանական էր թվում Qemu-ի տեղափոխումը: Թվում է, թե ինչու է Qemu-ն, կան ավելի պարզ և օգտագործողի համար հարմար էմուլյատորներ՝ նույն VirtualBox-ը, օրինակ, տեղադրված և աշխատում է: Սակայն Qemu-ն ունի մի քանի հետաքրքիր առանձնահատկություններ

  • բաց կոդով
  • առանց միջուկի վարորդի աշխատելու ունակություն
  • թարգմանչի ռեժիմում աշխատելու ունակություն
  • աջակցություն մեծ թվով ինչպես հյուրընկալող, այնպես էլ հյուր ճարտարապետների համար

Երրորդ կետի վերաբերյալ ես հիմա կարող եմ բացատրել, որ իրականում TCI ռեժիմում մեկնաբանվում են ոչ թե հյուրի մեքենայի հրահանգները, այլ դրանցից ստացված բայթկոդը, բայց դա չի փոխում էությունը՝ կառուցելու և գործարկելու համար: Qemu նոր ճարտարապետության վրա, եթե բախտդ բերի, բավական է C կոմպիլյատորը. կոդերի գեներատոր գրելը կարող է հետաձգվել:

Եվ հիմա, իմ ազատ ժամանակ Qemu-ի սկզբնաղբյուրի երկու տարի հանգիստ շփոթելուց հետո, հայտնվեց աշխատանքային նախատիպ, որում արդեն կարող եք գործարկել, օրինակ, Kolibri OS-ն:

Ինչ է Emscripten-ը

Մեր օրերում հայտնվել են բազմաթիվ կոմպիլյատորներ, որոնց վերջնական արդյունքը JavaScript-ն է։ Ոմանք, ինչպիսին է Type Script-ը, ի սկզբանե նախատեսված էր լինել համացանցի համար գրելու լավագույն միջոցը: Միևնույն ժամանակ, Emscripten-ը գոյություն ունեցող C կամ C++ կոդը վերցնելու և այն բրաուզերի կողմից ընթեռնելի ձևի մեջ հավաքելու միջոց է: Վրա այս էջը Մենք հավաքել ենք հայտնի ծրագրերի բազմաթիվ նավահանգիստներ. այստեղՕրինակ, դուք կարող եք նայել PyPy-ին, ի դեպ, նրանք պնդում են, որ արդեն ունեն JIT: Իրականում, ամեն ծրագիր չէ, որ կարող է պարզապես կազմվել և գործարկվել բրաուզերում. կան մի շարք Հատկություններ, որը դուք պետք է համակերպվեք, սակայն, քանի որ նույն էջի մակագրությունն ասում է. «Emscripten-ը կարող է օգտագործվել գրեթե ցանկացած կազմման համար. շարժական C/C++ կոդ JavaScript-ում»: Այսինքն՝ կան մի շարք գործողություններ, որոնք ստանդարտի համաձայն անորոշ վարքագիծ են, բայց սովորաբար աշխատում են x86-ի վրա, օրինակ՝ փոփոխականների չհավասարեցված մուտք, որն ընդհանրապես արգելված է որոշ ճարտարապետություններում: Ընդհանրապես: , Qemu-ն միջպլատֆորմային ծրագիր է և, ես ուզում էի հավատալ, և այն դեռևս շատ չսահմանված վարքագիծ չի պարունակում. վերցրեք այն և կազմեք, ապա մի փոքր հարեք JIT-ով, և դուք ավարտեցիք: Բայց դա այն չէ: գործ...

Փորձեք առաջինը

Ընդհանրապես, ես առաջին մարդը չեմ, ով միտք է հղացել Qemu-ն JavaScript-ում տեղափոխելու մասին: ReactOS ֆորումում հարց տրվեց, թե արդյոք դա հնարավոր է Emscripten-ի միջոցով: Նույնիսկ ավելի վաղ խոսակցություններ կային, որ Ֆաբրիս Բելարդն անձամբ է դա արել, բայց խոսքը jslinux-ի մասին էր, որը, որքան գիտեմ, ընդամենը JS-ում ձեռքով բավարար կատարման հասնելու փորձ է, և գրվել է զրոյից։ Ավելի ուշ գրվեց Virtual x86-ը, դրա համար տեղադրվեցին անորոշ աղբյուրներ, և, ինչպես ասվեց, էմուլյացիայի ավելի մեծ «իրատեսությունը» հնարավորություն տվեց օգտագործել SeaBIOS-ը որպես որոնվածը: Բացի այդ, եղել է Emscripten-ի միջոցով Qemu-ն տեղափոխելու առնվազն մեկ փորձ. ես փորձեցի դա անել socketpair, բայց զարգացումը, որքան հասկացա, սառեցված էր։

Այսպիսով, թվում է, ահա աղբյուրները, ահա Emscripten - վերցրեք այն և կազմեք: Բայց կան նաև գրադարաններ, որոնցից կախված է Քեմուն, և գրադարաններ, որոնցից կախված են այդ գրադարանները և այլն, և դրանցից մեկը. libffi, որից է կախված գլոբը։ Համացանցում լուրեր էին պտտվում, որ կա մեկը Emscripten-ի գրադարանների նավահանգիստների մեծ հավաքածուում, բայց ինչ-որ կերպ դժվար էր հավատալ. նախ՝ այն նախատեսված չէր լինել նոր կոմպիլյատոր, երկրորդ՝ այն չափազանց ցածր մակարդակի էր։ գրադարանը պարզապես վերցնելու և JS-ում կազմելու համար: Եվ դա միայն մոնտաժային ներդիրների խնդիր չէ, հավանաբար, եթե այն պտտեք, որոշ կանչող կոնվենցիաների համար դուք կարող եք ստեղծել անհրաժեշտ փաստարկները կույտի վրա և կանչել ֆունկցիան առանց դրանց: Բայց Emscripten-ը բարդ բան է. որպեսզի գեներացված կոդը ծանոթ տեսք ունենա բրաուզերի JS շարժիչի օպտիմիզատորին, օգտագործվում են որոշ հնարքներ: Մասնավորապես, այսպես կոչված relooping - կոդի գեներատոր, որն օգտագործում է ստացված LLVM IR-ը որոշ վերացական անցումային հրահանգներով, փորձում է վերստեղծել հավանական եթե-ներ, հանգույցներ և այլն: Դե, ինչպե՞ս են արգումենտները փոխանցվում ֆունկցիային: Բնականաբար, որպես արգումենտ JS ֆունկցիաների, այսինքն, եթե հնարավոր է, ոչ թե stack-ի միջոցով:

Սկզբում միտք կար պարզապես գրել libffi-ի փոխարինում JS-ով և գործարկել ստանդարտ թեստեր, բայց վերջում ես շփոթվեցի, թե ինչպես անել իմ վերնագրի ֆայլերը, որպեսզի նրանք աշխատեն գոյություն ունեցող կոդով. ինչ կարող եմ անել, ինչպես ասում են. «Արդյո՞ք առաջադրանքները այդքան բարդ են «Մենք այդքան հիմա՞ր ենք»: Ես ստիպված էի libffi-ին տեղափոխել մեկ այլ ճարտարապետություն, այսպես ասած, - բարեբախտաբար, Emscripten-ն ունի և՛ մակրոներ՝ ներկառուցված հավաքման համար (Javascript-ում, այո, ինչպիսին էլ որ լինի ճարտարապետությունը, այնպես որ՝ assembler-ը), և՛ արագորեն ստեղծվող կոդը գործարկելու հնարավորություն: Ընդհանրապես, որոշ ժամանակ հարթակից կախված libffi բեկորների հետ աշխատելուց հետո ես ստացա որոշ կոմպիլյատիվ կոդ և գործարկեցի այն առաջին թեստի ժամանակ, որին հանդիպեցի: Ի զարմանս ինձ, թեստը հաջող անցավ։ Իմ հանճարեղությունից ապշած - առանց կատակի, այն աշխատեց առաջին մեկնարկից - ես, դեռ չհավատալով աչքերիս, գնացի նորից նայելու ստացված ծածկագիրը, գնահատելու, թե որտեղ պետք է փորփրեմ: Այստեղ ես երկրորդ անգամ խելագարվեցի. միակ բանը, ինչ արեց իմ գործառույթը, դա էր ffi_call - սա հաղորդում է հաջող զանգ: Ինքնին զանգ չկար։ Այսպիսով, ես ուղարկեցի իմ առաջին խնդրանքը, որը ուղղեց թեստի սխալը, որը պարզ է ցանկացած օլիմպիադայի ուսանողի համար. իրական թվերը չպետք է համեմատվեն որպես a == b և նույնիսկ ինչպես a - b < EPS - Դուք նույնպես պետք է հիշեք մոդուլը, հակառակ դեպքում 0-ը կստացվի, որ շատ հավասար է 1/3-ի... Ընդհանրապես, ես եկել եմ libffi-ի որոշակի պորտ, որը անցնում է ամենապարզ թեստերը, և որի հետ glib-ը կազմել է - Ես որոշեցի, որ դա անհրաժեշտ է, հետո կավելացնեմ: Առաջ նայելով, կասեմ, որ, ինչպես պարզվեց, կոմպիլյատորը նույնիսկ libffi ֆունկցիան չի ներառել վերջնական կոդի մեջ։

Բայց, ինչպես արդեն ասացի, կան որոշ սահմանափակումներ, և զանազան չսահմանված վարքագծի ազատ օգտագործման մեջ թաքնված է ավելի տհաճ հատկանիշ՝ դիզայնով JavaScript-ը չի աջակցում բազմաթելային համատեղ հիշողության հետ: Սկզբունքորեն, սա սովորաբար կարելի է նույնիսկ լավ գաղափար անվանել, բայց ոչ կոդ տեղափոխելու համար, որի ճարտարապետությունը կապված է C թելերի հետ: Ընդհանուր առմամբ, Firefox-ը փորձարկում է համատեղ աշխատողներին աջակցելու համար, և Emscripten-ը նրանց համար ունի pthread իրականացում, բայց ես չէի ուզում կախված լինել դրանից: Ես ստիպված էի կամաց-կամաց արմատախիլ անել բազմաշերտությունը Qemu կոդից, այսինքն՝ պարզել, թե որտեղ են աշխատում թելերը, տեղափոխել այս թեմայում անցկացվող օղակի մարմինը առանձին ֆունկցիայի մեջ և այդպիսի գործառույթները մեկ առ մեկ կանչել հիմնական հանգույցից:

Երկրորդ փորձ

Ինչ-որ պահի պարզ դարձավ, որ խնդիրը դեռ կա, և որ կոդի շուրջ հենակները անկանոն խրվելը ոչ մի լավ բանի չի բերի: Եզրակացություն՝ պետք է ինչ-որ կերպ համակարգել հենակների ավելացման գործընթացը։ Հետևաբար վերցվեց 2.4.1 տարբերակը, որն այն ժամանակ թարմ էր (ոչ թե 2.5.0, որովհետև, ով գիտի, նոր տարբերակում դեռ չբռնված վրիպակներ կլինեն, և ես բավականաչափ ունեմ իմ սեփական սխալները. ), և առաջինը այն ապահով կերպով վերաշարադրելն էր thread-posix.c. Դե, այսինքն՝ նույնքան անվտանգ. եթե ինչ-որ մեկը փորձել է արգելափակման տանող գործողություն կատարել, ֆունկցիան անմիջապես կանչվել է abort() - Իհարկե, սա միանգամից չլուծեց բոլոր խնդիրները, բայց գոնե ինչ-որ կերպ ավելի հաճելի էր, քան անհամապատասխան տվյալներ անաղմուկ ստանալը։

Ընդհանրապես, Emscripten տարբերակները շատ օգտակար են JS կոդը տեղափոխելու համար -s ASSERTIONS=1 -s SAFE_HEAP=1 - նրանք որսում են չսահմանված վարքագծի որոշ տեսակներ, ինչպիսիք են զանգերը դեպի չհավասարեցված հասցեներ (ինչը բոլորովին համահունչ չէ մուտքագրված զանգվածների կոդի հետ, ինչպիսիք են. HEAP32[addr >> 2] = 1) կամ արգումենտների սխալ քանակով ֆունկցիա կանչելը:

Ի դեպ, հավասարեցման սխալներն առանձին խնդիր են։ Ինչպես արդեն ասացի, Qemu-ն ունի «դեգեներատիվ» մեկնաբանություն TCI-ի (փոքր կոդերի թարգմանիչ) մեկնաբանման համար, և նոր ճարտարապետության վրա Qemu-ն կառուցելու և գործարկելու համար, եթե հաջողակ եք, բավական է C կոմպիլյատորը: Հիմնաբառեր «եթե դու բախտավոր ես». Իմ բախտը չբերեց, և պարզվեց, որ TCI-ն օգտագործում է չհավասարեցված մուտք իր բայթկոդը վերլուծելիս: Այսինքն, բոլոր տեսակի ARM-ների և պարտադիր հարթեցված մուտք ունեցող այլ ճարտարապետությունների վրա, Qemu-ն հավաքում է, քանի որ նրանք ունեն նորմալ TCG backend, որը ստեղծում է հայրենի կոդ, բայց արդյոք TCI-ն կաշխատի դրանց վրա, այլ հարց է: Այնուամենայնիվ, ինչպես պարզվեց, TCI-ի փաստաթղթերը հստակորեն ցույց էին տալիս նման բան: Արդյունքում կոդի մեջ ավելացվել են ֆունկցիայի կանչեր՝ չհավասարեցված ընթերցման համար, որոնք հայտնաբերվել են Քեմուի մեկ այլ հատվածում։

Կույտի ոչնչացում

Արդյունքում շտկվեց TCI-ի չհամապատասխանեցված մուտքը, ստեղծվեց հիմնական հանգույց, որն իր հերթին կոչեց պրոցեսոր, RCU և մի քանի այլ մանր բաներ: Եվ այսպես, ես գործարկում եմ Qemu-ն տարբերակով -d exec,in_asm,out_asm, ինչը նշանակում է, որ դուք պետք է ասեք, թե որ կոդի բլոկներն են կատարվում, ինչպես նաև հեռարձակման պահին գրել, թե ինչ է հյուրի կոդը, ինչ է դարձել հոսթ կոդը (այս դեպքում՝ բայթկոդ)։ Այն սկսում է, կատարում է մի քանի թարգմանական բլոկներ, գրում է վրիպազերծման հաղորդագրությունը, որը թողել եմ, որ RCU-ն այժմ կսկսի և... խափանում է: abort() ֆունկցիայի ներսում free(). Ֆունկցիայի հետ շփվելով free() Մեզ հաջողվեց պարզել, որ կույտային բլոկի վերնագրում, որը գտնվում է հատկացված հիշողությանը նախորդող ութ բայթերում, բլոկի չափի կամ նմանատիպ այլ բանի փոխարեն աղբ է եղել։

Կույտի ոչնչացում - ինչ սրամիտ... Նման դեպքում կա մի օգտակար միջոց՝ (եթե հնարավոր է) նույն աղբյուրներից, հավաքեք բնիկ երկուական և գործարկեք այն Valgrind-ի տակ։ Որոշ ժամանակ անց երկուականը պատրաստ էր: Ես գործարկում եմ այն ​​նույն տարբերակներով. այն խափանում է նույնիսկ սկզբնավորման ժամանակ, մինչև իրականում հասնելը: Դա տհաճ է, իհարկե, - ըստ երևույթին, աղբյուրները նույնը չէին, ինչը զարմանալի չէ, քանի որ կարգավորելը մի փոքր տարբեր տարբերակներ է հայտնաբերել, բայց ես ունեմ Valgrind. նախ ես կուղղեմ այս սխալը, իսկ հետո, եթե բախտս բերի: , կհայտնվի բնօրինակը։ Ես աշխատում եմ նույն բանը Valgrind-ի ներքո... Y-y-y, y-y-y, Uh-uh, այն սկսվեց, անցավ սկզբնականացման միջով նորմալ և անցավ սկզբնական սխալի միջով առանց որևէ նախազգուշացման սխալ հիշողության հասանելիության մասին, էլ չեմ խոսում ընկնելու մասին: Կյանքը, ինչպես ասում են, ինձ չպատրաստեց դրան. վթարային ծրագիրը դադարում է խափանվել, երբ գործարկվում է Ուոլգրինդի ներքո: Ինչ էր դա առեղծված է: Իմ վարկածն այն է, որ մի անգամ ընթացիկ հրահանգի մոտակայքում՝ սկզբնավորման ժամանակ վթարից հետո, gdb-ն ցույց տվեց աշխատանք memset-a վավեր ցուցիչով կամ օգտագործելով mmx, կամ xmm գրանցում է, ապա միգուցե դա ինչ-որ դասավորվածության սխալ էր, թեև դեռ դժվար է հավատալ:

Լավ, Վալգրինդը կարծես թե չի օգնում այստեղ: Եվ այստեղ սկսվեց ամենազզվելին. ամեն ինչ կարծես թե նույնիսկ սկսվում է, բայց խափանում է բացարձակապես անհայտ պատճառներով մի իրադարձության պատճառով, որը կարող էր տեղի ունենալ միլիոնավոր հրահանգներ առաջ: Երկար ժամանակ նույնիսկ պարզ չէր, թե ինչպես մոտենալ։ Ի վերջո, ես դեռ պետք է նստեի և կարգաբերեի: Տպելը, թե ինչով վերագրված է վերնագիրը, ցույց տվեց, որ այն թվի տեսք չունի, այլ ինչ-որ երկուական տվյալների: Եվ ահա, այս երկուական տողը գտնվել է BIOS ֆայլում, այսինքն՝ հիմա կարելի էր ողջամիտ վստահությամբ ասել, որ դա բուֆերի գերհոսք է, և նույնիսկ պարզ է, որ այն գրված է այս բուֆերում։ Դե, ուրեմն նման բան. Emscripten-ում, բարեբախտաբար, հասցեների տարածության պատահականացում չկա, դրա մեջ նույնպես անցքեր չկան, այնպես որ կարող եք գրել ինչ-որ տեղ կոդի մեջտեղում՝ վերջին մեկնարկից տվյալները ցուցիչով դուրս բերելու համար, նայեք տվյալներին, նայեք ցուցիչին և, եթե այն չի փոխվել, մտածելու տեղիք տվեք: Ճիշտ է, ցանկացած փոփոխությունից հետո հղումը մի երկու րոպե է տևում, բայց ի՞նչ կարող ես անել։ Արդյունքում, գտնվեց հատուկ տող, որը BIOS-ը պատճենեց ժամանակավոր բուֆերից դեպի հյուրի հիշողություն, և, իրոք, բուֆերում բավարար տեղ չկար: Այդ տարօրինակ բուֆերային հասցեի աղբյուրը գտնելը հանգեցրեց ֆունկցիայի qemu_anon_ram_alloc ֆայլում oslib-posix.c - Տրամաբանությունը հետևյալն էր. երբեմն կարող է օգտակար լինել հասցեն հավասարեցնել 2 ՄԲ չափի հսկայական էջին, դրա համար մենք կխնդրենք. mmap սկզբում մի փոքր ավելին, իսկ հետո ավելցուկը կվերադարձնենք օգնությամբ munmap. Իսկ եթե նման հավասարեցում չի պահանջվում, ապա 2 ՄԲ-ի փոխարեն կնշենք արդյունքը getpagesize() - mmap այն դեռ կտրամադրի համապատասխան հասցե... Այսպիսով, Emscripten-ում mmap պարզապես զանգեր malloc, բայց, իհարկե, այն չի համընկնում էջի վրա: Ընդհանրապես, մի ​​սխալ, որը մի քանի ամիս հիասթափեցրեց ինձ, շտկվեց դրա փոփոխությամբ երկու տողեր.

Զանգահարելու գործառույթների առանձնահատկությունները

Եվ հիմա պրոցեսորը ինչ-որ բան է հաշվում, Qemu-ն չի խափանում, բայց էկրանը չի միանում, և պրոցեսորը արագորեն անցնում է օղակների ՝ դատելով ելքից: -d exec,in_asm,out_asm. Հիպոթեզ է առաջացել՝ ժամանակաչափի ընդհատումները (կամ, ընդհանրապես, բոլոր ընդհատումները) չեն գալիս։ Եվ իրոք, եթե արձակեք հայրենի ժողովի ընդհատումները, որոնք ինչ-ինչ պատճառներով աշխատեցին, կստանաք նմանատիպ պատկեր։ Բայց սա ամենևին էլ պատասխանը չէր. վերը նշված տարբերակի հետ թողարկված հետքերի համեմատությունը ցույց տվեց, որ կատարման հետագծերը շատ շուտ են շեղվել։ Այստեղ պետք է ասել, որ համեմատությունը, թե ինչ է արձանագրվել գործարկիչի միջոցով emrun Արդյունքների վրիպազերծումը բնօրինակ հավաքի արդյունքով ամբողջովին մեխանիկական գործընթաց չէ: Ես հստակ չգիտեմ, թե ինչպես է բրաուզերում աշխատող ծրագիրը միանում emrun, բայց ելքի որոշ գծեր պարզվում է, որ վերադասավորվել են, ուստի տարբերության տարբերությունը դեռևս հիմք չէ ենթադրելու, որ հետագծերը շեղվել են: Ընդհանուր առմամբ պարզ դարձավ, որ հրահանգների համաձայն ljmpl կա անցում դեպի տարբեր հասցեներ, և գեներացված բայթկոդը սկզբունքորեն տարբերվում է. մեկը պարունակում է օգնական ֆունկցիա կանչելու հրահանգ, մյուսը՝ ոչ: Հրահանգները գուգլելուց և այս հրահանգները թարգմանող ծածկագիրը ուսումնասիրելուց հետո պարզ դարձավ, որ նախևառաջ անմիջապես գրանցամատյանում. cr0 արվել է ձայնագրություն՝ օգտագործելով նաև օգնական, որը պրոցեսորն անցել է պաշտպանված ռեժիմի, և երկրորդ՝ js տարբերակը երբեք չի անցել պաշտպանված ռեժիմի։ Բայց փաստն այն է, որ Emscripten-ի մեկ այլ առանձնահատկությունը նրա դժկամությունն է հանդուրժել այնպիսի ծածկագրեր, ինչպիսիք են հրահանգների կատարումը: call TCI-ում, որի տեսակը ստացվում է ցանկացած ֆունկցիայի ցուցիչով long long f(int arg0, .. int arg9) - ֆունկցիաները պետք է կանչվեն արգումենտների ճիշտ քանակով: Եթե ​​այս կանոնը խախտվի, կախված վրիպազերծման կարգավորումներից, ծրագիրը կա՛մ կխափանվի (ինչը լավ է), կա՛մ ընդհանրապես կկանչի սխալ ֆունկցիա (ինչը տխուր կլինի վրիպազերծման համար): Կա նաև երրորդ տարբերակ՝ միացնել փաթաթիչների սերունդը, որոնք ավելացնում/հեռացնում են արգումենտները, բայց ընդհանուր առմամբ այս փաթաթանները շատ տեղ են զբաղեցնում, չնայած այն հանգամանքին, որ իրականում ինձ ընդամենը հարյուրից մի փոքր ավելի փաթաթա է պետք: Միայն սա շատ տխուր է, բայց պարզվեց, որ ավելի լուրջ խնդիր կա. wrapper ֆունկցիաների գեներացված կոդում արգումենտները փոխարկվում և փոխակերպվում էին, բայց երբեմն գեներացված արգումենտներով ֆունկցիան չէր կանչվում. իմ libffi իրականացումը. Այսինքն՝ որոշ օգնականներ պարզապես մահապատժի չեն ենթարկվել։

Բարեբախտաբար, Qemu-ն ունի մեքենայական ընթեռնելի օգնականների ցուցակներ վերնագրի ֆայլի տեսքով, ինչպիսին է.

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

Դրանք բավականին զվարճալի են օգտագործվում. նախ՝ մակրոները վերաիմաստավորվում են ամենատարօրինակ ձևով DEF_HELPER_n, այնուհետև միանում է helper.h. Այնքանով, որքանով մակրոն ընդլայնվում է կառուցվածքի սկզբնավորիչի և ստորակետի մեջ, այնուհետև սահմանվում է զանգված, և տարրերի փոխարեն՝ #include <helper.h> Արդյունքում ես վերջապես հնարավորություն ունեցա փորձելու գրադարանը աշխատավայրում պիպարսինգ, և գրվեց մի սցենար, որը ստեղծում է հենց այդ փաթաթանները հենց այն գործառույթների համար, որոնց համար դրանք անհրաժեշտ են:

Եվ այսպես, դրանից հետո պրոցեսորը կարծես թե աշխատում էր։ Թվում է, թե այն պատճառով, որ էկրանը երբեք չի սկզբնավորվել, թեև memtest86+-ը կարողացավ գործարկել բնօրինակ ժողովում: Այստեղ անհրաժեշտ է պարզաբանել, որ Qemu բլոկի I/O կոդը գրված է կորուտիններով։ Emscripten-ն ունի իր շատ բարդ իրականացումը, բայց այն դեռ պետք է աջակցվեր Qemu կոդը, և դուք կարող եք այժմ վրիպազերծել պրոցեսորը. Qemu-ն աջակցում է ընտրանքներին: -kernel, -initrd, -append, որով կարող եք բեռնել Linux-ը կամ, օրինակ, memtest86+-ը՝ ընդհանրապես առանց բլոկ սարքեր օգտագործելու։ Բայց ահա խնդիրը. հայրենի ժողովում կարելի էր տեսնել Linux միջուկի ելքը դեպի կոնսոլ՝ տարբերակով. -nographic, և ոչ մի ելք բրաուզերից դեպի տերմինալ, որտեղից այն գործարկվել է emrun, չեկավ։ Այսինքն՝ պարզ չէ՝ պրոցեսորը չի աշխատում կամ գրաֆիկական ելքը չի աշխատում։ Եվ հետո մտքովս անցավ մի քիչ սպասել։ Պարզվեց, որ «պրոցեսորը չի քնում, այլ պարզապես դանդաղ թարթում է», և մոտ հինգ րոպե անց միջուկը հաղորդագրությունների մի փունջ նետեց վահանակի վրա և շարունակեց կախվել: Պարզ դարձավ, որ պրոցեսորը, ընդհանուր առմամբ, աշխատում է, և մենք պետք է փորփրենք SDL2-ով աշխատելու կոդը։ Ցավոք, ես չգիտեմ, թե ինչպես օգտագործել այս գրադարանը, ուստի որոշ տեղերում ես ստիպված էի պատահականորեն գործել: Ինչ-որ պահի էկրանին կապույտ ֆոնի վրա փայլատակեց parallel0 գիծը, որը որոշ մտքեր էր հուշում։ Ի վերջո, պարզվեց, որ խնդիրն այն էր, որ Qemu-ն մի ֆիզիկական պատուհանում բացում է մի քանի վիրտուալ պատուհան, որոնց միջև կարելի է անցնել Ctrl-Alt-n-ի միջոցով. այն աշխատում է մայրենի Build-ում, բայց ոչ Emscripten-ում։ Ավելորդ պատուհաններից ազատվելուց հետո՝ օգտագործելով ընտրանքները -monitor none -parallel none -serial none և հրահանգներ՝ ստիպողաբար վերագծել ամբողջ էկրանը յուրաքանչյուր կադրի վրա, ամեն ինչ հանկարծ աշխատեց:

Կորուտիններ

Այսպիսով, զննարկիչում էմուլյացիան աշխատում է, բայց դուք չեք կարող դրանում որևէ հետաքրքիր մեկ անգործունյա բան գործարկել, քանի որ մուտքի/ելքի բլոկ չկա, դուք պետք է աջակցեք կորուտիններին: Qemu-ն արդեն ունի մի քանի կորուտինային հետնամասեր, բայց JavaScript-ի և Emscripten կոդերի գեներատորի բնույթի պատճառով դուք չեք կարող պարզապես սկսել ձեռնածություն կույտերով: Թվում է, թե «ամեն ինչ գնացել է, գիպսը հանվում է», բայց Emscripten-ի մշակողները արդեն հոգացել են ամեն ինչի մասին։ Սա բավականին զվարճալի է իրականացվում. եկեք նման ֆունկցիայի կանչը անվանենք կասկածելի emscripten_sleep և մի քանի ուրիշներ, որոնք օգտագործում են Asyncify մեխանիզմը, ինչպես նաև ցուցիչի զանգեր և զանգեր ցանկացած ֆունկցիայի, որտեղ նախորդ երկու դեպքերից մեկը կարող է առաջանալ ավելի ներքև: Եվ հիմա, յուրաքանչյուր կասկածելի զանգից առաջ, մենք կընտրենք համաժամեցված համատեքստ, և զանգից անմիջապես հետո կստուգենք, թե արդյոք տեղի է ունեցել ասինխրոն զանգ, և եթե այն տեղի է ունեցել, մենք կպահենք բոլոր տեղական փոփոխականները այս համաժամեցված համատեքստում, նշենք, թե որ գործառույթը: փոխանցել վերահսկողությունը, երբ մենք պետք է շարունակենք կատարումը և դուրս գանք ընթացիկ ֆունկցիայից: Այստեղ էֆեկտն ուսումնասիրելու հնարավորություն կա վատնել — ասինխրոն զանգից վերադառնալուց հետո կոդի գործարկումը շարունակելու կարիքների համար, կոմպիլյատորը ստեղծում է ֆունկցիայի «անմուշներ», որոնք սկսվում են կասկածելի զանգից հետո, այսպես. եթե կա n կասկածելի զանգ, ապա ֆունկցիան կընդլայնվի ինչ-որ տեղ n/2: times — սա դեռ, եթե ոչ Հիշեք, որ յուրաքանչյուր պոտենցիալ ասինխրոն զանգից հետո դուք պետք է սկզբնական ֆունկցիային ավելացնեք որոշ տեղական փոփոխականներ: Այնուհետև ես նույնիսկ ստիպված էի գրել մի պարզ սցենար Python-ում, որը, հիմնվելով հատուկ գերօգտագործված գործառույթների որոշակի շարքի վրա, որոնք իբր «թույլ չեն տալիս ասինխրոնությանը անցնել իրենց միջով» (այսինքն՝ շարային առաջխաղացումը և այն ամենը, ինչ ես նկարագրեցի, չեն աշխատել դրանցում), ցույց է տալիս ցուցիչների միջոցով զանգեր, որոնցում գործառույթները պետք է անտեսվեն կոմպիլյատորի կողմից, որպեսզի այդ գործառույթները չհամարվեն ասինխրոն: Եվ հետո 60 ՄԲ-ից ցածր JS ֆայլերը ակնհայտորեն շատ են, ասենք առնվազն 30: Չնայած, մի անգամ ես տեղադրում էի հավաքման սցենար և պատահաբար դուրս նետեցի կապող տարբերակները, որոնց թվում էր. -O3. Ես գործարկում եմ ստեղծված կոդը, և Chromium-ը խլում է հիշողությունը և խափանում: Հետո պատահաբար նայեցի, թե ինչ էր նա փորձում ներբեռնել... Դե, ինչ կարող եմ ասել, ես նույնպես կսառցեի, եթե ինձ խնդրեին մտածված ուսումնասիրել և օպտիմալացնել 500+ ՄԲ Javascript-ը:

Ցավոք, Asyncify-ի աջակցության գրադարանի կոդի ստուգումները լիովին բարեկամական չէին longjmp-ներ, որոնք օգտագործվում են վիրտուալ պրոցեսորի կոդում, բայց մի փոքրիկ կարկատումից հետո, որն անջատում է այդ ստուգումները և ուժով վերականգնում են համատեքստերը, կարծես ամեն ինչ լավ է, կոդը աշխատեց: Եվ հետո սկսվեց տարօրինակ բան. երբեմն գործարկվում էին համաժամացման կոդի ստուգումներ. նույն նրանք, որոնք խափանում են կոդը, եթե, ըստ կատարման տրամաբանության, այն պետք է արգելափակվի. ինչ-որ մեկը փորձել է գրավել արդեն իսկ գրավված մուտեքսը: Բարեբախտաբար, պարզվեց, որ սա տրամաբանական խնդիր չէր սերիական կոդում. ես պարզապես օգտագործում էի Emscripten-ի կողմից տրամադրված ստանդարտ հիմնական հանգույցի գործառույթը, բայց երբեմն ասինխրոն զանգը ամբողջությամբ բացում էր կույտը, և այդ պահին այն ձախողվում էր: setTimeout հիմնական հանգույցից - այսպիսով, ծածկագիրը մտել է հիմնական հանգույցի կրկնություն՝ չհեռանալով նախորդ կրկնությունից: Վերագրել է անսահման օղակի վրա և emscripten_sleep, և mutexes-ի հետ կապված խնդիրները դադարեցին: Կոդը նույնիսկ ավելի տրամաբանական է դարձել, ի վերջո, ես իրականում չունեմ կոդ, որը պատրաստում է հաջորդ անիմացիոն շրջանակը. պրոցեսորը պարզապես ինչ-որ բան է հաշվարկում, և էկրանը պարբերաբար թարմացվում է: Այնուամենայնիվ, խնդիրները դրանով չեն սահմանափակվել. երբեմն Qemu-ի կատարումը պարզապես լուռ ավարտվում էր՝ առանց բացառությունների կամ սխալների: Այդ պահին ես հրաժարվեցի դրանից, բայց, առաջ նայելով, կասեմ, որ խնդիրը հետևյալն էր. կորուտին կոդը, փաստորեն, չի օգտագործվում setTimeout (կամ գոնե ոչ այնքան հաճախ, որքան դուք կարող եք մտածել). գործառույթ emscripten_yield պարզապես սահմանում է ասինխրոն զանգի դրոշակը: Ամբողջ խնդիրն այն է, որ emscripten_coroutine_next Ասինխրոն ֆունկցիա չէ. ներքին կարգով այն ստուգում է դրոշը, վերակայում է այն և փոխանցում է հսկողությունը այնտեղ, որտեղ անհրաժեշտ է: Այսինքն, stack-ի առաջխաղացումը ավարտվում է այնտեղ։ Խնդիրն այն էր, որ «առանց օգտագործման» պատճառով, որը հայտնվեց այն ժամանակ, երբ կորուտինային լողավազանն անջատված էր, քանի որ ես չեմ պատճենել կոդերի կարևոր տողը գոյություն ունեցող կորուտինային հետնամասից, գործառույթը qemu_in_coroutine վերադարձել է true, երբ իրականում այն ​​պետք է վերադարձներ կեղծ: Սա հանգեցրեց զանգի emscripten_yield, որի վերևում ոչ ոք չկար դարակի վրա emscripten_coroutine_next, բուրգը բացվեց մինչև վերև, բայց ոչ setTimeout, ինչպես արդեն ասացի, չի ցուցադրվել։

JavaScript կոդի ստեղծում

Եվ ահա, փաստորեն, խոստացված «աղացած միսը հետ շրջելը»։ Իրականում ոչ: Իհարկե, եթե մենք գործարկենք Qemu-ն բրաուզերում, իսկ Node.js-ը դրանում, ապա, բնականաբար, Qemu-ում կոդ ստեղծելուց հետո մենք բոլորովին սխալ JavaScript կստանանք։ Բայց, այնուամենայնիվ, ինչ-որ հակադարձ փոխակերպում:

Նախ, մի փոքր այն մասին, թե ինչպես է աշխատում Քեմուն: Խնդրում եմ անմիջապես ներիր ինձ. ես պրոֆեսիոնալ Qemu ծրագրավորող չեմ, և իմ եզրակացությունները կարող են որոշ տեղերում սխալ լինել: Ինչպես ասում են, «աշակերտի կարծիքը պարտադիր չէ, որ համընկնի ուսուցչի կարծիքի, Պեանոյի աքսիոմատիկության և ողջախոհության հետ»: Qemu-ն ունի որոշակի թվով աջակցվող հյուր ճարտարապետներ և յուրաքանչյուրի համար կա նման գրացուցակ target-i386. Կառուցելիս կարող եք նշել մի քանի հյուր ճարտարապետների աջակցություն, բայց արդյունքը կլինի ընդամենը մի քանի երկուական: Հյուրերի ճարտարապետությանը աջակցելու կոդը, իր հերթին, առաջացնում է որոշ ներքին Qemu գործողություններ, որոնք TCG-ն (Tiny Code Generator) արդեն վերածում է մեքենայական կոդի՝ հյուրընկալող ճարտարապետության համար: Ինչպես նշված է tcg գրացուցակում գտնվող readme ֆայլում, սա ի սկզբանե սովորական C կոմպիլյատորի մի մասն էր, որը հետագայում հարմարեցվեց JIT-ի համար: Հետևաբար, օրինակ, թիրախային ճարտարապետությունն այս փաստաթղթի առումով այլևս հյուրի ճարտարապետություն չէ, այլ հյուրընկալող ճարտարապետություն: Ինչ-որ պահի հայտնվեց մեկ այլ բաղադրիչ՝ Tiny Code Interpreter (TCI), որը պետք է կատարի կոդը (գրեթե նույն ներքին գործառնությունները) կոնկրետ հյուրընկալող ճարտարապետության համար կոդ գեներատորի բացակայության դեպքում: Իրականում, ինչպես ասվում է նրա փաստաթղթերում, այս թարգմանիչը կարող է ոչ միշտ գործել այնպես, ինչպես JIT կոդերի գեներատորը, ոչ միայն քանակապես արագության, այլ նաև որակապես: Չնայած ես վստահ չեմ, որ նրա նկարագրությունը լիովին տեղին է։

Սկզբում ես փորձեցի լիարժեք TCG backend պատրաստել, բայց արագ շփոթվեցի սկզբնաղբյուրի և բայթկոդի հրահանգների ոչ ամբողջովին պարզ նկարագրության մեջ, ուստի որոշեցի փաթաթել TCI թարգմանիչը: Սա տվեց մի քանի առավելություններ.

  • Կոդի գեներատորի ներդրման ժամանակ դուք կարող եք դիտել ոչ թե հրահանգների նկարագրությունը, այլ թարգմանչի կոդը
  • Դուք կարող եք գործառույթներ ստեղծել ոչ թե հանդիպած յուրաքանչյուր թարգմանության բլոկի համար, այլ, օրինակ, միայն հարյուրերորդ կատարումից հետո
  • եթե գեներացված կոդը փոխվի (և դա հնարավոր է թվում, դատելով կարկատել բառը պարունակող անուններով ֆունկցիաներից), ինձ անհրաժեշտ կլինի անվավեր ճանաչել ստեղծված JS կոդը, բայց գոնե ինչ-որ բան կունենամ այն ​​վերականգնելու համար։

Ինչ վերաբերում է երրորդ կետին, ես վստահ չեմ, որ կոդն առաջին անգամ կատարելուց հետո հնարավոր է կարկատել, բայց առաջին երկու կետերը բավարար են։

Սկզբում կոդը ստեղծվում էր մեծ անջատիչի տեսքով սկզբնական բայթկոդի հրահանգի հասցեով, բայց հետո, հիշելով հոդվածը Emscripten-ի, գեներացված JS-ի օպտիմալացման և վերաշրջման մասին, ես որոշեցի ստեղծել ավելի շատ մարդկային կոդ, հատկապես որ էմպիրիկորեն այն պարզվեց, որ թարգմանության բլոկի միակ մուտքի կետը դրա սկիզբն է: Որոշ ժամանակ անց մենք ունեինք կոդերի գեներատոր, որը գեներացնում էր կոդ if-ներով (թեև առանց հանգույցների): Բայց դժբախտություն, այն վթարի ենթարկվեց՝ հաղորդագրություն տալով, որ հրահանգները ինչ-որ սխալ երկարության են: Ավելին, այս ռեկուրսիոն մակարդակի վերջին հրահանգն էր brcond. Լավ, ես կավելացնեմ նույնական ստուգում այս հրահանգի գեներացմանը ռեկուրսիվ զանգից առաջ և հետո, և… դրանցից ոչ մեկը չի կատարվել, բայց հաստատման անջատիչից հետո դրանք դեռ ձախողվել են: Ի վերջո, գեներացված կոդը ուսումնասիրելուց հետո ես հասկացա, որ անջատումից հետո ընթացիկ հրահանգի ցուցիչը վերաբեռնվում է ստեկից և հավանաբար վերագրվում է գեներացված JavaScript կոդով։ Եվ այսպես ստացվեց. Բուֆերը մեկ մեգաբայթից տասը բարձրացնելը ոչ մի բանի չհանգեցրեց, և պարզ դարձավ, որ կոդերի գեներատորը աշխատում է շրջանաձև: Մենք պետք է ստուգեինք, որ մենք դուրս չենք եկել ներկայիս տուբերկուլյոզի սահմաններից, և եթե դուրս ենք եկել, ապա մինուս նշանով թողարկենք հաջորդ տուբերկուլյոզի հասցեն, որպեսզի կարողանանք շարունակել կատարումը: Բացի այդ, սա լուծում է այն խնդիրը, «որ գեներացված գործառույթները պետք է անվավեր ճանաչվեն, եթե բայթկոդի այս հատվածը փոխվել է»: — միայն այն ֆունկցիան, որը համապատասխանում է այս թարգմանության բլոկին, պետք է անվավեր ճանաչվի: Ի դեպ, չնայած ես Chromium-ում ամեն ինչ կարգաբերեցի (քանի որ ես օգտագործում եմ Firefox և ինձ համար ավելի հեշտ է օգտագործել առանձին բրաուզեր փորձերի համար), Firefox-ն ինձ օգնեց շտկել անհամատեղելիությունները asm.js ստանդարտի հետ, որից հետո կոդը սկսեց ավելի արագ աշխատել։ Chromium.

Ստեղծված կոդի օրինակ

Compiling 0x15b46d0:
CompiledTB[0x015b46d0] = function(stdlib, ffi, heap) {
"use asm";
var HEAP8 = new stdlib.Int8Array(heap);
var HEAP16 = new stdlib.Int16Array(heap);
var HEAP32 = new stdlib.Int32Array(heap);
var HEAPU8 = new stdlib.Uint8Array(heap);
var HEAPU16 = new stdlib.Uint16Array(heap);
var HEAPU32 = new stdlib.Uint32Array(heap);

var dynCall_iiiiiiiiiii = ffi.dynCall_iiiiiiiiiii;
var getTempRet0 = ffi.getTempRet0;
var badAlignment = ffi.badAlignment;
var _i64Add = ffi._i64Add;
var _i64Subtract = ffi._i64Subtract;
var Math_imul = ffi.Math_imul;
var _mul_unsigned_long_long = ffi._mul_unsigned_long_long;
var execute_if_compiled = ffi.execute_if_compiled;
var getThrew = ffi.getThrew;
var abort = ffi.abort;
var qemu_ld_ub = ffi.qemu_ld_ub;
var qemu_ld_leuw = ffi.qemu_ld_leuw;
var qemu_ld_leul = ffi.qemu_ld_leul;
var qemu_ld_beuw = ffi.qemu_ld_beuw;
var qemu_ld_beul = ffi.qemu_ld_beul;
var qemu_ld_beq = ffi.qemu_ld_beq;
var qemu_ld_leq = ffi.qemu_ld_leq;
var qemu_st_b = ffi.qemu_st_b;
var qemu_st_lew = ffi.qemu_st_lew;
var qemu_st_lel = ffi.qemu_st_lel;
var qemu_st_bew = ffi.qemu_st_bew;
var qemu_st_bel = ffi.qemu_st_bel;
var qemu_st_leq = ffi.qemu_st_leq;
var qemu_st_beq = ffi.qemu_st_beq;

function tb_fun(tb_ptr, env, sp_value, depth) {
  tb_ptr = tb_ptr|0;
  env = env|0;
  sp_value = sp_value|0;
  depth = depth|0;
  var u0 = 0, u1 = 0, u2 = 0, u3 = 0, result = 0;
  var r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0;
  var r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0, r16 = 0, r17 = 0, r18 = 0, r19 = 0;
  var r20 = 0, r21 = 0, r22 = 0, r23 = 0, r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0;
  var r30 = 0, r31 = 0, r41 = 0, r42 = 0, r43 = 0, r44 = 0;
    r14 = env|0;
    r15 = sp_value|0;
  START: do {
    r0 = HEAPU32[((r14 + (-4))|0) >> 2] | 0;
    r42 = 0;
    result = ((r0|0) != (r42|0))|0;
    HEAPU32[1445307] = r0;
    HEAPU32[1445321] = r14;
    if(result|0) {
    HEAPU32[1445322] = r15;
    return 0x0345bf93|0;
    }
    r0 = HEAPU32[((r14 + (16))|0) >> 2] | 0;
    r42 = 8;
    r0 = ((r0|0) - (r42|0))|0;
    HEAPU32[(r14 + (16)) >> 2] = r0;
    r1 = 8;
    HEAPU32[(r14 + (44)) >> 2] = r1;
    r1 = r0|0;
    HEAPU32[(r14 + (40)) >> 2] = r1;
    r42 = 4;
    r0 = ((r0|0) + (r42|0))|0;
    r2 = HEAPU32[((r14 + (24))|0) >> 2] | 0;
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    HEAPU32[1445309] = r2;
    HEAPU32[1445321] = r14;
    HEAPU32[1445322] = r15;
    qemu_st_lel(env|0, r0|0, r2|0, 34, 22759218);
if(getThrew() | 0) abort();
    r0 = 3241038392;
    HEAPU32[1445307] = r0;
    r0 = qemu_ld_leul(env|0, r0|0, 34, 22759233)|0;
if(getThrew() | 0) abort();
    HEAPU32[(r14 + (24)) >> 2] = r0;
    r1 = HEAPU32[((r14 + (12))|0) >> 2] | 0;
    r2 = HEAPU32[((r14 + (40))|0) >> 2] | 0;
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    HEAPU32[1445309] = r2;
    qemu_st_lel(env|0, r2|0, r1|0, 34, 22759265);
if(getThrew() | 0) abort();
    r0 = HEAPU32[((r14 + (24))|0) >> 2] | 0;
    HEAPU32[(r14 + (40)) >> 2] = r0;
    r1 = 24;
    HEAPU32[(r14 + (52)) >> 2] = r1;
    r42 = 0;
    result = ((r0|0) == (r42|0))|0;
    if(result|0) {
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    }
    HEAPU32[1445307] = r0;
    HEAPU32[1445308] = r1;
    return execute_if_compiled(22759392|0, env|0, sp_value|0, depth|0) | 0;
    return execute_if_compiled(23164080|0, env|0, sp_value|0, depth|0) | 0;
    break;
  } while(1); abort(); return 0|0;
}
return {tb_fun: tb_fun};
}(window, CompilerFFI, Module.buffer)["tb_fun"]

Ամփոփում

Այսպիսով, աշխատանքը դեռ ավարտված չէ, բայց ես հոգնել եմ այս երկարաժամկետ շինարարությունը գաղտնի հասցնել կատարելության: Ուստի որոշեցի հրապարակել այն, ինչ ունեմ առայժմ։ Կոդը տեղ-տեղ մի փոքր սարսափելի է, քանի որ սա փորձ է, և նախապես պարզ չէ, թե ինչ է պետք անել: Հավանաբար, ապա արժե սովորական ատոմային պարտավորություններ թողարկել Qemu-ի ավելի ժամանակակից տարբերակի վերևում: Միևնույն ժամանակ, Gita-ում բլոգի ձևաչափով մի թեմա կա. յուրաքանչյուր «մակարդակի» համար, որը գոնե ինչ-որ կերպ անցել է, ավելացվել է ռուսերեն մանրամասն մեկնաբանություն: Փաստորեն, այս հոդվածը մեծ մասամբ եզրակացության վերապատմում է git log.

Դուք կարող եք փորձել այն ամենը այստեղ (Զգուշացեք երթևեկությունից):

Ինչն արդեն աշխատում է.

  • x86 վիրտուալ պրոցեսորն աշխատում է
  • Կա JIT կոդերի գեներատորի աշխատանքային նախատիպ՝ մեքենայական կոդից մինչև JavaScript
  • Գոյություն ունի այլ 32-բիթանոց հյուր ճարտարապետներ հավաքելու ձևանմուշ. հենց հիմա դուք կարող եք հիանալ Linux-ով, քանի որ MIPS ճարտարապետությունը սառչում է բրաուզերում բեռնման փուլում:

Էլ ի՞նչ կարող ես անել

  • Արագացնել էմուլյացիան: Նույնիսկ JIT ռեժիմում, թվում է, թե այն ավելի դանդաղ է աշխատում, քան Virtual x86-ը (բայց հնարավոր է, որ կա մի ամբողջ Qemu՝ բազմաթիվ նմանակված սարքավորումներով և ճարտարապետությամբ):
  • Նորմալ ինտերֆեյս ստեղծելու համար, անկեղծ ասած, ես լավ վեբ ծրագրավորող չեմ, ուստի առայժմ ես վերափոխել եմ ստանդարտ Emscripten կեղևը հնարավորինս լավ:
  • Փորձեք գործարկել ավելի բարդ Qemu գործառույթներ՝ ցանցեր, VM միգրացիա և այլն:
  • UPS: դուք պետք է ներկայացնեք ձեր մի քանի զարգացումները և սխալների մասին հաշվետվությունները Emscripten-ին հոսանքին հակառակ, ինչպես դա արեցին Qemu-ի և այլ նախագծերի նախորդ բեռնափոխադրողները: Շնորհակալություն նրանց, որ կարողացան անուղղակիորեն օգտագործել իրենց ներդրումը Emscripten-ում՝ որպես իմ առաջադրանքի մաս:

Source: www.habr.com

Добавить комментарий