JIT പിന്തുണയോടെ Qemu.js: നിങ്ങൾക്ക് ഇപ്പോഴും മിനസ് പിന്നിലേക്ക് തിരിക്കാം

കുറച്ച് വർഷങ്ങൾക്ക് മുമ്പ് ഫാബ്രിസ് ബെല്ലാർഡ് jslinux എഴുതിയത് ജാവാസ്ക്രിപ്റ്റിൽ എഴുതിയ പിസി എമുലേറ്ററാണ്. അതിനുശേഷം കുറഞ്ഞത് കൂടുതൽ ഉണ്ടായിരുന്നു വെർച്വൽ x86. പക്ഷേ, എനിക്കറിയാവുന്നിടത്തോളം അവരെല്ലാം വ്യാഖ്യാതാക്കളായിരുന്നു, അതേ ഫാബ്രിസ് ബെല്ലാർഡ് വളരെ മുമ്പ് എഴുതിയ ക്യുമു, ഒരുപക്ഷേ, ഏതെങ്കിലും സ്വയം ബഹുമാനിക്കുന്ന ആധുനിക എമുലേറ്റർ, ഹോസ്റ്റ് സിസ്റ്റം കോഡിലേക്ക് അതിഥി കോഡിന്റെ ജെഐടി സമാഹാരം ഉപയോഗിക്കുന്നു. ബ്രൗസറുകൾ പരിഹരിക്കുന്ന ഒന്നുമായി ബന്ധപ്പെട്ട് വിപരീത ടാസ്‌ക് നടപ്പിലാക്കാൻ സമയമായി എന്ന് എനിക്ക് തോന്നി: ജാവാസ്ക്രിപ്റ്റിലേക്ക് മെഷീൻ കോഡിന്റെ JIT സമാഹരണം, അതിനായി Qemu പോർട്ട് ചെയ്യാൻ ഏറ്റവും യുക്തിസഹമായി തോന്നി. എന്തുകൊണ്ട് Qemu, ലളിതവും ഉപയോക്തൃ-സൗഹൃദവുമായ എമുലേറ്ററുകൾ ഉണ്ടെന്ന് തോന്നുന്നു - അതേ VirtualBox, ഉദാഹരണത്തിന് - ഇൻസ്റ്റാൾ ചെയ്യുകയും പ്രവർത്തിക്കുകയും ചെയ്യുന്നു. എന്നാൽ ക്യുമുവിന് രസകരമായ നിരവധി സവിശേഷതകൾ ഉണ്ട്

  • തുറന്ന ഉറവിടം
  • ഒരു കേർണൽ ഡ്രൈവർ ഇല്ലാതെ പ്രവർത്തിക്കാനുള്ള കഴിവ്
  • ഇന്റർപ്രെറ്റർ മോഡിൽ പ്രവർത്തിക്കാനുള്ള കഴിവ്
  • ഹോസ്റ്റ്, ഗസ്റ്റ് ആർക്കിടെക്ചറുകൾക്കുള്ള വലിയൊരു സംഖ്യയ്ക്കുള്ള പിന്തുണ

മൂന്നാമത്തെ പോയിന്റുമായി ബന്ധപ്പെട്ട്, വാസ്തവത്തിൽ, ടിസിഐ മോഡിൽ, ഗസ്റ്റ് മെഷീൻ നിർദ്ദേശങ്ങളല്ല, അവയിൽ നിന്ന് ലഭിച്ച ബൈറ്റ്കോഡാണ് വ്യാഖ്യാനിക്കുന്നത്, പക്ഷേ ഇത് സത്തയെ മാറ്റില്ല - നിർമ്മിക്കാനും പ്രവർത്തിപ്പിക്കാനും. ഒരു പുതിയ ആർക്കിടെക്ചറിൽ ക്യുമു, നിങ്ങൾക്ക് ഭാഗ്യമുണ്ടെങ്കിൽ, എ സി കമ്പൈലർ മതി - ഒരു കോഡ് ജനറേറ്റർ എഴുതുന്നത് മാറ്റിവയ്ക്കാം.

ഇപ്പോൾ, എന്റെ ഒഴിവുസമയങ്ങളിൽ ക്യുമു സോഴ്‌സ് കോഡുമായി രണ്ട് വർഷത്തെ വിശ്രമത്തിന് ശേഷം, ഒരു വർക്കിംഗ് പ്രോട്ടോടൈപ്പ് പ്രത്യക്ഷപ്പെട്ടു, അതിൽ നിങ്ങൾക്ക് ഇതിനകം പ്രവർത്തിപ്പിക്കാൻ കഴിയും, ഉദാഹരണത്തിന്, കോലിബ്രി ഒഎസ്.

എന്താണ് എംസ്ക്രിപ്റ്റൻ

ഇക്കാലത്ത്, നിരവധി കംപൈലറുകൾ പ്രത്യക്ഷപ്പെട്ടു, അതിന്റെ അവസാന ഫലം JavaScript ആണ്. ടൈപ്പ് സ്ക്രിപ്റ്റ് പോലെയുള്ള ചിലത്, വെബിൽ എഴുതാനുള്ള ഏറ്റവും നല്ല മാർഗമാണ്. അതേ സമയം, നിലവിലുള്ള C അല്ലെങ്കിൽ C++ കോഡ് എടുത്ത് ബ്രൗസറിൽ വായിക്കാവുന്ന ഫോമിലേക്ക് കംപൈൽ ചെയ്യുന്നതിനുള്ള ഒരു മാർഗമാണ് എംസ്ക്രിപ്റ്റൻ. ഓൺ ഈ പേജ് അറിയപ്പെടുന്ന പ്രോഗ്രാമുകളുടെ നിരവധി പോർട്ടുകൾ ഞങ്ങൾ ശേഖരിച്ചു: ഇവിടെഉദാഹരണത്തിന്, നിങ്ങൾക്ക് PyPy നോക്കാം - വഴിയിൽ, അവർ ഇതിനകം JIT ഉണ്ടെന്ന് അവകാശപ്പെടുന്നു. വാസ്തവത്തിൽ, എല്ലാ പ്രോഗ്രാമുകളും ലളിതമായി കംപൈൽ ചെയ്യാനും ബ്രൗസറിൽ പ്രവർത്തിപ്പിക്കാനും കഴിയില്ല - ഒരു സംഖ്യയുണ്ട് ഫീച്ചറുകൾ, നിങ്ങൾ സഹിക്കേണ്ടിവരുന്നു, എന്നിരുന്നാലും, അതേ പേജിലെ ലിഖിതത്തിൽ പറയുന്നതുപോലെ, “ഏതാണ്ട് കംപൈൽ ചെയ്യാൻ എംസ്ക്രിപ്റ്റൻ ഉപയോഗിക്കാം വഹനീയമായ ജാവാസ്ക്രിപ്റ്റിലേക്കുള്ള C/C++ കോഡ്". അതായത്, സ്റ്റാൻഡേർഡ് അനുസരിച്ച് നിർവചിക്കാത്ത സ്വഭാവമുള്ള നിരവധി പ്രവർത്തനങ്ങൾ ഉണ്ട്, എന്നാൽ സാധാരണയായി x86-ൽ പ്രവർത്തിക്കുന്നു - ഉദാഹരണത്തിന്, ചില ആർക്കിടെക്ചറുകളിൽ സാധാരണയായി നിരോധിച്ചിരിക്കുന്ന വേരിയബിളുകളിലേക്കുള്ള അലൈൻ ചെയ്യാത്ത ആക്സസ്. പൊതുവെ , ക്യുമു ഒരു ക്രോസ്-പ്ലാറ്റ്ഫോം പ്രോഗ്രാമാണ് കൂടാതെ , ഞാൻ വിശ്വസിക്കാൻ ആഗ്രഹിച്ചു, അതിൽ ഇതിനകം നിർവചിക്കാത്ത പെരുമാറ്റം അടങ്ങിയിട്ടില്ല - അത് എടുത്ത് കംപൈൽ ചെയ്യുക, തുടർന്ന് ജെഐടിയിൽ അൽപ്പം ടിങ്കർ ചെയ്യുക - നിങ്ങൾ പൂർത്തിയാക്കി! പക്ഷേ അത് അങ്ങനെയല്ല കേസ്...

ആദ്യ ശ്രമം

പൊതുവായി പറഞ്ഞാൽ, ക്യുമു ജാവാസ്ക്രിപ്റ്റിലേക്ക് പോർട്ട് ചെയ്യാനുള്ള ആശയം കൊണ്ടുവന്ന ആദ്യത്തെ വ്യക്തി ഞാനല്ല. Emscripten ഉപയോഗിച്ച് ഇത് സാധ്യമാണോ എന്ന് ReactOS ഫോറത്തിൽ ഒരു ചോദ്യം ചോദിച്ചിരുന്നു. നേരത്തെ തന്നെ, ഫാബ്രിസ് ബെല്ലാർഡ് ഇത് വ്യക്തിപരമായി ചെയ്തതായി കിംവദന്തികൾ ഉണ്ടായിരുന്നു, പക്ഷേ ഞങ്ങൾ സംസാരിക്കുന്നത് jslinux നെക്കുറിച്ചാണ്, എനിക്കറിയാവുന്നിടത്തോളം, JS-ൽ മതിയായ പ്രകടനം സ്വമേധയാ നേടാനുള്ള ഒരു ശ്രമം മാത്രമാണിത്, അത് ആദ്യം മുതൽ എഴുതിയതാണ്. പിന്നീട്, വെർച്വൽ x86 എഴുതപ്പെട്ടു - അതിനായി അവ്യക്തമായ ഉറവിടങ്ങൾ പോസ്റ്റുചെയ്‌തു, കൂടാതെ, പറഞ്ഞതുപോലെ, എമുലേഷന്റെ വലിയ “റിയലിസം” സീബയോസ് ഫേംവെയറായി ഉപയോഗിക്കുന്നത് സാധ്യമാക്കി. കൂടാതെ, Emscripten ഉപയോഗിച്ച് Qemu പോർട്ട് ചെയ്യാനുള്ള ഒരു ശ്രമമെങ്കിലും ഉണ്ടായിരുന്നു - ഞാൻ ഇത് ചെയ്യാൻ ശ്രമിച്ചു സോക്കറ്റ്പെയർ, എന്നാൽ വികസനം, ഞാൻ മനസ്സിലാക്കിയിടത്തോളം, മരവിച്ചു.

അതിനാൽ, ഇവിടെ ഉറവിടങ്ങൾ ഉണ്ടെന്ന് തോന്നുന്നു, ഇതാ എംസ്ക്രിപ്റ്റൻ - അത് എടുത്ത് കംപൈൽ ചെയ്യുക. എന്നാൽ ക്യുമു ആശ്രയിക്കുന്ന ലൈബ്രറികളും ആ ലൈബ്രറികളെ ആശ്രയിക്കുന്ന ലൈബ്രറികളും ഉണ്ട്, അവയിലൊന്നാണ്. ലിബ്ഫി, ഏത് glib ആശ്രയിച്ചിരിക്കുന്നു. എംസ്ക്രിപ്റ്റനുള്ള ലൈബ്രറികളുടെ വലിയ ശേഖരത്തിൽ ഒന്ന് ഉണ്ടെന്ന് ഇൻറർനെറ്റിൽ കിംവദന്തികൾ ഉണ്ടായിരുന്നു, പക്ഷേ വിശ്വസിക്കാൻ എങ്ങനെയെങ്കിലും ബുദ്ധിമുട്ടായിരുന്നു: ഒന്നാമതായി, ഇത് ഒരു പുതിയ കംപൈലർ ആകാൻ ഉദ്ദേശിച്ചിരുന്നില്ല, രണ്ടാമതായി, ഇത് വളരെ താഴ്ന്ന നിലയിലായിരുന്നു. ലൈബ്രറി എടുക്കാനും JS-ലേക്ക് കംപൈൽ ചെയ്യാനും. ഇത് അസംബ്ലി ഉൾപ്പെടുത്തലുകളുടെ കാര്യമല്ല - ഒരുപക്ഷേ, നിങ്ങൾ ഇത് വളച്ചൊടിക്കുകയാണെങ്കിൽ, ചില കോളിംഗ് കൺവെൻഷനുകൾക്കായി നിങ്ങൾക്ക് സ്റ്റാക്കിൽ ആവശ്യമായ ആർഗ്യുമെന്റുകൾ സൃഷ്ടിക്കാനും അവയില്ലാതെ ഫംഗ്ഷനിലേക്ക് വിളിക്കാനും കഴിയും. എന്നാൽ എംസ്‌ക്രിപ്റ്റൻ ഒരു തന്ത്രപ്രധാനമായ കാര്യമാണ്: ജനറേറ്റ് ചെയ്‌ത കോഡ് ബ്രൗസർ JS എഞ്ചിൻ ഒപ്റ്റിമൈസറിന് പരിചിതമാക്കാൻ, ചില തന്ത്രങ്ങൾ ഉപയോഗിക്കുന്നു. പ്രത്യേകിച്ചും, റീലൂപ്പിംഗ് എന്ന് വിളിക്കപ്പെടുന്നത് - ലഭിച്ച LLVM IR ഉപയോഗിക്കുന്ന ഒരു കോഡ് ജനറേറ്റർ ചില അമൂർത്ത സംക്രമണ നിർദ്ദേശങ്ങൾ ഉപയോഗിച്ച് വിശ്വസനീയമായ ifs, loops മുതലായവ പുനഃസൃഷ്ടിക്കാൻ ശ്രമിക്കുന്നു. ശരി, എങ്ങനെയാണ് വാദങ്ങൾ ഫംഗ്ഷനിലേക്ക് കൈമാറുന്നത്? സ്വാഭാവികമായും, JS ഫംഗ്‌ഷനുകളിലേക്കുള്ള ആർഗ്യുമെന്റുകളായി, അതായത്, സാധ്യമെങ്കിൽ, സ്റ്റാക്കിലൂടെയല്ല.

തുടക്കത്തിൽ, ലിബിഫിക്ക് പകരം ജെഎസ് ഉപയോഗിച്ച് ഒരു ബദലായി എഴുതാനും സ്റ്റാൻഡേർഡ് ടെസ്റ്റുകൾ പ്രവർത്തിപ്പിക്കാനും ഒരു ആശയം ഉണ്ടായിരുന്നു, എന്നാൽ അവസാനം എന്റെ ഹെഡ്ഡർ ഫയലുകൾ എങ്ങനെ നിർമ്മിക്കാം എന്നതിനെക്കുറിച്ച് ആശയക്കുഴപ്പത്തിലായി, അതിലൂടെ അവ നിലവിലുള്ള കോഡ് ഉപയോഗിച്ച് പ്രവർത്തിക്കും - എനിക്ക് എന്തുചെയ്യാനാകും, അവർ പറയുന്നതുപോലെ, "ജോലികൾ വളരെ സങ്കീർണ്ണമാണോ "നമ്മൾ ഇത്ര വിഡ്ഢികളാണോ?" എനിക്ക് മറ്റൊരു ആർക്കിടെക്ചറിലേക്ക് libffi പോർട്ട് ചെയ്യേണ്ടി വന്നു, അങ്ങനെ പറഞ്ഞാൽ - ഭാഗ്യവശാൽ, ഇൻലൈൻ അസംബ്ലിക്ക് (ജാവാസ്ക്രിപ്റ്റിൽ, അതെ - ശരി, ആർക്കിടെക്ചർ എന്തായാലും, അസംബ്ലർ) രണ്ട് മാക്രോകളും എംസ്ക്രിപ്റ്റനുണ്ട്. പൊതുവേ, പ്ലാറ്റ്‌ഫോം-ആശ്രിത ലിബ്ഫി ശകലങ്ങൾ കുറച്ച് സമയത്തേക്ക് ടിങ്കറിംഗിന് ശേഷം, എനിക്ക് കംപൈൽ ചെയ്യാവുന്ന ചില കോഡ് ലഭിക്കുകയും ഞാൻ കണ്ട ആദ്യ ടെസ്റ്റിൽ അത് പ്രവർത്തിപ്പിക്കുകയും ചെയ്തു. എന്നെ അത്ഭുതപ്പെടുത്തി, പരീക്ഷണം വിജയിച്ചു. എന്റെ പ്രതിഭയാൽ സ്തംഭിച്ചുപോയി - തമാശയില്ല, ആദ്യ വിക്ഷേപണം മുതൽ ഇത് പ്രവർത്തിച്ചു - ഞാൻ, ഇപ്പോഴും എന്റെ കണ്ണുകളെ വിശ്വസിക്കുന്നില്ല, ഫലമായുണ്ടാകുന്ന കോഡ് വീണ്ടും നോക്കാൻ പോയി, അടുത്തതായി എവിടെ കുഴിക്കണം എന്ന് വിലയിരുത്താൻ. ഇവിടെ ഞാൻ രണ്ടാമതും തളർന്നുപോയി - എന്റെ ഫംഗ്‌ഷൻ ചെയ്‌ത ഒരേയൊരു കാര്യം ffi_call - ഇത് വിജയകരമായ ഒരു കോൾ റിപ്പോർട്ട് ചെയ്തു. ഒരു വിളി തന്നെ ഉണ്ടായില്ല. അതിനാൽ ഞാൻ എന്റെ ആദ്യത്തെ പുൾ അഭ്യർത്ഥന അയച്ചു, അത് പരീക്ഷയിലെ ഒരു പിശക് തിരുത്തി, അത് ഏത് ഒളിമ്പ്യാഡ് വിദ്യാർത്ഥിക്കും വ്യക്തമാണ് - യഥാർത്ഥ നമ്പറുകൾ ഇതുപോലെ താരതമ്യം ചെയ്യാൻ പാടില്ല. a == b കൂടാതെ എങ്ങനെ a - b < EPS - നിങ്ങൾ മൊഡ്യൂളും ഓർമ്മിക്കേണ്ടതുണ്ട്, അല്ലാത്തപക്ഷം 0 എന്നത് 1/3 ന് തുല്യമായി മാറും... പൊതുവേ, ഞാൻ ലിബ്ഫിയുടെ ഒരു പ്രത്യേക പോർട്ട് കൊണ്ടുവന്നു, അത് ഏറ്റവും ലളിതമായ ടെസ്റ്റുകളിൽ വിജയിക്കുകയും ഏത് ഗ്ലിബ് ആണ്. സമാഹരിച്ചത് - അത് ആവശ്യമാണെന്ന് ഞാൻ തീരുമാനിച്ചു, ഞാൻ അത് പിന്നീട് ചേർക്കാം. മുന്നോട്ട് നോക്കുമ്പോൾ, കംപൈലർ അവസാന കോഡിൽ ലിബ്ഫി ഫംഗ്ഷൻ പോലും ഉൾപ്പെടുത്തിയിട്ടില്ലെന്ന് ഞാൻ പറയും.

പക്ഷേ, ഞാൻ ഇതിനകം പറഞ്ഞതുപോലെ, ചില പരിമിതികളുണ്ട്, കൂടാതെ വിവിധ നിർവചിക്കാത്ത സ്വഭാവങ്ങളുടെ സ്വതന്ത്ര ഉപയോഗത്തിൽ, കൂടുതൽ അസുഖകരമായ ഒരു സവിശേഷത മറച്ചിരിക്കുന്നു - JavaScript ബൈ ഡിസൈൻ പങ്കിട്ട മെമ്മറിയുള്ള മൾട്ടിത്രെഡിംഗിനെ പിന്തുണയ്ക്കുന്നില്ല. തത്വത്തിൽ, ഇത് സാധാരണയായി ഒരു നല്ല ആശയം എന്ന് പോലും വിളിക്കാം, പക്ഷേ സി ത്രെഡുകളുമായി ബന്ധിപ്പിച്ചിരിക്കുന്ന ആർക്കിടെക്ചർ പോർട്ടിംഗ് കോഡിന് വേണ്ടിയല്ല. പൊതുവായി പറഞ്ഞാൽ, പങ്കിട്ട തൊഴിലാളികളെ പിന്തുണയ്‌ക്കുന്നതിൽ ഫയർഫോക്‌സ് പരീക്ഷണം നടത്തുന്നു, കൂടാതെ എംസ്‌ക്രിപ്റ്റന് അവർക്കായി ഒരു pthread നടപ്പിലാക്കൽ ഉണ്ട്, പക്ഷേ ഞാൻ അതിനെ ആശ്രയിക്കാൻ ആഗ്രഹിച്ചില്ല. ക്യുമു കോഡിൽ നിന്ന് മൾട്ടിത്രെഡിംഗ് സാവധാനം റൂട്ട് ഔട്ട് ചെയ്യേണ്ടി വന്നു - അതായത്, ത്രെഡുകൾ എവിടെയാണ് പ്രവർത്തിക്കുന്നതെന്ന് കണ്ടെത്തുക, ഈ ത്രെഡിൽ പ്രവർത്തിക്കുന്ന ലൂപ്പിന്റെ ബോഡി ഒരു പ്രത്യേക ഫംഗ്ഷനിലേക്ക് നീക്കുക, കൂടാതെ അത്തരം ഫംഗ്ഷനുകൾ മെയിൻ ലൂപ്പിൽ നിന്ന് ഓരോന്നായി വിളിക്കുക.

രണ്ടാമത്തെ പരീക്ഷണം

ചില ഘട്ടങ്ങളിൽ, പ്രശ്നം ഇപ്പോഴും ഉണ്ടെന്നും കോഡിന് ചുറ്റും ക്രച്ചുകൾ ഇടയ്ക്കിടെ തള്ളുന്നത് ഒരു ഗുണത്തിനും കാരണമാകില്ലെന്നും വ്യക്തമായി. ഉപസംഹാരം: ഊന്നുവടികൾ ചേർക്കുന്ന പ്രക്രിയ എങ്ങനെയെങ്കിലും ചിട്ടപ്പെടുത്തേണ്ടതുണ്ട്. അതിനാൽ, അക്കാലത്ത് പുതുമയുള്ള പതിപ്പ് 2.4.1 എടുത്തു (2.5.0 അല്ല, കാരണം, ആർക്കറിയാം, പുതിയ പതിപ്പിൽ ഇതുവരെ പിടിക്കപ്പെടാത്ത ബഗുകൾ ഉണ്ടാകും, എനിക്ക് എന്റെ സ്വന്തം ബഗുകൾ മതിയാകും ), സുരക്ഷിതമായി മാറ്റിയെഴുതുക എന്നതായിരുന്നു ആദ്യത്തെ കാര്യം thread-posix.c. ശരി, അതായത്, സുരക്ഷിതമായി: തടയുന്നതിലേക്ക് നയിക്കുന്ന ഒരു ഓപ്പറേഷൻ നടത്താൻ ആരെങ്കിലും ശ്രമിച്ചാൽ, ഫംഗ്ഷൻ ഉടൻ വിളിക്കപ്പെട്ടു abort() - തീർച്ചയായും, ഇത് എല്ലാ പ്രശ്നങ്ങളും ഒറ്റയടിക്ക് പരിഹരിച്ചില്ല, എന്നാൽ പൊരുത്തമില്ലാത്ത ഡാറ്റ ശാന്തമായി സ്വീകരിക്കുന്നതിനേക്കാൾ ഇത് എങ്ങനെയെങ്കിലും മനോഹരമാണ്.

പൊതുവേ, JS-ലേക്ക് കോഡ് പോർട്ടുചെയ്യുന്നതിന് എംസ്ക്രിപ്റ്റൻ ഓപ്ഷനുകൾ വളരെ സഹായകരമാണ് -s ASSERTIONS=1 -s SAFE_HEAP=1 - വിന്യസിക്കാത്ത വിലാസത്തിലേക്കുള്ള കോളുകൾ പോലെയുള്ള ചില തരം നിർവചിക്കാത്ത പെരുമാറ്റം അവർ പിടിക്കുന്നു (ഇത് ടൈപ്പ് ചെയ്ത അറേകൾക്കുള്ള കോഡുമായി ഒട്ടും പൊരുത്തപ്പെടുന്നില്ല. HEAP32[addr >> 2] = 1) അല്ലെങ്കിൽ തെറ്റായ ആർഗ്യുമെന്റുകളുള്ള ഒരു ഫംഗ്‌ഷൻ വിളിക്കുന്നു.

വഴിയിൽ, വിന്യാസ പിശകുകൾ ഒരു പ്രത്യേക പ്രശ്നമാണ്. ഞാൻ ഇതിനകം പറഞ്ഞതുപോലെ, കോഡ് ജനറേഷൻ TCI (ചെറിയ കോഡ് ഇന്റർപ്രെറ്റർ) ക്കായി ക്യുമുവിന് ഒരു "ഡീജനറേറ്റ്" ഇന്റർപ്രെറ്റീവ് ബാക്കെൻഡ് ഉണ്ട്, കൂടാതെ ഒരു പുതിയ ആർക്കിടെക്ചറിൽ ക്യുമു നിർമ്മിക്കാനും പ്രവർത്തിപ്പിക്കാനും, നിങ്ങൾക്ക് ഭാഗ്യമുണ്ടെങ്കിൽ, ഒരു സി കംപൈലർ മതിയാകും. കീവേഡുകൾ "നിങ്ങൾ ഭാഗ്യവാനാണെങ്കിൽ". ഞാൻ നിർഭാഗ്യവാനായിരുന്നു, കൂടാതെ TCI അതിന്റെ ബൈറ്റ്കോഡ് പാഴ്‌സ് ചെയ്യുമ്പോൾ വിന്യസിക്കാത്ത ആക്‌സസ് ഉപയോഗിക്കുന്നതായി തെളിഞ്ഞു. അതായത്, എല്ലാത്തരം ARM-ലും അത്യാവശ്യമായി ലെവൽ ആക്‌സസ് ഉള്ള മറ്റ് ആർക്കിടെക്ചറുകളിലും, ക്യൂമു കംപൈൽ ചെയ്യുന്നു, കാരണം അവയ്ക്ക് നേറ്റീവ് കോഡ് സൃഷ്ടിക്കുന്ന ഒരു സാധാരണ TCG ബാക്കെൻഡ് ഉണ്ട്, എന്നാൽ TCI അവയിൽ പ്രവർത്തിക്കുമോ എന്നത് മറ്റൊരു ചോദ്യമാണ്. എന്നിരുന്നാലും, TCI ഡോക്യുമെന്റേഷൻ സമാനമായ ഒന്ന് വ്യക്തമായി സൂചിപ്പിച്ചു. തൽഫലമായി, അലൈൻ ചെയ്യാത്ത വായനയ്ക്കുള്ള ഫംഗ്‌ഷൻ കോളുകൾ കോഡിലേക്ക് ചേർത്തു, അവ ക്യൂമുവിന്റെ മറ്റൊരു ഭാഗത്ത് കണ്ടെത്തി.

കൂമ്പാര നാശം

തൽഫലമായി, ടിസിഐയിലേക്കുള്ള അലൈൻ ചെയ്യാത്ത ആക്‌സസ് ശരിയാക്കി, ഒരു പ്രധാന ലൂപ്പ് സൃഷ്ടിച്ചു, അത് പ്രോസസ്സർ, ആർ‌സി‌യു, മറ്റ് ചില ചെറിയ കാര്യങ്ങൾ എന്നിവയെ വിളിക്കുന്നു. അതിനാൽ ഞാൻ ഓപ്ഷൻ ഉപയോഗിച്ച് ക്യുമു സമാരംഭിക്കുന്നു -d exec,in_asm,out_asm, അതിനർത്ഥം ഏത് കോഡ് ബ്ലോക്കുകളാണ് എക്സിക്യൂട്ട് ചെയ്യുന്നതെന്ന് നിങ്ങൾ പറയേണ്ടതുണ്ട്, കൂടാതെ പ്രക്ഷേപണ സമയത്ത് അതിഥി കോഡ് എന്തായിരുന്നു, ഹോസ്റ്റ് കോഡ് എന്തായി മാറി (ഈ സാഹചര്യത്തിൽ, ബൈറ്റ്കോഡ്). ഇത് ആരംഭിക്കുന്നു, നിരവധി വിവർത്തന ബ്ലോക്കുകൾ നിർവ്വഹിക്കുന്നു, RCU ഇപ്പോൾ ആരംഭിക്കും എന്ന് ഞാൻ ഇട്ട ഡീബഗ്ഗിംഗ് സന്ദേശം എഴുതുന്നു... ക്രാഷുകൾ abort() ഒരു ചടങ്ങിനുള്ളിൽ free(). ഫംഗ്‌ഷനെ ടിങ്കറിംഗ് ചെയ്തുകൊണ്ട് free() ഹീപ്പ് ബ്ലോക്കിന്റെ ഹെഡറിൽ, അനുവദിച്ച മെമ്മറിക്ക് മുമ്പുള്ള എട്ട് ബൈറ്റുകളിൽ, ബ്ലോക്കിന്റെ വലുപ്പത്തിനോ സമാനമായ മറ്റെന്തെങ്കിലുമോ പകരം മാലിന്യം ഉണ്ടെന്ന് ഞങ്ങൾ കണ്ടെത്തി.

കൂമ്പാരത്തിന്റെ നാശം - എത്ര മനോഹരമാണ്... അത്തരമൊരു സാഹചര്യത്തിൽ, ഉപയോഗപ്രദമായ ഒരു പ്രതിവിധി ഉണ്ട് - (സാധ്യമെങ്കിൽ) അതേ ഉറവിടങ്ങളിൽ നിന്ന്, ഒരു നേറ്റീവ് ബൈനറി കൂട്ടിച്ചേർക്കുകയും വാൽഗ്രിൻഡിന് കീഴിൽ പ്രവർത്തിപ്പിക്കുകയും ചെയ്യുക. കുറച്ച് സമയത്തിന് ശേഷം, ബൈനറി തയ്യാറായി. അതേ ഓപ്‌ഷനുകൾ ഉപയോഗിച്ചാണ് ഞാൻ ഇത് സമാരംഭിക്കുന്നത് - യഥാർത്ഥത്തിൽ എക്‌സിക്യൂഷൻ എത്തുന്നതിന് മുമ്പ്, ഇനീഷ്യലൈസേഷൻ സമയത്ത് പോലും ഇത് ക്രാഷാകും. ഇത് അരോചകമാണ്, തീർച്ചയായും - പ്രത്യക്ഷത്തിൽ, ഉറവിടങ്ങൾ ഒരേപോലെയായിരുന്നില്ല, അത് ആശ്ചര്യകരമല്ല, കാരണം കോൺഫിഗർ കുറച്ച് വ്യത്യസ്ത ഓപ്ഷനുകൾ സ്കൗട്ട് ചെയ്തു, പക്ഷേ എനിക്ക് Valgrind ഉണ്ട് - ആദ്യം ഞാൻ ഈ ബഗ് പരിഹരിക്കും, തുടർന്ന്, ഞാൻ ഭാഗ്യവാനാണെങ്കിൽ , യഥാർത്ഥമായത് ദൃശ്യമാകും. Valgrind-ന് കീഴിൽ ഞാൻ ഒരേ കാര്യം തന്നെ പ്രവർത്തിപ്പിക്കുന്നു... Y-y-y, y-y-y, uh-uh, ഇത് ആരംഭിച്ചു, സാധാരണ തുടക്കത്തിലൂടെ കടന്നുപോയി, തെറ്റായ മെമ്മറി ആക്‌സസിനെ കുറിച്ച് ഒരു മുന്നറിയിപ്പുമില്ലാതെ യഥാർത്ഥ ബഗ് കടന്നുപോയി, വീഴ്ചകളെ കുറിച്ച് പറയേണ്ടതില്ല. ജീവിതം, അവർ പറയുന്നതുപോലെ, ഇതിന് എന്നെ ഒരുക്കിയില്ല - വാൾഗ്രിന്ഡിന് കീഴിൽ സമാരംഭിക്കുമ്പോൾ ഒരു ക്രാഷിംഗ് പ്രോഗ്രാം ക്രാഷ് ചെയ്യുന്നത് നിർത്തുന്നു. അത് എന്തായിരുന്നു എന്നത് ദുരൂഹമാണ്. എന്റെ അനുമാനം, ഇനീഷ്യലൈസേഷൻ സമയത്ത് ഒരു തകർച്ചയ്ക്ക് ശേഷം നിലവിലെ നിർദ്ദേശത്തിന് സമീപം ഒരിക്കൽ, gdb പ്രവർത്തനം കാണിച്ചു. memset-എ ഏതെങ്കിലും ഒരു സാധുവായ പോയിന്റർ ഉപയോഗിച്ച് mmx, അഥവാ xmm രജിസ്റ്റർ ചെയ്യുന്നു, ഒരുപക്ഷേ ഇത് ഏതെങ്കിലും തരത്തിലുള്ള വിന്യാസ പിശകായിരിക്കാം, എന്നിരുന്നാലും ഇപ്പോഴും വിശ്വസിക്കാൻ പ്രയാസമാണ്.

ശരി, Valgrind ഇവിടെ സഹായിക്കുന്നതായി തോന്നുന്നില്ല. ഇവിടെ ഏറ്റവും വെറുപ്പുളവാക്കുന്ന കാര്യം ആരംഭിച്ചു - എല്ലാം ആരംഭിക്കുന്നതായി തോന്നുന്നു, പക്ഷേ ദശലക്ഷക്കണക്കിന് നിർദ്ദേശങ്ങൾക്ക് മുമ്പ് സംഭവിച്ചേക്കാവുന്ന ഒരു സംഭവം കാരണം തികച്ചും അജ്ഞാതമായ കാരണങ്ങളാൽ തകരുന്നു. എങ്ങനെ സമീപിക്കണമെന്ന് പോലും വ്യക്തതയില്ലാതിരുന്ന കാലം. അവസാനം, എനിക്ക് ഇപ്പോഴും ഇരുന്നു ഡീബഗ് ചെയ്യേണ്ടിവന്നു. തലക്കെട്ട് മാറ്റിയെഴുതിയത് പ്രിന്റ് ചെയ്യുമ്പോൾ അത് ഒരു സംഖ്യ പോലെയല്ല, മറിച്ച് ഒരുതരം ബൈനറി ഡാറ്റയാണെന്ന് കാണിച്ചു. കൂടാതെ, ഇതാ, ഈ ബൈനറി സ്ട്രിംഗ് BIOS ഫയലിൽ കണ്ടെത്തി - അതായത്, ഇത് ഒരു ബഫർ ഓവർഫ്ലോ ആണെന്ന് ന്യായമായ ആത്മവിശ്വാസത്തോടെ പറയാൻ കഴിഞ്ഞു, ഇത് ഈ ബഫറിലേക്ക് എഴുതിയതാണെന്ന് പോലും വ്യക്തമാണ്. ശരി, പിന്നെ ഇതുപോലൊന്ന് - എംസ്ക്രിപ്റ്റനിൽ, ഭാഗ്യവശാൽ, വിലാസ സ്ഥലത്തിന്റെ ക്രമരഹിതമാക്കൽ ഇല്ല, അതിലും ദ്വാരങ്ങളൊന്നുമില്ല, അതിനാൽ അവസാന ലോഞ്ച് മുതൽ പോയിന്റർ ഉപയോഗിച്ച് ഡാറ്റ ഔട്ട്പുട്ട് ചെയ്യുന്നതിന് കോഡിന്റെ മധ്യത്തിൽ എവിടെയെങ്കിലും എഴുതാം, ഡാറ്റ നോക്കുക, പോയിന്റർ നോക്കുക, അത് മാറിയിട്ടില്ലെങ്കിൽ, ചിന്തയ്ക്ക് ഭക്ഷണം നേടുക. ശരിയാണ്, എന്തെങ്കിലും മാറ്റത്തിന് ശേഷം ലിങ്ക് ചെയ്യാൻ കുറച്ച് മിനിറ്റ് എടുക്കും, എന്നാൽ നിങ്ങൾക്ക് എന്ത് ചെയ്യാൻ കഴിയും? തൽഫലമായി, താൽക്കാലിക ബഫറിൽ നിന്ന് ഗസ്റ്റ് മെമ്മറിയിലേക്ക് ബയോസ് പകർത്തുന്ന ഒരു പ്രത്യേക ലൈൻ കണ്ടെത്തി - കൂടാതെ, ബഫറിൽ മതിയായ ഇടമില്ലായിരുന്നു. ആ വിചിത്രമായ ബഫർ വിലാസത്തിന്റെ ഉറവിടം കണ്ടെത്തുന്നത് ഒരു ഫംഗ്ഷനിൽ കലാശിച്ചു qemu_anon_ram_alloc ഫയലിൽ oslib-posix.c - യുക്തി ഇതായിരുന്നു: ചിലപ്പോൾ വിലാസം 2 MB വലുപ്പമുള്ള ഒരു വലിയ പേജിലേക്ക് വിന്യസിക്കുന്നത് ഉപയോഗപ്രദമാകും, ഇതിനായി ഞങ്ങൾ ചോദിക്കും mmap ആദ്യം കുറച്ച് കൂടി, തുടർന്ന് ഞങ്ങൾ സഹായത്തോടെ അധികമായി തിരികെ നൽകും munmap. അത്തരം വിന്യാസം ആവശ്യമില്ലെങ്കിൽ, 2 MB ന് പകരം ഞങ്ങൾ ഫലം സൂചിപ്പിക്കും getpagesize() - mmap അത് ഇപ്പോഴും ഒരു വിന്യസിച്ച വിലാസം നൽകും... അതിനാൽ എംസ്ക്രിപ്റ്റനിൽ mmap വെറും വിളിക്കുന്നു malloc, എന്നാൽ തീർച്ചയായും അത് പേജിൽ വിന്യസിക്കുന്നില്ല. പൊതുവേ, കുറച്ച് മാസത്തേക്ക് എന്നെ നിരാശപ്പെടുത്തിയ ഒരു ബഗ് ഒരു മാറ്റത്തിലൂടെ ശരിയാക്കി двух ലൈനുകൾ.

കോളിംഗ് ഫംഗ്‌ഷനുകളുടെ സവിശേഷതകൾ

ഇപ്പോൾ പ്രോസസർ എന്തെങ്കിലും കണക്കാക്കുന്നു, ക്യുമു ക്രാഷ് ചെയ്യുന്നില്ല, പക്ഷേ സ്ക്രീൻ ഓണാക്കുന്നില്ല, കൂടാതെ പ്രോസസർ വേഗത്തിൽ ലൂപ്പുകളിലേക്ക് പോകുന്നു, ഔട്ട്പുട്ട് അനുസരിച്ച് വിലയിരുത്തുന്നു -d exec,in_asm,out_asm. ഒരു സിദ്ധാന്തം ഉയർന്നുവന്നു: ടൈമർ തടസ്സങ്ങൾ (അല്ലെങ്കിൽ, പൊതുവേ, എല്ലാ തടസ്സങ്ങളും) വരുന്നില്ല. തീർച്ചയായും, ചില കാരണങ്ങളാൽ പ്രവർത്തിച്ച നേറ്റീവ് അസംബ്ലിയിൽ നിന്നുള്ള തടസ്സങ്ങൾ നിങ്ങൾ അഴിച്ചാൽ, നിങ്ങൾക്ക് സമാനമായ ഒരു ചിത്രം ലഭിക്കും. എന്നാൽ ഇതൊന്നും ഉത്തരമായിരുന്നില്ല: മുകളിലുള്ള ഓപ്ഷനുമായി നൽകിയ ട്രെയ്‌സുകളുടെ താരതമ്യം, എക്സിക്യൂഷൻ പാതകൾ വളരെ നേരത്തെ തന്നെ വ്യതിചലിച്ചതായി കാണിച്ചു. ലോഞ്ചർ ഉപയോഗിച്ച് റെക്കോർഡ് ചെയ്തതിന്റെ താരതമ്യം ഇവിടെ പറയണം emrun നേറ്റീവ് അസംബ്ലിയുടെ ഔട്ട്പുട്ട് ഉപയോഗിച്ച് ഡീബഗ്ഗിംഗ് ഔട്ട്പുട്ട് പൂർണ്ണമായും മെക്കാനിക്കൽ പ്രക്രിയയല്ല. ഒരു ബ്രൗസറിൽ പ്രവർത്തിക്കുന്ന ഒരു പ്രോഗ്രാം എങ്ങനെ കണക്ട് ചെയ്യുമെന്ന് എനിക്ക് കൃത്യമായി അറിയില്ല emrun, എന്നാൽ ഔട്ട്പുട്ടിലെ ചില വരികൾ പുനഃക്രമീകരിക്കപ്പെട്ടതായി മാറുന്നു, അതിനാൽ വ്യത്യസ്‌തതയിലെ വ്യത്യാസം പാതകൾ വ്യതിചലിച്ചുവെന്ന് അനുമാനിക്കാൻ ഇതുവരെ കാരണമായിട്ടില്ല. പൊതുവേ, നിർദ്ദേശങ്ങൾക്കനുസൃതമായി അത് വ്യക്തമായി ljmpl വ്യത്യസ്‌ത വിലാസങ്ങളിലേക്കുള്ള ഒരു പരിവർത്തനമുണ്ട്, കൂടാതെ സൃഷ്‌ടിച്ച ബൈറ്റ്‌കോഡ് അടിസ്ഥാനപരമായി വ്യത്യസ്തമാണ്: ഒന്നിൽ ഒരു സഹായി ഫംഗ്‌ഷൻ വിളിക്കാനുള്ള നിർദ്ദേശം അടങ്ങിയിരിക്കുന്നു, മറ്റൊന്ന് അങ്ങനെയല്ല. നിർദ്ദേശങ്ങൾ ഗൂഗിൾ ചെയ്‌ത് ഈ നിർദ്ദേശങ്ങൾ വിവർത്തനം ചെയ്യുന്ന കോഡ് പഠിച്ച ശേഷം, ഒന്നാമതായി, രജിസ്റ്ററിൽ അതിന് തൊട്ടുമുമ്പ് cr0 ഒരു റെക്കോഡിംഗ് നടത്തി - ഒരു സഹായി ഉപയോഗിച്ചും - പ്രൊസസറിനെ പ്രൊട്ടക്റ്റഡ് മോഡിലേക്ക് മാറ്റി, രണ്ടാമതായി, js പതിപ്പ് ഒരിക്കലും സംരക്ഷിത മോഡിലേക്ക് മാറിയില്ല. എന്നാൽ നിർദ്ദേശങ്ങൾ നടപ്പിലാക്കുന്നത് പോലെയുള്ള കോഡ് സഹിക്കുന്നതിനുള്ള വിമുഖതയാണ് എംസ്ക്രിപ്റ്റന്റെ മറ്റൊരു സവിശേഷത എന്നതാണ് വസ്തുത. call ടിസിഐയിൽ, ഏത് ഫംഗ്ഷൻ പോയിന്ററും തരത്തിൽ കലാശിക്കുന്നു long long f(int arg0, .. int arg9) - ഫംഗ്‌ഷനുകൾ ശരിയായ ആർഗ്യുമെന്റുകൾ ഉപയോഗിച്ച് വിളിക്കണം. ഈ നിയമം ലംഘിക്കുകയാണെങ്കിൽ, ഡീബഗ്ഗിംഗ് സജ്ജീകരണങ്ങളെ ആശ്രയിച്ച്, പ്രോഗ്രാം ഒന്നുകിൽ ക്രാഷ് ചെയ്യും (ഇത് നല്ലതാണ്) അല്ലെങ്കിൽ തെറ്റായ പ്രവർത്തനത്തെ വിളിക്കുക (ഇത് ഡീബഗ് ചെയ്യുന്നത് സങ്കടകരമാണ്). മൂന്നാമത്തെ ഓപ്ഷനും ഉണ്ട് - ആർഗ്യുമെന്റുകൾ ചേർക്കുന്ന / നീക്കം ചെയ്യുന്ന റാപ്പറുകളുടെ ജനറേഷൻ പ്രവർത്തനക്ഷമമാക്കുക, എന്നാൽ മൊത്തത്തിൽ ഈ റാപ്പറുകൾ ധാരാളം സ്ഥലം എടുക്കുന്നു, വാസ്തവത്തിൽ എനിക്ക് നൂറിൽ കൂടുതൽ റാപ്പറുകൾ മാത്രമേ ആവശ്യമുള്ളൂ. ഇത് വളരെ സങ്കടകരമാണ്, പക്ഷേ കൂടുതൽ ഗുരുതരമായ ഒരു പ്രശ്നമായി മാറി: റാപ്പർ ഫംഗ്‌ഷനുകളുടെ ജനറേറ്റുചെയ്‌ത കോഡിൽ, ആർഗ്യുമെന്റുകൾ പരിവർത്തനം ചെയ്യുകയും പരിവർത്തനം ചെയ്യുകയും ചെയ്‌തു, പക്ഷേ ചിലപ്പോൾ സൃഷ്‌ടിച്ച ആർഗ്യുമെന്റുകളുള്ള ഫംഗ്‌ഷനെ വിളിക്കില്ല - നന്നായി, ഇൻ പോലെ എന്റെ libffi നടപ്പിലാക്കൽ. അതായത്, ചില സഹായികൾ വെറുതെ വധിക്കപ്പെട്ടില്ല.

ഭാഗ്യവശാൽ, ഒരു ഹെഡർ ഫയലിന്റെ രൂപത്തിൽ സഹായികളുടെ മെഷീൻ റീഡബിൾ ലിസ്റ്റുകൾ ക്യുമുവിന് ഉണ്ട്

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+ പ്രവർത്തിപ്പിക്കാൻ കഴിഞ്ഞെങ്കിലും സ്‌ക്രീൻ ഒരിക്കലും ആരംഭിക്കാത്തതുകൊണ്ടാണെന്ന് തോന്നുന്നു. ക്യുമു ബ്ലോക്ക് I/O കോഡ് കോറൗട്ടിനിലാണ് എഴുതിയിരിക്കുന്നതെന്ന് ഇവിടെ വ്യക്തമാക്കേണ്ടത് ആവശ്യമാണ്. എംസ്‌ക്രിപ്റ്റന് അതിന്റേതായ വളരെ തന്ത്രപ്രധാനമായ നടപ്പിലാക്കൽ ഉണ്ട്, പക്ഷേ ക്യുമു കോഡിൽ ഇത് ഇപ്പോഴും പിന്തുണയ്‌ക്കേണ്ടതുണ്ട്, നിങ്ങൾക്ക് ഇപ്പോൾ പ്രോസസർ ഡീബഗ് ചെയ്യാം: Qemu ഓപ്‌ഷനുകളെ പിന്തുണയ്‌ക്കുന്നു -kernel, -initrd, -append, ഇതുപയോഗിച്ച് നിങ്ങൾക്ക് ലിനക്സ് ബൂട്ട് ചെയ്യാം അല്ലെങ്കിൽ, ഉദാഹരണത്തിന്, ബ്ലോക്ക് ഡിവൈസുകൾ ഉപയോഗിക്കാതെ memtest86+. എന്നാൽ ഇവിടെയാണ് പ്രശ്നം: നേറ്റീവ് അസംബ്ലിയിൽ ഒരാൾക്ക് കൺസോളിലേക്കുള്ള ലിനക്സ് കേർണൽ ഔട്ട്പുട്ട് ഓപ്ഷൻ ഉപയോഗിച്ച് കാണാൻ കഴിയും. -nographic, കൂടാതെ ബ്രൗസറിൽ നിന്ന് അത് സമാരംഭിച്ച ടെർമിനലിലേക്ക് ഔട്ട്പുട്ട് ഇല്ല emrun, വന്നില്ല. അതായത്, ഇത് വ്യക്തമല്ല: പ്രോസസർ പ്രവർത്തിക്കുന്നില്ല അല്ലെങ്കിൽ ഗ്രാഫിക്സ് ഔട്ട്പുട്ട് പ്രവർത്തിക്കുന്നില്ല. പിന്നെ അൽപ്പം കാത്തിരിക്കണമെന്നു തോന്നി. “പ്രോസസർ ഉറങ്ങുകയല്ല, സാവധാനം മിന്നിമറയുകയാണ്” എന്ന് മനസ്സിലായി, ഏകദേശം അഞ്ച് മിനിറ്റിനുശേഷം കേർണൽ ഒരു കൂട്ടം സന്ദേശങ്ങൾ കൺസോളിലേക്ക് എറിഞ്ഞ് ഹാംഗ് ചെയ്യുന്നത് തുടർന്നു. പ്രോസസർ, പൊതുവേ, പ്രവർത്തിക്കുന്നുണ്ടെന്ന് വ്യക്തമായി, SDL2-മായി പ്രവർത്തിക്കുന്നതിനുള്ള കോഡ് ഞങ്ങൾ പരിശോധിക്കേണ്ടതുണ്ട്. നിർഭാഗ്യവശാൽ, ഈ ലൈബ്രറി എങ്ങനെ ഉപയോഗിക്കണമെന്ന് എനിക്കറിയില്ല, അതിനാൽ ചില സ്ഥലങ്ങളിൽ എനിക്ക് ക്രമരഹിതമായി പ്രവർത്തിക്കേണ്ടി വന്നു. ചില സമയങ്ങളിൽ, ഒരു നീല പശ്ചാത്തലത്തിൽ സ്‌ക്രീനിൽ സമാന്തര 0 എന്ന രേഖ മിന്നിമറഞ്ഞു, അത് ചില ചിന്തകൾ നിർദ്ദേശിച്ചു. അവസാനം, ഒരു ഫിസിക്കൽ വിൻഡോയിൽ ക്യുമു നിരവധി വെർച്വൽ വിൻഡോകൾ തുറക്കുന്നു എന്നതാണ് പ്രശ്‌നം എന്ന് മനസ്സിലായി, അതിനിടയിൽ നിങ്ങൾക്ക് Ctrl-Alt-n ഉപയോഗിച്ച് മാറാം: ഇത് നേറ്റീവ് ബിൽഡിൽ പ്രവർത്തിക്കുന്നു, പക്ഷേ എംസ്‌ക്രിപ്റ്റനിൽ അല്ല. ഓപ്ഷനുകൾ ഉപയോഗിച്ച് അനാവശ്യ വിൻഡോകൾ ഒഴിവാക്കിയ ശേഷം -monitor none -parallel none -serial none ഓരോ ഫ്രെയിമിലും മുഴുവൻ സ്‌ക്രീനും വീണ്ടും വരയ്ക്കാനുള്ള നിർദ്ദേശങ്ങളും, എല്ലാം പെട്ടെന്ന് പ്രവർത്തിച്ചു.

കൊറൂട്ടിൻസ്

അതിനാൽ, ബ്രൗസറിലെ എമുലേഷൻ പ്രവർത്തിക്കുന്നു, പക്ഷേ നിങ്ങൾക്ക് അതിൽ രസകരമായ ഒന്നും പ്രവർത്തിപ്പിക്കാൻ കഴിയില്ല, കാരണം I/O ബ്ലോക്ക് ഇല്ല - നിങ്ങൾ കോറൂട്ടീനുകൾക്കുള്ള പിന്തുണ നടപ്പിലാക്കേണ്ടതുണ്ട്. ക്യുമുവിന് ഇതിനകം തന്നെ നിരവധി കോറൂട്ടീൻ ബാക്കെൻഡുകളുണ്ട്, എന്നാൽ ജാവാസ്ക്രിപ്റ്റിന്റെയും എംസ്ക്രിപ്റ്റൻ കോഡ് ജനറേറ്ററിന്റെയും സ്വഭാവം കാരണം, നിങ്ങൾക്ക് സ്റ്റാക്കുകൾ ജഗ്ലിംഗ് ആരംഭിക്കാൻ കഴിയില്ല. "എല്ലാം പോയി, പ്ലാസ്റ്റർ നീക്കംചെയ്യുന്നു" എന്ന് തോന്നുന്നു, പക്ഷേ എംസ്ക്രിപ്റ്റൻ ഡവലപ്പർമാർ ഇതിനകം തന്നെ എല്ലാം ശ്രദ്ധിച്ചിട്ടുണ്ട്. ഇത് വളരെ തമാശയായി നടപ്പിലാക്കുന്നു: ഇതുപോലുള്ള ഒരു ഫംഗ്ഷൻ കോളിനെ സംശയാസ്പദമായി വിളിക്കാം emscripten_sleep കൂടാതെ മറ്റ് നിരവധി Asyncify മെക്കാനിസം ഉപയോഗിക്കുന്നു, കൂടാതെ മുമ്പത്തെ രണ്ട് കേസുകളിൽ ഒന്ന് സ്റ്റാക്കിന് താഴെ സംഭവിക്കാനിടയുള്ള ഏത് ഫംഗ്ഷനിലേക്കുള്ള പോയിന്റർ കോളുകളും കോളുകളും. ഇപ്പോൾ, സംശയാസ്പദമായ ഓരോ കോളിനും മുമ്പായി, ഞങ്ങൾ ഒരു അസിൻക് സന്ദർഭം തിരഞ്ഞെടുക്കും, കോളിന് തൊട്ടുപിന്നാലെ, ഒരു അസിൻക്രണസ് കോൾ സംഭവിച്ചിട്ടുണ്ടോ എന്ന് ഞങ്ങൾ പരിശോധിക്കും, അങ്ങനെയാണെങ്കിൽ, ഈ അസിൻക് സന്ദർഭത്തിൽ എല്ലാ ലോക്കൽ വേരിയബിളുകളും ഞങ്ങൾ സംരക്ഷിക്കും, ഏത് ഫംഗ്‌ഷനാണെന്ന് സൂചിപ്പിക്കുക. എക്‌സിക്യൂഷൻ തുടരേണ്ട സമയത്തേക്ക് നിയന്ത്രണം കൈമാറുന്നതിനും നിലവിലെ ഫംഗ്‌ഷനിൽ നിന്ന് പുറത്തുകടക്കുന്നതിനും. ഇവിടെയാണ് പ്രഭാവം പഠിക്കാനുള്ള സാധ്യതയുള്ളത് പാഴാക്കുന്നു - ഒരു അസിൻക്രണസ് കോളിൽ നിന്ന് മടങ്ങിയതിന് ശേഷം കോഡ് എക്‌സിക്യൂഷൻ തുടരുന്നതിന്, കംപൈലർ ഒരു സംശയാസ്പദമായ കോളിന് ശേഷം ആരംഭിക്കുന്ന ഫംഗ്‌ഷന്റെ “സ്റ്റബുകൾ” ജനറേറ്റുചെയ്യുന്നു - ഇതുപോലെ: n സംശയാസ്പദമായ കോളുകൾ ഉണ്ടെങ്കിൽ, ഫംഗ്‌ഷൻ എവിടെയെങ്കിലും n/2 വിപുലീകരിക്കും. തവണ — ഇത് ഇപ്പോഴും, ഇല്ലെങ്കിൽ, അസമന്വിതമാകാൻ സാധ്യതയുള്ള ഓരോ കോളിനും ശേഷം, ഒറിജിനൽ ഫംഗ്‌ഷനിലേക്ക് ചില ലോക്കൽ വേരിയബിളുകൾ സംരക്ഷിക്കുന്നത് നിങ്ങൾ ചേർക്കേണ്ടതുണ്ടെന്ന് ഓർമ്മിക്കുക. തുടർന്ന്, എനിക്ക് പൈത്തണിൽ ഒരു ലളിതമായ സ്ക്രിപ്റ്റ് പോലും എഴുതേണ്ടിവന്നു, അത് "അസമന്വിതതയെ സ്വയം കടന്നുപോകാൻ അനുവദിക്കരുത്" (അതായത്, സ്റ്റാക്ക് പ്രൊമോഷനും ഞാൻ ഇപ്പോൾ വിവരിച്ചതെല്ലാം ചെയ്യാത്തതുമായ അമിതമായി ഉപയോഗിക്കുന്ന ഫംഗ്ഷനുകളുടെ ഒരു കൂട്ടത്തെ അടിസ്ഥാനമാക്കിയാണ്. അവയിൽ പ്രവർത്തിക്കുക), പോയിന്ററുകൾ വഴിയുള്ള കോളുകളെ സൂചിപ്പിക്കുന്നു, അതിൽ ഫംഗ്ഷനുകൾ കംപൈലർ അവഗണിക്കണം, അതിനാൽ ഈ ഫംഗ്ഷനുകൾ അസമന്വിതമായി കണക്കാക്കില്ല. തുടർന്ന് 60 MB-യിൽ താഴെയുള്ള JS ഫയലുകൾ വളരെ കൂടുതലാണ് - നമുക്ക് കുറഞ്ഞത് 30 എന്ന് പറയാം. ഒരിക്കൽ ഞാൻ ഒരു അസംബ്ലി സ്ക്രിപ്റ്റ് സജ്ജീകരിക്കുമ്പോൾ, അബദ്ധവശാൽ ലിങ്കർ ഓപ്‌ഷനുകൾ വലിച്ചെറിഞ്ഞു. -O3. ഞാൻ ജനറേറ്റ് ചെയ്‌ത കോഡ് പ്രവർത്തിപ്പിക്കുന്നു, Chromium മെമ്മറി ഇല്ലാതാക്കുകയും ക്രാഷുചെയ്യുകയും ചെയ്യുന്നു. അവൻ ഡൗൺലോഡ് ചെയ്യാൻ ശ്രമിക്കുന്നത് അബദ്ധവശാൽ ഞാൻ നോക്കി... ശരി, എനിക്ക് എന്ത് പറയാൻ കഴിയും, 500+ MB ജാവാസ്ക്രിപ്റ്റ് നന്നായി പഠിച്ച് ഒപ്റ്റിമൈസ് ചെയ്യാൻ എന്നോട് ആവശ്യപ്പെട്ടിരുന്നെങ്കിൽ ഞാനും മരവിച്ചേനെ.

നിർഭാഗ്യവശാൽ, Asyncify പിന്തുണാ ലൈബ്രറി കോഡിലെ പരിശോധനകൾ പൂർണ്ണമായും സൗഹൃദപരമായിരുന്നില്ല longjmp-s എന്നത് വെർച്വൽ പ്രോസസർ കോഡിൽ ഉപയോഗിക്കുന്നു, എന്നാൽ ഈ പരിശോധനകൾ പ്രവർത്തനരഹിതമാക്കുകയും എല്ലാം ശരിയാണെന്ന മട്ടിൽ സന്ദർഭങ്ങൾ ബലമായി പുനഃസ്ഥാപിക്കുകയും ചെയ്യുന്ന ഒരു ചെറിയ പാച്ചിന് ശേഷം, കോഡ് പ്രവർത്തിച്ചു. തുടർന്ന് ഒരു വിചിത്രമായ കാര്യം ആരംഭിച്ചു: ചിലപ്പോൾ സിൻക്രൊണൈസേഷൻ കോഡിലെ പരിശോധനകൾ പ്രവർത്തനക്ഷമമാകും - എക്സിക്യൂഷൻ ലോജിക് അനുസരിച്ച്, അത് ബ്ലോക്ക് ചെയ്യപ്പെടുകയാണെങ്കിൽ കോഡ് ക്രാഷ് ചെയ്യുന്ന അതേവ - ആരെങ്കിലും ഇതിനകം പിടിച്ചെടുത്ത മ്യൂട്ടക്സ് പിടിച്ചെടുക്കാൻ ശ്രമിച്ചു. ഭാഗ്യവശാൽ, ഇത് സീരിയലൈസ് ചെയ്ത കോഡിലെ ഒരു ലോജിക്കൽ പ്രശ്‌നമല്ലെന്ന് തെളിഞ്ഞു - എംസ്‌ക്രിപ്റ്റൻ നൽകുന്ന സ്റ്റാൻഡേർഡ് മെയിൻ ലൂപ്പ് ഫംഗ്‌ഷണാലിറ്റിയാണ് ഞാൻ ഉപയോഗിക്കുന്നത്, പക്ഷേ ചിലപ്പോൾ അസിൻക്രണസ് കോൾ സ്റ്റാക്ക് പൂർണ്ണമായും അഴിച്ചുമാറ്റും, ആ നിമിഷം അത് പരാജയപ്പെടും. setTimeout പ്രധാന ലൂപ്പിൽ നിന്ന് - അങ്ങനെ, മുൻ ആവർത്തനത്തിൽ നിന്ന് വിട്ടുപോകാതെ കോഡ് പ്രധാന ലൂപ്പ് ആവർത്തനത്തിലേക്ക് പ്രവേശിച്ചു. അനന്തമായ ലൂപ്പിൽ വീണ്ടും എഴുതി emscripten_sleep, മ്യൂട്ടക്സുകളിലെ പ്രശ്നങ്ങൾ നിലച്ചു. കോഡ് കൂടുതൽ യുക്തിസഹമായി മാറിയിരിക്കുന്നു - എല്ലാത്തിനുമുപരി, അടുത്ത ആനിമേഷൻ ഫ്രെയിം തയ്യാറാക്കുന്ന ചില കോഡുകൾ എന്റെ പക്കലില്ല - പ്രോസസർ എന്തെങ്കിലും കണക്കാക്കുകയും സ്ക്രീൻ ഇടയ്ക്കിടെ അപ്ഡേറ്റ് ചെയ്യുകയും ചെയ്യുന്നു. എന്നിരുന്നാലും, പ്രശ്‌നങ്ങൾ അവിടെ അവസാനിച്ചില്ല: ചിലപ്പോൾ ക്യുമു എക്സിക്യൂഷൻ ഒഴിവാക്കലുകളോ പിശകുകളോ ഇല്ലാതെ നിശബ്ദമായി അവസാനിക്കും. ആ നിമിഷം ഞാൻ അത് ഉപേക്ഷിച്ചു, പക്ഷേ, മുന്നോട്ട് നോക്കുമ്പോൾ, പ്രശ്നം ഇതായിരുന്നുവെന്ന് ഞാൻ പറയും: കൊറൂട്ടിൻ കോഡ്, വാസ്തവത്തിൽ, ഉപയോഗിക്കുന്നില്ല setTimeout (അല്ലെങ്കിൽ കുറഞ്ഞത് നിങ്ങൾ വിചാരിക്കുന്നതുപോലെ അല്ല): പ്രവർത്തനം emscripten_yield അസിൻക്രണസ് കോൾ ഫ്ലാഗ് സജ്ജീകരിക്കുന്നു. മുഴുവൻ കാര്യവും അതാണ് emscripten_coroutine_next ഒരു അസിൻക്രണസ് ഫംഗ്‌ഷൻ അല്ല: ആന്തരികമായി അത് ഫ്ലാഗ് പരിശോധിക്കുകയും പുനഃസജ്ജമാക്കുകയും ആവശ്യമുള്ളിടത്തേക്ക് നിയന്ത്രണം കൈമാറുകയും ചെയ്യുന്നു. അതായത്, സ്റ്റാക്കിന്റെ പ്രമോഷൻ അവിടെ അവസാനിക്കുന്നു. നിലവിലുള്ള കോറൗട്ടിൻ ബാക്കെൻഡിൽ നിന്നും ഫംഗ്‌ഷൻ എന്നതിൽ നിന്നും ഒരു പ്രധാന കോഡ് കോപ്പി ഞാൻ പകർത്താത്തതിനാൽ, കോറൗട്ടിൻ പൂൾ പ്രവർത്തനരഹിതമാക്കിയപ്പോൾ പ്രത്യക്ഷപ്പെട്ട ഉപയോഗത്തിന് ശേഷം-ഫ്രീ എന്നതായിരുന്നു പ്രശ്നം. qemu_in_coroutine യഥാർത്ഥത്തിൽ അത് തെറ്റായി നൽകേണ്ടിയിരുന്നപ്പോൾ സത്യമായി മടങ്ങി. ഇത് ഒരു കോളിലേക്ക് നയിച്ചു emscripten_yield, അതിന് മുകളിൽ സ്റ്റാക്കിൽ ആരും ഉണ്ടായിരുന്നില്ല emscripten_coroutine_next, സ്റ്റാക്ക് വളരെ മുകളിലേക്ക് തുറന്നു, പക്ഷേ ഇല്ല setTimeout, ഞാൻ ഇതിനകം പറഞ്ഞതുപോലെ, പ്രദർശിപ്പിച്ചിട്ടില്ല.

ജാവാസ്ക്രിപ്റ്റ് കോഡ് ജനറേഷൻ

ഇവിടെ, വാസ്തവത്തിൽ, വാഗ്ദത്തം "അരിഞ്ഞ ഇറച്ചി തിരിച്ച്" ആണ്. ശരിക്കുമല്ല. തീർച്ചയായും, നമ്മൾ ബ്രൗസറിൽ Qemu ഉം Node.js ഉം പ്രവർത്തിപ്പിക്കുകയാണെങ്കിൽ, സ്വാഭാവികമായും, Qemu-ൽ കോഡ് ജനറേഷൻ കഴിഞ്ഞാൽ നമുക്ക് തികച്ചും തെറ്റായ JavaScript ലഭിക്കും. എന്നിട്ടും, ഒരുതരം വിപരീത പരിവർത്തനം.

ആദ്യം, ക്യുമു എങ്ങനെ പ്രവർത്തിക്കുന്നു എന്നതിനെക്കുറിച്ച് കുറച്ച്. ദയവായി എന്നോട് ഉടൻ ക്ഷമിക്കൂ: ഞാൻ ഒരു പ്രൊഫഷണൽ ക്യുമു ഡെവലപ്പർ അല്ല, ചില സ്ഥലങ്ങളിൽ എന്റെ നിഗമനങ്ങൾ തെറ്റായിരിക്കാം. അവർ പറയുന്നതുപോലെ, "വിദ്യാർത്ഥിയുടെ അഭിപ്രായം ടീച്ചറുടെ അഭിപ്രായം, പീനോയുടെ ആക്സിയോമാറ്റിക്സ്, സാമാന്യബുദ്ധി എന്നിവയുമായി പൊരുത്തപ്പെടേണ്ടതില്ല." Qemu-ന് പിന്തുണയ്‌ക്കുന്ന ഒരു നിശ്ചിത എണ്ണം ഗസ്റ്റ് ആർക്കിടെക്ചറുകൾ ഉണ്ട്, ഓരോന്നിനും ഒരു ഡയറക്‌ടറി ഉണ്ട് target-i386. നിർമ്മിക്കുമ്പോൾ, നിങ്ങൾക്ക് നിരവധി ഗസ്റ്റ് ആർക്കിടെക്ചറുകൾക്കുള്ള പിന്തുണ വ്യക്തമാക്കാൻ കഴിയും, എന്നാൽ ഫലം നിരവധി ബൈനറികൾ മാത്രമായിരിക്കും. ഗസ്റ്റ് ആർക്കിടെക്ചറിനെ പിന്തുണയ്ക്കുന്നതിനുള്ള കോഡ്, ചില ആന്തരിക ക്യുമു ഓപ്പറേഷനുകൾ സൃഷ്ടിക്കുന്നു, അത് ഹോസ്റ്റ് ആർക്കിടെക്ചറിനുള്ള മെഷീൻ കോഡായി TCG (ടൈനി കോഡ് ജനറേറ്റർ) മാറുന്നു. tcg ഡയറക്‌ടറിയിലുള്ള റീഡ്‌മെ ഫയലിൽ പറഞ്ഞിരിക്കുന്നതുപോലെ, ഇത് യഥാർത്ഥത്തിൽ ഒരു സാധാരണ സി കംപൈലറിന്റെ ഭാഗമായിരുന്നു, അത് പിന്നീട് JIT-യ്‌ക്ക് അനുയോജ്യമാക്കി. അതിനാൽ, ഉദാഹരണത്തിന്, ഈ ഡോക്യുമെന്റിന്റെ അടിസ്ഥാനത്തിൽ ടാർഗെറ്റ് ആർക്കിടെക്ചർ ഇനി ഒരു ഗസ്റ്റ് ആർക്കിടെക്ചറല്ല, മറിച്ച് ഒരു ഹോസ്റ്റ് ആർക്കിടെക്ചറാണ്. ചില ഘട്ടങ്ങളിൽ, മറ്റൊരു ഘടകം പ്രത്യക്ഷപ്പെട്ടു - ടിനി കോഡ് ഇന്റർപ്രെറ്റർ (ടിസിഐ), ഒരു നിർദ്ദിഷ്ട ഹോസ്റ്റ് ആർക്കിടെക്ചറിനായി ഒരു കോഡ് ജനറേറ്ററിന്റെ അഭാവത്തിൽ കോഡ് (ഏതാണ്ട് അതേ ആന്തരിക പ്രവർത്തനങ്ങൾ) എക്സിക്യൂട്ട് ചെയ്യണം. വാസ്തവത്തിൽ, അതിന്റെ ഡോക്യുമെന്റേഷൻ പ്രസ്താവിക്കുന്നതുപോലെ, ഈ വ്യാഖ്യാതാവ് എല്ലായ്‌പ്പോഴും ഒരു JIT കോഡ് ജനറേറ്റർ പോലെ മികച്ച രീതിയിൽ പ്രവർത്തിക്കണമെന്നില്ല, വേഗതയുടെ കാര്യത്തിൽ മാത്രമല്ല, ഗുണപരമായും. അദ്ദേഹത്തിന്റെ വിവരണം തികച്ചും പ്രസക്തമാണെന്ന് എനിക്ക് ഉറപ്പില്ലെങ്കിലും.

ആദ്യം ഞാൻ ഒരു പൂർണ്ണമായ TCG ബാക്കെൻഡ് നിർമ്മിക്കാൻ ശ്രമിച്ചു, പക്ഷേ സോഴ്സ് കോഡിലും ബൈറ്റ്കോഡ് നിർദ്ദേശങ്ങളുടെ പൂർണ്ണമായ വിവരണത്തിലും പെട്ടെന്ന് ആശയക്കുഴപ്പത്തിലായി, അതിനാൽ ഞാൻ TCI വ്യാഖ്യാതാവിനെ പൊതിയാൻ തീരുമാനിച്ചു. ഇത് നിരവധി ഗുണങ്ങൾ നൽകി:

  • ഒരു കോഡ് ജനറേറ്റർ നടപ്പിലാക്കുമ്പോൾ, നിങ്ങൾക്ക് നിർദ്ദേശങ്ങളുടെ വിവരണത്തിലല്ല, മറിച്ച് ഇന്റർപ്രെറ്റർ കോഡിലേക്കാണ് നോക്കാൻ കഴിയുക
  • അഭിമുഖീകരിക്കുന്ന എല്ലാ വിവർത്തന ബ്ലോക്കുകൾക്കും നിങ്ങൾക്ക് ഫംഗ്‌ഷനുകൾ സൃഷ്‌ടിക്കാനാകും, പക്ഷേ, ഉദാഹരണത്തിന്, നൂറാമത്തെ എക്‌സിക്യൂഷനുശേഷം മാത്രം
  • ജനറേറ്റ് ചെയ്‌ത കോഡ് മാറുകയാണെങ്കിൽ (ഇത് സാധ്യമാണെന്ന് തോന്നുന്നു, പാച്ച് എന്ന വാക്ക് അടങ്ങിയ പേരുകളുള്ള ഫംഗ്‌ഷനുകൾ അനുസരിച്ച് വിലയിരുത്തുക), എനിക്ക് സൃഷ്‌ടിച്ച JS കോഡ് അസാധുവാക്കേണ്ടി വരും, പക്ഷേ അതിൽ നിന്ന് പുനഃസൃഷ്ടിക്കാൻ എനിക്ക് എന്തെങ്കിലും ഉണ്ടായിരിക്കും.

മൂന്നാമത്തെ പോയിന്റ് സംബന്ധിച്ച്, കോഡ് ആദ്യമായി എക്സിക്യൂട്ട് ചെയ്തതിന് ശേഷം പാച്ചിംഗ് സാധ്യമാണെന്ന് എനിക്ക് ഉറപ്പില്ല, എന്നാൽ ആദ്യത്തെ രണ്ട് പോയിന്റുകൾ മതിയാകും.

തുടക്കത്തിൽ, യഥാർത്ഥ ബൈറ്റ്കോഡ് നിർദ്ദേശത്തിന്റെ വിലാസത്തിൽ ഒരു വലിയ സ്വിച്ചിന്റെ രൂപത്തിലാണ് കോഡ് സൃഷ്ടിച്ചത്, എന്നാൽ പിന്നീട്, എംസ്ക്രിപ്റ്റനെക്കുറിച്ചുള്ള ലേഖനം ഓർത്തു, ജനറേറ്റ് ചെയ്ത JS ഒപ്റ്റിമൈസേഷൻ, റീലൂപ്പിംഗ്, കൂടുതൽ ഹ്യൂമൻ കോഡ് സൃഷ്ടിക്കാൻ ഞാൻ തീരുമാനിച്ചു, പ്രത്യേകിച്ചും അത് അനുഭവപരമായി. വിവർത്തന ബ്ലോക്കിലേക്കുള്ള ഏക പ്രവേശന പോയിന്റ് അതിന്റെ ആരംഭം മാത്രമാണെന്ന് മനസ്സിലായി. അധികം താമസിയാതെ പറഞ്ഞു, കുറച്ച് സമയത്തിന് ശേഷം ഞങ്ങൾക്ക് ഒരു കോഡ് ജനറേറ്റർ ഉണ്ടായിരുന്നു, അത് ifs ഉപയോഗിച്ച് കോഡ് ജനറേറ്റുചെയ്‌തു (ലൂപ്പുകളില്ലെങ്കിലും). പക്ഷേ നിർഭാഗ്യവശാൽ, നിർദ്ദേശങ്ങൾ കുറച്ച് തെറ്റായ ദൈർഘ്യമുള്ളതാണെന്ന സന്ദേശം നൽകി അത് തകർന്നു. മാത്രമല്ല, ഈ ആവർത്തന തലത്തിലെ അവസാന നിർദ്ദേശം brcond. ശരി, ആവർത്തിച്ചുള്ള കോളിന് മുമ്പും ശേഷവും ഈ നിർദ്ദേശത്തിന്റെ ജനറേഷനിലേക്ക് ഞാൻ സമാനമായ ഒരു പരിശോധന ചേർക്കും കൂടാതെ... അവയിലൊന്ന് പോലും എക്‌സിക്യൂട്ട് ചെയ്‌തില്ല, എന്നാൽ അസെർട്ട് സ്വിച്ചിന് ശേഷവും അവ പരാജയപ്പെട്ടു. അവസാനം, ജനറേറ്റ് ചെയ്‌ത കോഡ് പഠിച്ച ശേഷം, സ്വിച്ചിന് ശേഷം, നിലവിലെ നിർദ്ദേശത്തിലേക്കുള്ള പോയിന്റർ സ്റ്റാക്കിൽ നിന്ന് വീണ്ടും ലോഡുചെയ്‌ത് ജനറേറ്റുചെയ്‌ത JavaScript കോഡ് ഉപയോഗിച്ച് പുനരാലേഖനം ചെയ്‌തിരിക്കാമെന്ന് ഞാൻ മനസ്സിലാക്കി. അങ്ങനെ സംഭവിച്ചു. ബഫർ ഒരു മെഗാബൈറ്റിൽ നിന്ന് പത്തിലേക്ക് ഉയർത്തുന്നത് ഒന്നിനും ഇടയാക്കില്ല, കൂടാതെ കോഡ് ജനറേറ്റർ സർക്കിളുകളിൽ പ്രവർത്തിക്കുന്നുണ്ടെന്ന് വ്യക്തമായി. നിലവിലെ ടിബിയുടെ അതിരുകൾക്കപ്പുറത്തേക്ക് ഞങ്ങൾ പോയിട്ടില്ലെന്ന് ഞങ്ങൾ പരിശോധിക്കേണ്ടതുണ്ട്, അങ്ങനെ ചെയ്താൽ, അടുത്ത ടിബിയുടെ വിലാസം ഒരു മൈനസ് ചിഹ്നത്തോടെ നൽകുക, അതുവഴി ഞങ്ങൾക്ക് എക്‌സിക്യൂഷൻ തുടരാം. കൂടാതെ, "ഈ ബൈറ്റ്കോഡ് മാറിയിട്ടുണ്ടെങ്കിൽ ജനറേറ്റ് ചെയ്ത ഫംഗ്‌ഷനുകൾ അസാധുവാകണം?" എന്ന പ്രശ്നം ഇത് പരിഹരിക്കുന്നു. — ഈ വിവർത്തന ബ്ലോക്കുമായി പൊരുത്തപ്പെടുന്ന പ്രവർത്തനം മാത്രം അസാധുവാക്കിയാൽ മതി. വഴിയിൽ, ഞാൻ Chromium-ൽ എല്ലാം ഡീബഗ് ചെയ്‌തെങ്കിലും (ഞാൻ Firefox ഉപയോഗിക്കുന്നതിനാലും പരീക്ഷണങ്ങൾക്കായി ഒരു പ്രത്യേക ബ്രൗസർ ഉപയോഗിക്കുന്നത് എനിക്ക് എളുപ്പമായതിനാലും), Firefox എന്നെ asm.js സ്റ്റാൻഡേർഡുമായുള്ള പൊരുത്തക്കേടുകൾ ശരിയാക്കാൻ സഹായിച്ചു, അതിനുശേഷം കോഡ് വേഗത്തിൽ പ്രവർത്തിക്കാൻ തുടങ്ങി. ക്രോമിയം.

സൃഷ്ടിച്ച കോഡിന്റെ ഉദാഹരണം

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

തീരുമാനം

അതിനാൽ, ജോലി ഇപ്പോഴും പൂർത്തിയായിട്ടില്ല, പക്ഷേ ഈ ദീർഘകാല നിർമ്മാണം രഹസ്യമായി പൂർണതയിലേക്ക് കൊണ്ടുവരുന്നതിൽ ഞാൻ മടുത്തു. അതിനാൽ, ഇപ്പോൾ ഉള്ളത് പ്രസിദ്ധീകരിക്കാൻ ഞാൻ തീരുമാനിച്ചു. കോഡ് സ്ഥലങ്ങളിൽ അൽപ്പം ഭയാനകമാണ്, കാരണം ഇതൊരു പരീക്ഷണമാണ്, എന്തുചെയ്യണമെന്ന് മുൻകൂട്ടി വ്യക്തമല്ല. ഒരുപക്ഷേ, ക്യൂമുവിന്റെ കൂടുതൽ ആധുനിക പതിപ്പിന് മുകളിൽ സാധാരണ ആറ്റോമിക് കമ്മിറ്റുകൾ നൽകുന്നത് മൂല്യവത്താണ്. ഇതിനിടയിൽ, ഒരു ബ്ലോഗ് ഫോർമാറ്റിൽ ഗീതയിൽ ഒരു ത്രെഡ് ഉണ്ട്: കുറഞ്ഞത് എങ്ങനെയെങ്കിലും കടന്നുപോകുന്ന ഓരോ "ലെവലിനും" റഷ്യൻ ഭാഷയിൽ വിശദമായ വ്യാഖ്യാനം ചേർത്തിട്ടുണ്ട്. യഥാർത്ഥത്തിൽ, ഈ ലേഖനം ഒരു പരിധിവരെ നിഗമനത്തിന്റെ പുനരാഖ്യാനമാണ് git log.

നിങ്ങൾക്ക് എല്ലാം പരീക്ഷിക്കാം ഇവിടെ (ഗതാഗതം സൂക്ഷിക്കുക).

എന്താണ് ഇതിനകം പ്രവർത്തിക്കുന്നത്:

  • x86 വെർച്വൽ പ്രോസസർ പ്രവർത്തിക്കുന്നു
  • മെഷീൻ കോഡിൽ നിന്ന് ജാവാസ്ക്രിപ്റ്റിലേക്ക് ഒരു JIT കോഡ് ജനറേറ്ററിന്റെ പ്രവർത്തന പ്രോട്ടോടൈപ്പ് ഉണ്ട്
  • മറ്റ് 32-ബിറ്റ് ഗസ്റ്റ് ആർക്കിടെക്ചറുകൾ കൂട്ടിച്ചേർക്കുന്നതിന് ഒരു ടെംപ്ലേറ്റ് ഉണ്ട്: ലോഡിംഗ് ഘട്ടത്തിൽ ബ്രൗസറിൽ ഫ്രീസുചെയ്യുന്ന MIPS ആർക്കിടെക്ചറിനായി നിങ്ങൾക്ക് ഇപ്പോൾ Linux-നെ അഭിനന്ദിക്കാം.

നിങ്ങൾക്ക് മറ്റെന്താണ് ചെയ്യാൻ കഴിയുക

  • അനുകരണം വേഗത്തിലാക്കുക. JIT മോഡിൽ പോലും ഇത് വെർച്വൽ x86-നേക്കാൾ സാവധാനത്തിൽ പ്രവർത്തിക്കുന്നതായി തോന്നുന്നു (എന്നാൽ ധാരാളം എമുലേറ്റഡ് ഹാർഡ്‌വെയറുകളും ആർക്കിടെക്ചറുകളും ഉള്ള ഒരു മുഴുവൻ ക്യൂമു ഉണ്ട്)
  • ഒരു സാധാരണ ഇന്റർഫേസ് ഉണ്ടാക്കാൻ - സത്യം പറഞ്ഞാൽ, ഞാൻ ഒരു നല്ല വെബ് ഡെവലപ്പർ അല്ല, അതിനാൽ ഇപ്പോൾ ഞാൻ സ്റ്റാൻഡേർഡ് എംസ്ക്രിപ്റ്റൻ ഷെൽ എനിക്ക് കഴിയുന്നത്ര റീമേക്ക് ചെയ്തിട്ടുണ്ട്.
  • കൂടുതൽ സങ്കീർണ്ണമായ Qemu ഫംഗ്‌ഷനുകൾ സമാരംഭിക്കാൻ ശ്രമിക്കുക - നെറ്റ്‌വർക്കിംഗ്, VM മൈഗ്രേഷൻ മുതലായവ.
  • യുപിഎസ്: Qemu-ന്റെയും മറ്റ് പ്രോജക്റ്റുകളുടെയും മുൻ പോർട്ടർമാർ ചെയ്‌തതുപോലെ, നിങ്ങളുടെ കുറച്ച് സംഭവവികാസങ്ങളും ബഗ് റിപ്പോർട്ടുകളും എംസ്‌ക്രിപ്റ്റൻ അപ്‌സ്ട്രീമിലേക്ക് സമർപ്പിക്കേണ്ടതുണ്ട്. എന്റെ ചുമതലയുടെ ഭാഗമായി എംസ്‌ക്രിപ്റ്റനിലേക്കുള്ള അവരുടെ സംഭാവന പരോക്ഷമായി ഉപയോഗിക്കാൻ കഴിഞ്ഞതിന് അവർക്ക് നന്ദി.

അവലംബം: www.habr.com

ഒരു അഭിപ്രായം ചേർക്കുക