QEMU.js: දැන් බරපතල සහ WASM සමඟ

වරක් මම විනෝදය සඳහා තීරණය කළෙමි ක්රියාවලියේ ආපසු හැරවීමේ හැකියාව ඔප්පු කරන්න යන්ත්‍ර කේතයෙන් JavaScript (වඩාත් නිවැරදිව, Asm.js) උත්පාදනය කරන්නේ කෙසේදැයි ඉගෙන ගන්න. අත්හදා බැලීම සඳහා QEMU තෝරා ගන්නා ලද අතර, කලකට පසුව Habr පිළිබඳ ලිපියක් ලියා ඇත. අදහස් දැක්වීමේදී, ව්‍යාපෘතිය WebAssembly හි ප්‍රතිනිර්මාණය කරන ලෙස මට උපදෙස් දෙන ලද අතර, එයින් ඉවත් වීමට පවා පාහේ අවසන් මට කෙසේ හෝ ව්‍යාපෘතිය අවශ්‍ය නොවීය ... වැඩ සිදුවෙමින් පැවතුනි, නමුත් ඉතා සෙමින්, දැන්, මෑතකදී එම ලිපියේ පෙනී සිටියේය අදහස් දැක්වීය මාතෘකාව මත "එසේ නම් සියල්ල අවසන් වූයේ කෙසේද?" මගේ සවිස්තරාත්මක පිළිතුරට ප්‍රතිචාර වශයෙන්, “මෙය ලිපියක් මෙන් පෙනේ” යැයි මට ඇසිණි. හොඳයි, ඔබට හැකි නම්, ලිපියක් ඇත. සමහර විට කෙනෙකුට එය ප්රයෝජනවත් වනු ඇත. එයින් පාඨකයා QEMU කේත උත්පාදන පසුපෙළ සැලසුම් කිරීම මෙන්ම වෙබ් යෙදුමක් සඳහා Just-in-Time compiler එකක් ලියන ආකාරය පිළිබඳ කරුණු කිහිපයක් ඉගෙන ගනු ඇත.

කාර්යයන්

QEMU ජාවාස්ක්‍රිප්ට් වෙත “කොහොම හරි” පෝට් කරන්නේ කෙසේදැයි මම දැනටමත් ඉගෙන ගෙන ඇති බැවින්, මෙවර එය බුද්ධිමත්ව කිරීමට සහ පැරණි වැරදි නැවත නොකිරීමට තීරණය විය.

දෝෂ අංක එක: ලක්ෂ්‍ය නිකුතුවේ සිට ශාඛාව

මගේ පළමු වැරැද්ද වූයේ උඩු ගංවතුර අනුවාදය 2.4.1 වෙතින් මගේ අනුවාදය වෙන් කිරීමයි. එවිට මට එය හොඳ අදහසක් පෙනුනි: ලක්ෂ්‍ය නිකුතුව පවතී නම්, එය බොහෝ විට සරල 2.4 ට වඩා ස්ථායී වන අතර ඊටත් වඩා ශාඛාව master. ඒ වගේම මම මගේම දෝෂ සෑහෙන ප්‍රමාණයක් එකතු කිරීමට සැලසුම් කළ නිසා, මට වෙන කාගේවත් අවශ්‍ය වුණේ නැහැ. එහෙම වෙන්න ඇති. නමුත් මෙන්න කාරණය: QEMU නිශ්චලව නොසිටින අතර, යම් අවස්ථාවක දී ඔවුන් උත්පාදනය කරන ලද කේතය සියයට 10 කින් ප්‍රශස්තිකරණය කිරීම පවා නිවේදනය කළා. මෙහිදී අපට අපගමනය කිරීමට අවශ්‍ය වේ: QEMU.js හි තනි නූල් ස්වභාවය සහ මුල් QEMU බහු නූල් නොමැති වීම (එනම්, නොබැඳි කේත මාර්ග කිහිපයක් එකවර ක්‍රියාත්මක කිරීමේ හැකියාව, සහ "සියලු කර්නල් භාවිතා කිරීම" පමණක් නොවේ) ඒ සඳහා තීරනාත්මක වේ, නූල් වල ප්‍රධාන කාර්යයන් පිටතින් ඇමතීමට මට "එය හැරවීමට" සිදු විය. මෙය ඒකාබද්ධ කිරීමේදී ස්වභාවික ගැටළු කිහිපයක් ඇති කළේය. කෙසේ වෙතත්, ශාඛාවෙන් සමහර වෙනස්කම් ඇති බව master, මම මගේ කේතය ඒකාබද්ධ කිරීමට උත්සාහ කළ අතර, ලක්ෂ්‍ය නිකුතුවේදී චෙරි ද තෝරාගෙන ඇත (සහ ඒ නිසා මගේ ශාඛාවේ) ද බොහෝ විට පහසුවක් එකතු නොකරනු ඇත.

පොදුවේ ගත් කල, මූලාකෘතිය ඉවතට විසි කිරීම, කොටස් සඳහා එය විසුරුවා හැරීම සහ නැවුම් යමක් මත පදනම්ව මුල සිටම නව අනුවාදයක් තැනීම තවමත් අර්ථවත් බව මම තීරණය කළෙමි. master.

වැරදි අංක දෙක: TLP ක්‍රමවේදය

සාරාංශයක් ලෙස, මෙය වැරැද්දක් නොවේ, පොදුවේ ගත් කල, එය “කොහෙද සහ කෙසේද යන්නද?” සහ පොදුවේ “අපි එහි යනවාද?” යන දෙකම පිළිබඳ සම්පූර්ණ වරදවා වටහාගැනීමේ කොන්දේසි යටතේ ව්‍යාපෘතියක් නිර්මාණය කිරීමේ ලක්ෂණයක් පමණි. මෙම තත්වයන් තුළ අවුල් සහගත වැඩසටහන්කරණය යුක්තිසහගත විකල්පයක් විය, නමුත්, ස්වභාවිකවම, මට එය අනවශ්ය ලෙස නැවත කිරීමට අවශ්ය නොවීය. මෙවර මට එය ඥානාන්විතව කිරීමට අවශ්‍ය විය: පරමාණුක කැපවීම්, සවිඥානික කේත වෙනස් කිරීම් (සහ “එය සම්පාදනය කරන තුරු (අනතුරු ඇඟවීම් සහිතව) අහඹු අක්ෂර එකට නූල් කිරීම නොවේ”, විකිකෝට් අනුව Linus Torvalds කෙනෙකු ගැන වරක් පැවසූ පරිදි) යනාදිය.

වැරදි අංක තුන: බලකොටුව නොදැන වතුරට බැසීම

මම තවමත් මෙයින් සම්පූර්ණයෙන් මිදී නැත, නමුත් දැන් මම අවම ප්‍රතිරෝධයේ මාවත අනුගමනය නොකිරීමට සහ එය “වැඩිහිටි ක්‍රමයට” කිරීමට තීරණය කළෙමි, එනම්, මගේ TCG පසුබිම මුල සිටම ලියන්න, එසේ නොවේ. පසුව පැවසීමට සිදුවේ, "ඔව්, මෙය ඇත්ත වශයෙන්ම, සෙමින්, නමුත් මට සියල්ල පාලනය කළ නොහැක - TCI ලියා ඇත්තේ එලෙසයි..." එපමණක් නොව, මෙය මුලදී පැහැදිලි විසඳුමක් ලෙස පෙනෙන්නට තිබුණි මම ද්විමය කේතය ජනනය කරමි. ඔවුන් පවසන පරිදි, "ගෙන්ට් රැස් වියу, නමුත් එය එකක් නොවේ”: කේතය, ඇත්ත වශයෙන්ම, ද්විමය, නමුත් පාලනය සරලව එයට මාරු කළ නොහැක - එය සම්පාදනය සඳහා බ්‍රවුසරයට පැහැදිලිව තල්ලු කළ යුතු අතර, එහි ප්‍රතිඵලයක් ලෙස JS ලෝකයෙන් යම් වස්තුවක් ලැබෙනු ඇත, එය තවමත් අවශ්‍ය වේ. කොහේ හරි බේරෙනවා. කෙසේ වෙතත්, සාමාන්‍ය RISC ගෘහ නිර්මාණ ශිල්පය මත, මා තේරුම් ගත් පරිදි, සාමාන්‍ය තත්වයක් වන්නේ ප්‍රතිජනනය කරන ලද කේතය සඳහා උපදෙස් හැඹිලිය පැහැදිලිව නැවත සැකසීමේ අවශ්‍යතාවයයි - මෙය අපට අවශ්‍ය නොවේ නම්, ඕනෑම අවස්ථාවක, එය ආසන්නයි. ඊට අමතරව, මගේ අවසාන උත්සාහයෙන්, පරිවර්තන කොටසේ මැදට පාලනය මාරු නොවන බව මම දැන සිටියෙමි, එබැවින් අපට ඇත්ත වශයෙන්ම කිසිදු ඕෆ්සෙට් එකකින් බයිට්කෝඩ් පරිවර්ථනය කිරීම අවශ්‍ය නොවන අතර අපට එය TB හි ශ්‍රිතයෙන් සරලව ජනනය කළ හැකිය. .

ඇවිත් පයින් ගැහුවා

මම නැවත ජූලි මාසයේදී කේතය නැවත ලිවීමට පටන් ගත්තද, නොදැනුවත්වම මැජික් පහරක් ඇති විය: සාමාන්‍යයෙන් GitHub වෙතින් ලිපි පැමිණෙන්නේ ගැටළු සහ ඉල්ලීම් සඳහා ප්‍රතිචාර පිළිබඳ දැනුම්දීම් ලෙසය, නමුත් මෙහි, හදිසියේම ත්‍රෙඩ් එකේ සඳහන් කරන්න qemu පසුබිමක් ලෙස Binaryen සන්දර්භය තුළ, "ඔහු ඒ වගේ දෙයක් කළා, සමහර විට ඔහු යමක් කියයි." අපි කතා කළේ එම්ක්‍රිප්ටෙන් සම්බන්ධ පුස්තකාලය භාවිතා කිරීම ගැනයි බිනරින් WASM JIT නිර්මාණය කිරීමට. හොඳයි, මම කිව්වා ඔබට එහි Apache 2.0 බලපත්‍රයක් ඇති බවත්, QEMU සමස්තයක් ලෙස GPLv2 යටතේ බෙදා හරින බවත්, ඒවා එතරම් නොගැලපෙන බවත්ය. බලපත්රයක් විය හැකි බව හදිසියේම පෙනී ගියේය එය කෙසේ හෝ නිවැරදි කරන්න (මම දන්නේ නැහැ: සමහර විට එය වෙනස් කරන්න, සමහර විට ද්විත්ව බලපත්ර, සමහර විට වෙනත් දෙයක් ...). ඇත්ත වශයෙන්ම, මෙය මට සතුටක් ගෙන දුන්නේය, මන්ද ඒ වන විට මම ඒ වන විටත් සමීපව බලා සිටි බැවිනි ද්විමය ආකෘතිය WebAssembly, සහ මම කෙසේ හෝ දුකෙන් සහ තේරුම්ගත නොහැකි විය. සංක්‍රාන්ති ප්‍රස්ථාරය සමඟ මූලික කොටස් ගිල දමමින්, බයිට්කේතය නිපදවන, අවශ්‍ය නම් පරිවර්තකයේම පවා ධාවනය කරන පුස්තකාලයක් ද විය.

ඊට පස්සේ තවත් තිබුණා ලිපියක් QEMU තැපැල් ලැයිස්තුවේ, නමුත් මෙය වඩාත් ප්‍රශ්නය, “කෙසේ වෙතත් එය අවශ්‍ය කාටද?” ඒ වගේම තමයි හදිසියේම, එය අවශ්ය බව පෙනී ගියේය. අවම වශයෙන්, එය ඉක්මනින් වැඩි හෝ අඩුවෙන් ක්‍රියා කරන්නේ නම්, ඔබට පහත භාවිත හැකියාවන් එකට සීරීමට හැකිය:

  • කිසිදු ස්ථාපනයකින් තොරව අධ්‍යාපනික දෙයක් දියත් කිරීම
  • IOS හි අථත්‍යකරණය, කටකතා වලට අනුව, පියාසර කිරීමේදී කේත උත්පාදනය කිරීමට අයිතිය ඇති එකම යෙදුම JS එන්ජිමකි (මෙය සත්‍යද?)
  • mini-OS නිරූපණය - තනි-floppy, බිල්ට්, සියලු වර්ගවල ස්ථිරාංග, ආදිය.

බ්‍රව්සරයේ ධාවන කාල විශේෂාංග

මම දැනටමත් පවසා ඇති පරිදි, QEMU බහු නූල් වලට බැඳී ඇත, නමුත් බ්‍රවුසරයේ එය නොමැත. හොඳයි, එනම්, නැත ... මුලදී එය කිසිසේත් නොතිබුණි, පසුව WebWorkers පෙනී සිටියේය - මට තේරෙන පරිදි, මෙය පණිවිඩ යැවීම මත පදනම්ව බහු නූල් කිරීමකි. හවුල් විචල්‍ය නොමැතිව. ස්වාභාවිකවම, හවුල් මතක ආකෘතිය මත පදනම්ව පවතින කේතය මාරු කිරීමේදී මෙය සැලකිය යුතු ගැටළු ඇති කරයි. අනතුරුව මහජන බලපෑම් මත එය නමින් ද ක් රියාත්මක විය SharedArrayBuffers. එය ක්‍රමක්‍රමයෙන් හඳුන්වා දී, ඔවුන් විවිධ බ්‍රවුසර වල එහි දියත් කිරීම සමරනු ලැබුවා, පසුව ඔවුන් අලුත් අවුරුද්ද සැමරුවා, පසුව Meltdown... ඉන්පසු ඔවුන් නිගමනය කළේ කාලය මැනීම රළු හෝ රළු බව, නමුත් හවුල් මතකය සහ a. නූල් කවුන්ටරය වැඩි කිරීම, ඒ සියල්ල එක හා සමානයි එය ඉතා නිවැරදිව වැඩ කරනු ඇත. එබැවින් අපි හවුල් මතකය සමඟ බහු නූල් කිරීම අක්‍රීය කළෙමු. ඔවුන් පසුව එය නැවත ක්‍රියාත්මක කළ බව පෙනේ, නමුත්, පළමු අත්හදා බැලීමෙන් පැහැදිලි වූ පරිදි, එය නොමැතිව ජීවිතයක් ඇත, එසේ නම්, අපි බහු නූල් මත රඳා නොසිට එය කිරීමට උත්සාහ කරමු.

දෙවන ලක්ෂණය නම් තොගය සමඟ පහත් මට්ටමේ උපාමාරු වල නොහැකියාවයි: ඔබට සරලව ගත නොහැක, වත්මන් සන්දර්භය සුරැකීමට සහ නව තොගයක් සහිත නව එකක් වෙත මාරු විය නොහැක. ඇමතුම් තොගය කළමනාකරණය කරනු ලබන්නේ JS අතථ්‍ය යන්ත්‍රය මගිනි. පළමු ප්‍රවාහයන් සම්පූර්ණයෙන්ම අතින් කළමනාකරණය කිරීමට අප තවමත් තීරණය කර ඇති බැවින් ගැටලුව කුමක්දැයි පෙනේ. කාරණය නම්, QEMU හි අවහිර I/O ක්‍රියාවට නංවනු ලබන්නේ coroutines හරහා වන අතර, පහත මට්ටමේ අට්ටි හැසිරවීම් ප්‍රයෝජනවත් වන්නේ මෙහිදීය. වාසනාවකට මෙන්, Emscipten හි දැනටමත් අසමමුහුර්ත මෙහෙයුම් සඳහා යාන්ත්‍රණයක් ඇත, දෙකක් පවා: සමමුහුර්ත කරන්න и එම්ටර්ප්රෙටර්. පළමු එක ජනනය කරන ලද ජාවාස්ක්‍රිප්ට් කේතයේ සැලකිය යුතු බූට් හරහා ක්‍රියා කරන අතර තවදුරටත් සහාය නොදක්වයි. දෙවැන්න වත්මන් "නිවැරදි මාර්ගය" වන අතර ස්වදේශික පරිවර්තකයා සඳහා බයිට්කේත උත්පාදනය හරහා ක්‍රියා කරයි. එය ඇත්ත වශයෙන්ම, සෙමින් ක්රියා කරයි, නමුත් එය කේතය පුපුරවා හරිනු නොලැබේ. ඇත්ත, මෙම යාන්ත්‍රණය සඳහා coroutines සඳහා සහය ස්වාධීනව දායක විය යුතු විය (Asyncify සඳහා දැනටමත් coroutines ලියා ඇති අතර Emterpreter සඳහා ආසන්න වශයෙන් එකම API ක්‍රියාත්මක කිරීමක් සිදු විය, ඔබට ඒවා සම්බන්ධ කිරීමට අවශ්‍ය විය).

මේ මොහොතේ, මම කේතය WASM හි සම්පාදනය කර එම්ටර්ප්‍රෙටර් භාවිතයෙන් අර්ථකථනය කළ එකකට බෙදීමට තවමත් සමත් වී නැත, එබැවින් බ්ලොක් උපාංග තවමත් ක්‍රියා නොකරයි (ඊළඟ මාලාවෙන් බලන්න, ඔවුන් පවසන පරිදි...). එනම්, අවසානයේදී ඔබට මෙවැනි විහිලු ස්ථර දෙයක් ලබා ගත යුතුය:

  • අර්ථකථනය කරන ලද වාරණ I/O. හොඳයි, ඔබ ඇත්තටම NVMe දේශීය කාර්ය සාධනය සමඟ අනුකරණය කිරීමට බලාපොරොත්තු වූවාද? 🙂
  • ස්ථිතිකව සම්පාදනය කරන ලද ප්‍රධාන QEMU කේතය (පරිවර්තකය, වෙනත් අනුකරණය කළ උපාංග, ආදිය)
  • WASM වෙත ගතිකව සම්පාදනය කරන ලද ආගන්තුක කේතය

QEMU මූලාශ්‍රවල විශේෂාංග

ඔබ දැනටමත් අනුමාන කර ඇති පරිදි, ආගන්තුක ගෘහ නිර්මාණ ශිල්පය අනුකරණය කිරීමේ කේතය සහ ධාරක යන්ත්‍ර උපදෙස් ජනනය කිරීමේ කේතය QEMU හි වෙන් කර ඇත. ඇත්ත වශයෙන්ම, එය ටිකක් උපක්‍රමශීලී ය:

  • ආගන්තුක ගෘහ නිර්මාණ ශිල්ප ඇත
  • ත්වරණකාරක, එනම්, Linux මත දෘඪාංග අථත්‍යකරණය සඳහා KVM (එකිනෙකා සමඟ අනුකූල වන ආගන්තුක සහ ධාරක පද්ධති සඳහා), ඕනෑම තැනක JIT කේත උත්පාදනය සඳහා TCG. QEMU 2.9 සමඟින් පටන් ගෙන, Windows හි HAXM දෘඪාංග අථත්‍යකරණ ප්‍රමිතිය සඳහා සහය පළ විය (විස්තර)
  • TCG භාවිතා කරන්නේ දෘඪාංග අථත්‍යකරණය නොවේ නම්, එය එක් එක් ධාරක ගෘහ නිර්මාණ ශිල්පය සඳහා මෙන්ම විශ්ව පරිවර්තකය සඳහා වෙනම කේත උත්පාදන සහාය ඇත.
  • ... සහ මේ සියල්ල වටා - අනුකරණය කරන ලද පර්යන්ත, පරිශීලක අතුරුමුහුණත, සංක්‍රමණය, වාර්තා නැවත ධාවනය, ආදිය.

මාර්ගය වන විට, ඔබ දැන සිටියාද: QEMU හට සම්පූර්ණ පරිගණකය පමණක් නොව, ධාරක කර්නලයේ වෙනම පරිශීලක ක්‍රියාවලියක් සඳහා ප්‍රොසෙසරය ද අනුකරණය කළ හැකිය, උදාහරණයක් ලෙස, ද්විමය උපකරණ සඳහා AFL ෆුසර් විසින් භාවිතා කරනු ලැබේ. සමහර විට යමෙක් QEMU හි මෙම මෙහෙයුම් ආකාරය JS වෙත ගෙනයාමට කැමතිද? 😉

බොහෝ දිගුකාලීන නිදහස් මෘදුකාංග මෙන්, QEMU ඇමතුම හරහා ගොඩනගා ඇත configure и make. ඔබ යමක් එකතු කිරීමට තීරණය කළා යැයි සිතමු: TCG පසුබිමක්, නූල් ක්‍රියාත්මක කිරීම, වෙනත් දෙයක්. Autoconf සමඟ සන්නිවේදනය කිරීමේ අපේක්ෂාවෙන් සතුටු වීමට/බියට පත්වීමට ඉක්මන් නොවන්න (සුදුසු පරිදි යටින් ඉරි අඳින්න) - ඇත්ත වශයෙන්ම, configure QEMUs පැහැදිලිවම ස්වයං-ලිඛිත වන අතර කිසිවකින් ජනනය නොවේ.

වෙබ් එකලස් කිරීම

ඉතින් මොකක්ද මේ WebAssembly (aka WASM) කියන දේ? මෙය තවදුරටත් වලංගු JavaScript කේතය ලෙස පෙනී නොසිටින Asm.js සඳහා ආදේශකයකි. ඊට පටහැනිව, එය තනිකරම ද්විමය සහ ප්‍රශස්ත වන අතර, එයට පූර්ණ සංඛ්‍යාවක් ලිවීම පවා ඉතා සරල නොවේ: සංයුක්තතාවය සඳහා, එය ආකෘතියේ ගබඩා කර ඇත. LEB128.

Asm.js සඳහා නැවත සකස් කිරීමේ ඇල්ගොරිතම ගැන ඔබ අසා ඇති - මෙය JS එන්ජින් නිර්මාණය කර ඇති “ඉහළ මට්ටමේ” ප්‍රවාහ පාලන උපදෙස් (එනම්, එසේ නම්, ලූප, ආදිය) ප්‍රතිසාධනය කිරීමකි. පහළ මට්ටමේ LLVM IR, ප්‍රොසෙසරය මඟින් ක්‍රියාත්මක කරන යන්ත්‍ර කේතයට සමීප වේ. ස්වාභාවිකවම, QEMU හි අතරමැදි නිරූපණය දෙවැන්නට සමීප වේ. එය මෙහි ඇති බව පෙනේ, බයිට්කෝඩ්, වධහිංසාවේ අවසානය ... ඉන්පසු බ්ලොක්, if-then-else සහ loops ඇත!..

මෙය Binaryen ප්‍රයෝජනවත් වීමට තවත් හේතුවක් වේ: WASM හි ගබඩා කර ඇති දේට ආසන්නව ඉහළ මට්ටමේ කුට්ටි ස්වභාවිකව පිළිගත හැකිය. නමුත් එය මූලික කුට්ටි සහ ඒවා අතර සංක්‍රාන්ති ප්‍රස්ථාරයකින් කේතයක් නිපදවිය හැකිය. හොඳයි, එය පහසු C/C++ API පිටුපස WebAssembly ගබඩා ආකෘතිය සඟවන බව මම දැනටමත් පවසා ඇත.

TCG (කුඩා කේත උත්පාදක)

ටී.සී.ජී. මුලින් විය C compiler සඳහා backend.ඉන්පසු, පෙනෙන පරිදි, එය GCC සමඟ තරඟයට ඔරොත්තු දීමට නොහැකි විය, නමුත් අවසානයේ එය සත්කාරක වේදිකාව සඳහා කේත උත්පාදන යාන්ත්‍රණයක් ලෙස QEMU හි එහි ස්ථානය සොයා ගත්තේය. කිසියම් වියුක්ත බයිට්කේතයක් ජනනය කරන TCG පසුබිමක් ද ඇත, එය පරිවර්තකයා විසින් වහාම ක්‍රියාත්මක කරනු ලැබේ, නමුත් මෙවර එය භාවිතා නොකිරීමට මම තීරණය කළෙමි. කෙසේ වෙතත්, QEMU හි ශ්‍රිතය හරහා උත්පාදනය කරන ලද TB වෙත සංක්‍රමණය සක්‍රීය කිරීමට දැනටමත් හැකියාව ඇත. tcg_qemu_tb_exec, එය මට ඉතා ප්රයෝජනවත් විය.

QEMU වෙත නව TCG පසුබිමක් එක් කිරීමට, ඔබ උප බහලුමක් සෑදිය යුතුය tcg/<имя архитектуры> (මේ අවස්ථාවේ දී, tcg/binaryen), සහ එහි ගොනු දෙකක් අඩංගු වේ: tcg-target.h и tcg-target.inc.c и නියම එය සියල්ල ගැන ය configure. ඔබට වෙනත් ලිපිගොනු එහි තැබිය හැකිය, නමුත්, ඔබට මෙම දෙකේ නම් වලින් අනුමාන කළ හැකි පරිදි, ඒවා දෙකම කොතැනක හෝ ඇතුළත් වනු ඇත: එකක් සාමාන්‍ය ශීර්ෂ ගොනුවක් ලෙස (එය ඇතුළත් වේ tcg/tcg.h, සහ එය දැනටමත් නාමාවලි වල වෙනත් ගොනු වල ඇත tcg, accel සහ පමණක් නොව), අනෙක - කේත ඛණ්ඩයක් ලෙස පමණි tcg/tcg.c, නමුත් එහි ස්ථිතික කාර්යයන් සඳහා ප්රවේශය ඇත.

එය ක්‍රියා කරන ආකාරය පිළිබඳ සවිස්තරාත්මක විමර්ශන සඳහා මම වැඩි කාලයක් ගත කිරීමට තීරණය කළෙමි, මම මෙම ලිපිගොනු දෙකේ “ඇටසැකිලි” වෙනත් පසුපෙළ ක්‍රියාත්මක කිරීමකින් පිටපත් කළෙමි, මෙය බලපත්‍ර ශීර්ෂයේ අවංකව දක්වයි.

ගොනුව tcg-target.h ආකෘතියේ ප්රධාන වශයෙන් සැකසුම් අඩංගු වේ #define-s:

  • ඉලක්ක ගෘහ නිර්මාණ ශිල්පයේ කොපමණ රෙජිස්ටර් සහ පළල කොපමණ තිබේද (අපට අවශ්‍ය තරම්, අපට අවශ්‍ය තරම් ප්‍රමාණයක් ඇත - ප්‍රශ්නය වන්නේ “සම්පූර්ණයෙන්ම ඉලක්ක කරගත්” ගෘහ නිර්මාණ ශිල්පය මත බ්‍රවුසරය මඟින් වඩාත් කාර්යක්ෂම කේතයක් බවට ජනනය කරන්නේ කුමක්ද යන්නයි. ...)
  • ධාරක උපදෙස් පෙළගැස්වීම: x86 මත, සහ TCI හි පවා, උපදෙස් කිසිසේත්ම පෙළගස්වා නැත, නමුත් මම කේත බෆරයේ තැබීමට යන්නේ කිසිසේත් උපදෙස් නොව, නමුත් Binaryen පුස්තකාල ව්‍යුහයන් වෙත පොයින්ටර් ය, එබැවින් මම මෙසේ කියමි: 4 බයිට්
  • පසුපෙළට ජනනය කළ හැකි විකල්ප උපදෙස් මොනවාද - අපි Binaryen හි සොයා ගන්නා සෑම දෙයක්ම ඇතුළත් කරමු, ත්වරණකාරකයට ඉතිරිය සරල ඒවා බවට පත් කිරීමට ඉඩ දෙන්න
  • පසුපෙළ විසින් ඉල්ලා සිටින TLB හැඹිලියේ ආසන්න ප්‍රමාණය කොපමණද. කාරණය නම් QEMU හි සෑම දෙයක්ම බරපතල ය: ආගන්තුක MMU සැලකිල්ලට ගනිමින් භාරය / ගබඩාව සිදු කරන උපකාරක කාර්යයන් ඇතත් (අපි දැන් එය නොමැතිව සිටින්නේ කොහේද?), ඔවුන් ඔවුන්ගේ පරිවර්තන හැඹිලිය ව්‍යුහයක් ආකාරයෙන් සුරකියි, විකාශන කොටස් වලට කෙලින්ම කාවැද්දීමට පහසු වන සැකසීම. ප්‍රශ්නය නම්, මෙම ව්‍යුහයේ කුඩා සහ වේගවත් විධාන අනුපිළිවෙලක් මඟින් වඩාත් කාර්යක්ෂමව සකසන ලද ඕෆ්සෙට් මොනවාද?
  • මෙහිදී ඔබට වෙන් කර ඇති ලේඛන එකක හෝ දෙකක අරමුණ වෙනස් කිරීමට, ශ්‍රිතයක් හරහා TB ඇමතීම සක්‍රීය කිරීමට සහ විකල්ප වශයෙන් කුඩා කිහිපයක් විස්තර කිරීමට හැකිය inline- වැනි කාර්යයන් flush_icache_range (නමුත් මෙය අපගේ නඩුව නොවේ)

ගොනුව tcg-target.inc.c, ඇත්ත වශයෙන්ම, සාමාන්‍යයෙන් ප්‍රමාණයෙන් වඩා විශාල වන අතර අනිවාර්ය කාර්යයන් කිහිපයක් අඩංගු වේ:

  • ආරම්භ කිරීම, කුමන ඔපරේන්ඩ් මත ක්‍රියාත්මක විය හැකි උපදෙස් සීමා කිරීම් ඇතුළුව. වෙනත් පසුබිමකින් මා විසින් අමු අමුවේ පිටපත් කරන ලදී
  • එක් අභ්‍යන්තර බයිට්කේත උපදෙස් ලබා ගන්නා කාර්යය
  • ඔබට මෙහි සහායක ශ්‍රිත ද තැබිය හැකි අතර, ඔබට ස්ථිතික ශ්‍රිත ද භාවිතා කළ හැක tcg/tcg.c

මා සඳහා, මම පහත උපාය මාර්ගය තෝරා ගත්තෙමි: ඊළඟ පරිවර්තන කොටසේ පළමු වචන වලින්, මම දර්ශක හතරක් ලියා තැබුවෙමි: ආරම්භක ලකුණක් (ආසන්න වශයෙන් යම් අගයක් 0xFFFFFFFF, TB හි වත්මන් තත්ත්වය තීරණය කරන ලද), සන්දර්භය, ජනනය කරන ලද මොඩියුලය සහ දෝශ නිරාකරණය සඳහා මැජික් අංකය. මුලදී ලකුණ තබා ඇත 0xFFFFFFFF - nකොහෙද n - කුඩා ධන සංඛ්‍යාවක්, සහ පරිවර්තකය හරහා ක්‍රියාත්මක කරන සෑම අවස්ථාවකම එය 1 කින් වැඩි විය. එය ළඟා වූ විට 0xFFFFFFFE, සම්පාදනය සිදු විය, මොඩියුලය ක්‍රියාකාරී වගුවේ සුරකින ලදී, කුඩා "දියත් කිරීමකට" ආනයනය කරන ලදී, එය ක්‍රියාත්මක කිරීම සිදු විය. tcg_qemu_tb_exec, සහ මොඩියුලය QEMU මතකයෙන් ඉවත් කරන ලදී.

සම්භාව්‍යයන් ප්‍රකාශ කිරීම සඳහා, "ක්‍රච්, ප්‍රෝගර්ගේ හදවත සඳහා මෙම ශබ්දය තුළ කොපමණ ප්‍රමාණයක් බැඳී තිබේද ...". කෙසේ වෙතත්, මතකය කොහේ හරි කාන්දු විය. එපමණක් නොව, එය QEMU විසින් මතකය කළමනාකරණය කරන ලදී! මට කේතයක් තිබුණා, ඊළඟ උපදෙස් ලිවීමේදී (හොඳයි, එනම්, දර්ශකයක්), කලින් මෙම ස්ථානයේ සබැඳිය තිබූ එක මකා දැමූ නමුත් මෙය උදව් කළේ නැත. ඇත්ත වශයෙන්ම, සරලම අවස්ථාවෙහිදී, QEMU ආරම්භයේදී මතකය වෙන් කර ජනනය කරන ලද කේතය එහි ලියයි. බෆරය අවසන් වූ විට, කේතය ඉවතට විසි කරන අතර ඊළඟ එක එහි ස්ථානයේ ලිවීමට පටන් ගනී.

කේතය අධ්‍යයනය කිරීමෙන් පසු, මැජික් අංකය සහිත උපක්‍රමය පළමු පාස් අවස්ථාවේදීම ආරම්භ නොකළ බෆරයක යම් වැරැද්දක් මුදා හැරීමෙන් ගොඩ විනාශයේදී අසමත් නොවීමට මට ඉඩ දුන් බව මට වැටහුණි. නමුත් පසුව මගේ කාර්යය මඟ හැරීමට බෆරය නැවත ලියන්නේ කවුද? එම්ක්‍රිප්ටන් සංවර්ධකයින් උපදෙස් දෙන පරිදි, මම ගැටලුවකට මුහුණ දුන් විට, මම එහි ප්‍රතිඵලයක් ලෙස ලැබෙන කේතය ස්වදේශීය යෙදුමට ආපසු ගෙන ගොස්, එය මත Mozilla Record-Replay සකසන්න... පොදුවේ, අවසානයේ මම සරල දෙයක් තේරුම් ගත්තා: එක් එක් වාරණ සඳහා, ඒ struct TranslationBlock එහි විස්තරය සමඟ. කොහෙද අනුමාන කරන්න... ඒක හරි, බෆරයේ හරි බ්ලොක් එකට කලින්. මෙය වටහා ගත් මම, කිහිලිකරු (අඩුම තරමින් සමහරක්) භාවිතා කිරීම නතර කිරීමට තීරණය කළ අතර, මැජික් අංකය සරලව විසි කර, ඉතිරි වචන වෙත මාරු කළෙමි. struct TranslationBlock, පරිවර්තන හැඹිලිය යළි සකසන විට ඉක්මනින් ගමන් කළ හැකි තනි සම්බන්ධිත ලැයිස්තුවක් නිර්මාණය කිරීම සහ මතකය නිදහස් කිරීම.

සමහර කිහිලිකරු ඉතිරිව ඇත: නිදසුනක් ලෙස, කේත බෆරයේ සලකුණු කර ඇති පොයින්ටර් - ඒවායින් සමහරක් සරල ය BinaryenExpressionRef, එනම්, ඔවුන් ජනනය කරන ලද මූලික බ්ලොක් එකට රේඛීයව තැබිය යුතු ප්‍රකාශන දෙස බලයි, කොටස BBs අතර සංක්‍රමණය සඳහා වන කොන්දේසිය, කොටස යනු යා යුතු ස්ථානයයි. හොඳයි, කොන්දේසි අනුව සම්බන්ධ කළ යුතු Relooper සඳහා දැනටමත් සූදානම් කර ඇති කුට්ටි තිබේ. ඒවා වෙන්කර හඳුනා ගැනීම සඳහා, උපකල්පනය භාවිතා කරනුයේ ඒවා සියල්ලම අවම වශයෙන් බයිට් හතරකින් පෙළගස්වා ඇති බවයි, එබැවින් ඔබට ලේබලය සඳහා අවම වශයෙන් සැලකිය යුතු බිටු දෙකක් ආරක්ෂිතව භාවිතා කළ හැකිය, අවශ්‍ය නම් එය ඉවත් කිරීමට ඔබ මතක තබා ගත යුතුය. මාර්ගය වන විට, TCG ලූපයෙන් පිටවීමට හේතුව දැක්වීමට QEMU හි එවැනි ලේබල දැනටමත් භාවිතා කර ඇත.

Binaryen භාවිතා කිරීම

WebAssembly හි මොඩියුලවල ශ්‍රිත අඩංගු වන අතර, ඒ සෑම එකක්ම ප්‍රකාශනයක් වන ශරීරයක් අඩංගු වේ. ප්‍රකාශන යනු ඒකීය සහ ද්විමය මෙහෙයුම්, වෙනත් ප්‍රකාශනවල ලැයිස්තු වලින් සමන්විත බ්ලොක්, පාලන ප්‍රවාහ යනාදියයි. මා දැනටමත් පවසා ඇති පරිදි, මෙහි පාලන ප්‍රවාහය හරියටම ඉහළ මට්ටමේ ශාඛා, ලූප, ක්‍රියාකාරී ඇමතුම් යනාදිය ලෙස සංවිධානය කර ඇත. ශ්‍රිත සඳහා වන තර්ක අට්ටිය මත සම්මත කර නැත, නමුත් පැහැදිලිවම, JS හි මෙන්. ගෝලීය විචල්‍යයන් ද ඇත, නමුත් මම ඒවා භාවිතා කර නැත, එබැවින් මම ඒවා ගැන ඔබට නොකියමි.

ශ්‍රිතවලට දේශීය විචල්‍යයන් ද ඇත, ශුන්‍යයේ සිට අංකිත, වර්ගයේ: int32 / int64 / float / double. මෙම අවස්ථාවෙහිදී, පළමු n ප්‍රාදේශීය විචල්‍යයන් යනු ශ්‍රිතයට ලබා දුන් තර්ක වේ. පාලන ප්‍රවාහය අනුව මෙහි ඇති සෑම දෙයක්ම මුළුමනින්ම පහත් මට්ටමක නොතිබුණද, පූර්ණ සංඛ්‍යා තවමත් “අත්සන් කළ/අත්සන් නොකළ” ගුණාංගය රැගෙන නොයන බව කරුණාවෙන් සලකන්න: අංකය හැසිරෙන ආකාරය මෙහෙයුම් කේතය මත රඳා පවතී.

සාමාන්යයෙන් කතා කිරීම, Binaryen සපයයි සරල C-API: ඔබ මොඩියුලයක් සාදන්න, ඔහු තුළ ප්‍රකාශන සාදන්න - ඒකීය, ද්විමය, වෙනත් ප්‍රකාශන වලින් අවහිර කිරීම්, ප්‍රවාහ පාලනය යනාදිය. එවිට ඔබ ප්‍රකාශනයක් එහි ශරීරය ලෙසින් ශ්‍රිතයක් නිර්මාණය කරයි. ඔබට, මා මෙන්, පහත මට්ටමේ සංක්‍රාන්ති ප්‍රස්ථාරයක් තිබේ නම්, relooper සංරචකය ඔබට උපකාර කරනු ඇත. මා තේරුම් ගත් පරිදි, බ්ලොක් එකක ක්‍රියාත්මක කිරීමේ ප්‍රවාහයේ ඉහළ මට්ටමේ පාලනයක් භාවිතා කළ හැකිය, එය බ්ලොක් සීමාවෙන් ඔබ්බට නොයන තාක් කල් - එනම් අභ්‍යන්තර වේගවත් මාර්ගය / මන්දගාමී බවට පත් කළ හැකිය. බිල්ට්-ඉන් TLB හැඹිලි සැකසුම් කේතය ඇතුලත මාර්ගය අතු බෙදී යයි, නමුත් "බාහිර" පාලන ප්‍රවාහයට බාධා කිරීමට නොවේ. ඔබ relooper නිදහස් කරන විට, එහි කුට්ටි නිදහස් වේ; ඔබ මොඩියුලයක් නිදහස් කරන විට, එයට වෙන් කර ඇති ප්‍රකාශන, ශ්‍රිත ආදිය අතුරුදහන් වේ. පිටිය.

කෙසේ වෙතත්, ඔබට අනවශ්‍ය ලෙස පරිවර්තක අවස්ථාවක් නිර්මාණය කිරීම සහ මකා දැමීමකින් තොරව පියාසර කරන විට කේතය අර්ථ නිරූපණය කිරීමට අවශ්‍ය නම්, මෙම තර්කනය C++ ගොනුවකට දැමීම අර්ථවත් කළ හැකි අතර, එතැන් සිට පුස්තකාලයේ සම්පූර්ණ C++ API සෘජුවම කළමනාකරණය කරන්න. දවටන හැදුවා.

එබැවින් ඔබට අවශ්ය කේතය උත්පාදනය කිරීමට

// настроить глобальные параметры (можно поменять потом)
BinaryenSetAPITracing(0);

BinaryenSetOptimizeLevel(3);
BinaryenSetShrinkLevel(2);

// создать модуль
BinaryenModuleRef MODULE = BinaryenModuleCreate();

// описать типы функций (как создаваемых, так и вызываемых)
helper_type  BinaryenAddFunctionType(MODULE, "helper-func", BinaryenTypeInt32(), int32_helper_args, ARRAY_SIZE(int32_helper_args));
// (int23_helper_args приоб^Wсоздаются отдельно)

// сконструировать супер-мега выражение
// ... ну тут уж вы как-нибудь сами :)

// потом создать функцию
BinaryenAddFunction(MODULE, "tb_fun", tb_func_type, func_locals, FUNC_LOCALS_COUNT, expr);
BinaryenAddFunctionExport(MODULE, "tb_fun", "tb_fun");
...
BinaryenSetMemory(MODULE, (1 << 15) - 1, -1, NULL, NULL, NULL, NULL, NULL, 0, 0);
BinaryenAddMemoryImport(MODULE, NULL, "env", "memory", 0);
BinaryenAddTableImport(MODULE, NULL, "env", "tb_funcs");

// запросить валидацию и оптимизацию при желании
assert (BinaryenModuleValidate(MODULE));
BinaryenModuleOptimize(MODULE);

... මට කිසිවක් අමතක වූවා නම්, සමාවන්න, මෙය පරිමාණය නියෝජනය කිරීමට පමණක් වන අතර, විස්තර ලේඛනගත කර ඇත.

දැන් crack-fex-pex ආරම්භ වේ, මේ වගේ දෙයක්:

static char buf[1 << 20];
BinaryenModuleOptimize(MODULE);
BinaryenSetMemory(MODULE, 0, -1, NULL, NULL, NULL, NULL, NULL, 0, 0);
int sz = BinaryenModuleWrite(MODULE, buf, sizeof(buf));
BinaryenModuleDispose(MODULE);
EM_ASM({
  var module = new WebAssembly.Module(new Uint8Array(wasmMemory.buffer, $0, $1));
  var fptr = $2;
  var instance = new WebAssembly.Instance(module, {
      'env': {
          'memory': wasmMemory,
          // ...
      }
  );
  // и вот уже у вас есть instance!
}, buf, sz);

කෙසේ හෝ QEMU සහ JS ලෝක සම්බන්ධ කිරීම සඳහා සහ ඒ සමඟම සම්පාදනය කරන ලද ශ්‍රිතවලට ඉක්මනින් ප්‍රවේශ වීම සඳහා, අරාවක් නිර්මාණය කරන ලදී (දියත් කරන්නා වෙත ආයාත කිරීම සඳහා ශ්‍රිත වගුවක්), සහ ජනනය කරන ලද ශ්‍රිත එහි තබා ඇත. දර්ශකය ඉක්මනින් ගණනය කිරීම සඳහා, ශුන්‍ය වචන පරිවර්තන වාරණයේ දර්ශකය මුලින් එය ලෙස භාවිතා කරන ලදී, නමුත් පසුව මෙම සූත්‍රය භාවිතයෙන් ගණනය කරන ලද දර්ශකය හුදෙක් ක්ෂේත්‍රයට ගැලපීමට පටන් ගත්තේය. struct TranslationBlock.

මාර්ගය වන විට, නිරූපණය (දැනට අඳුරු බලපත්‍රයක් ඇත) Firefox වල විතරයි හොදට වැඩ කරන්නේ. Chrome සංවර්ධකයින් විය කෙසේ හෝ සූදානම් නැත යමෙකුට WebAssembly මොඩියුලවල අවස්ථා දහසකට වඩා නිර්මාණය කිරීමට අවශ්‍ය වනු ඇත, එබැවින් ඔවුන් එක් එක් සඳහා ගිගාබයිට් අථත්‍ය ලිපින ඉඩක් වෙන් කළේය.

දැනට එච්චරයි. සමහර විට කැමති කෙනෙක් ඉන්නවා නම් තවත් ලිපියක් එයි. එනම්, අවම වශයෙන් පවතී එකම බ්ලොක් උපාංග වැඩ කරන්න. ස්වදේශීය මොඩියුලය සූදානම් වන තෙක් මේ සියල්ල කළ හැකි පරිවර්තකයෙකු තවමත් සිටින බැවින්, JS ලෝකයේ සිරිත පරිදි, WebAssembly මොඩියුල සම්පාදනය අසමමුහුර්ත කිරීම අර්ථවත් කළ හැකිය.

අවසානයේ ප්‍රහේලිකාවක්: ඔබ 32-bit architecture මත ද්විමයයක් සම්පාදනය කර ඇත, නමුත් කේතය, මතක මෙහෙයුම් හරහා, Binaryen වෙතින්, තොගයේ කොතැනක හෝ 2-bit ලිපින අවකාශයේ ඉහළ 32 GB හි වෙනත් ස්ථානයකට නගියි. ගැටලුව වන්නේ Binaryen ගේ දෘෂ්ටි කෝණයෙන් මෙය ඉතා විශාල ප්‍රතිඵල සහිත ලිපිනයකට ප්‍රවේශ වීමයි. මේක වටේ යන්නේ කොහොමද?

පරිපාලකගේ ආකාරයෙන්

මම මෙය පරීක්ෂා කිරීම අවසන් කළේ නැත, නමුත් මගේ පළමු සිතුවිල්ල වූයේ "මම 32-bit Linux ස්ථාපනය කළහොත් කුමක් කළ යුතුද?" එවිට ලිපින අවකාශයේ ඉහළ කොටස කර්නලය විසින් අල්ලා ගනු ඇත. එකම ප්‍රශ්නය වන්නේ කොපමණ ප්‍රමාණයක් අල්ලා ගනු ඇත්ද යන්නයි: 1 හෝ 2 Gb.

ක්‍රමලේඛකයෙකුගේ ආකාරයෙන් (වෛද්‍යවරුන් සඳහා විකල්පය)

අපි ලිපින අවකාශයේ ඉහලින් බුබුලක් පිඹිමු. එය ක්‍රියාත්මක වන්නේ මන්දැයි මටම තේරෙන්නේ නැත - එහි දැනටමත් තොගයක් තිබිය යුතුය. නමුත් "අපි වෘත්තිකයන්: සියල්ල අප වෙනුවෙන් වැඩ කරයි, නමුත් කිසිවෙකු දන්නේ නැත ..."

// 2gbubble.c
// Usage: LD_PRELOAD=2gbubble.so <program>

#include <sys/mman.h>
#include <assert.h>

void __attribute__((constructor)) constr(void)
{
  assert(MAP_FAILED != mmap(1u >> 31, (1u >> 31) - (1u >> 20), PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
}

... එය Valgrind සමඟ නොගැලපෙන බව සත්යයකි, නමුත්, වාසනාවකට මෙන්, Valgrind විසින්ම ඉතා ඵලදායී ලෙස සෑම කෙනෙකුම එතැනින් ඉවතට තල්ලු කරයි :)

සමහර විට මගේ මෙම කේතය ක්‍රියා කරන ආකාරය ගැන වඩා හොඳ පැහැදිලි කිරීමක් යමෙකු ලබා දෙනු ඇත ...

මූලාශ්රය: www.habr.com

අදහස් එක් කරන්න