JIT ஆதரவுடன் Qemu.js: நீங்கள் இன்னும் பின்னோக்கி துருவலாம்

சில ஆண்டுகளுக்கு முன்பு ஃபேப்ரீஸ் பெல்லார்ட் jslinux ஆல் எழுதப்பட்டது ஜாவாஸ்கிரிப்ட்டில் எழுதப்பட்ட பிசி எமுலேட்டர். அதன் பிறகு குறைந்தது இன்னும் இருந்தது மெய்நிகர் x86. ஆனால் அவர்கள் அனைவரும், எனக்குத் தெரிந்தவரை, மொழிபெயர்ப்பாளர்களாக இருந்தனர், அதே நேரத்தில், அதே ஃபேப்ரைஸ் பெல்லார்டால் எழுதப்பட்ட Qemu, மற்றும், அநேகமாக, எந்தவொரு சுயமரியாதையுள்ள நவீன எமுலேட்டரும், விருந்தினர் குறியீட்டின் JIT தொகுப்பை ஹோஸ்ட் சிஸ்டம் குறியீட்டில் பயன்படுத்துகின்றனர். உலாவிகள் தீர்க்கும் பணியுடன் தொடர்புடைய எதிர் பணியைச் செயல்படுத்த வேண்டிய நேரம் இது என்று எனக்குத் தோன்றியது: இயந்திரக் குறியீட்டை ஜாவாஸ்கிரிப்டில் JIT தொகுத்தல், இது போர்ட் Qemu க்கு மிகவும் தர்க்கரீதியானதாகத் தோன்றியது. ஏன் Qemu, எளிமையான மற்றும் பயனர் நட்பு எமுலேட்டர்கள் உள்ளன என்று தோன்றுகிறது - அதே VirtualBox, எடுத்துக்காட்டாக - நிறுவப்பட்டு வேலை செய்கிறது. ஆனால் கேமு பல சுவாரஸ்யமான அம்சங்களைக் கொண்டுள்ளது

  • திறந்த மூல
  • கர்னல் இயக்கி இல்லாமல் வேலை செய்யும் திறன்
  • மொழிபெயர்ப்பாளர் பயன்முறையில் வேலை செய்யும் திறன்
  • அதிக எண்ணிக்கையிலான ஹோஸ்ட் மற்றும் கெஸ்ட் ஆர்கிடெக்சர்களுக்கான ஆதரவு

மூன்றாவது புள்ளியைப் பொறுத்தவரை, உண்மையில், டிசிஐ பயன்முறையில், விருந்தினர் இயந்திர அறிவுறுத்தல்கள் அல்ல, ஆனால் அவற்றிலிருந்து பெறப்பட்ட பைட்கோட் விளக்கப்படுகிறது, ஆனால் இது சாரத்தை மாற்றாது - உருவாக்க மற்றும் இயக்குவதற்காக. புதிய கட்டிடக்கலையில் Qemu, நீங்கள் அதிர்ஷ்டசாலி என்றால், A C கம்பைலர் போதும் - குறியீடு ஜெனரேட்டரை எழுதுவது தள்ளிப்போகலாம்.

இப்போது, ​​​​என் ஓய்வு நேரத்தில் இரண்டு வருடங்கள் Qemu மூலக் குறியீட்டுடன் நிதானமாக டிங்கரிங் செய்த பிறகு, ஒரு வேலை செய்யும் முன்மாதிரி தோன்றியது, அதில் நீங்கள் ஏற்கனவே இயக்கலாம், எடுத்துக்காட்டாக, Kolibri OS.

எம்ஸ்கிரிப்டன் என்றால் என்ன

இப்போதெல்லாம், பல கம்பைலர்கள் தோன்றியுள்ளன, அதன் இறுதி முடிவு ஜாவாஸ்கிரிப்ட் ஆகும். சில, டைப் ஸ்கிரிப்ட் போன்றவை, முதலில் வலையில் எழுதுவதற்கான சிறந்த வழியாகும். அதே நேரத்தில், எம்ஸ்கிரிப்டன் என்பது ஏற்கனவே உள்ள C அல்லது C++ குறியீட்டை எடுத்து உலாவியில் படிக்கக்கூடிய வடிவத்தில் தொகுக்க ஒரு வழியாகும். அன்று இந்த பக்கம் நன்கு அறியப்பட்ட நிரல்களின் பல துறைமுகங்களை நாங்கள் சேகரித்துள்ளோம்: இங்கேஎடுத்துக்காட்டாக, நீங்கள் PyPy ஐப் பார்க்கலாம் - ஏற்கனவே JIT இருப்பதாக அவர்கள் கூறுகின்றனர். உண்மையில், ஒவ்வொரு நிரலையும் வெறுமனே தொகுத்து உலாவியில் இயக்க முடியாது - ஒரு எண் உள்ளது அம்சங்கள், நீங்கள் பொறுத்துக்கொள்ள வேண்டும், இருப்பினும், அதே பக்கத்தில் உள்ள கல்வெட்டு கூறுகிறது “எம்ஸ்கிரிப்டன் கிட்டத்தட்ட எதையும் தொகுக்கப் பயன்படுத்தலாம் சிறிய ஜாவாஸ்கிரிப்ட்க்கு C/C++ குறியீடு". அதாவது, தரநிலையின்படி வரையறுக்கப்படாத செயல்பாடுகள் பல உள்ளன, ஆனால் பொதுவாக x86 இல் வேலை செய்கின்றன - எடுத்துக்காட்டாக, மாறிகளுக்கான சீரமைக்கப்படாத அணுகல், பொதுவாக சில கட்டமைப்புகளில் இது தடைசெய்யப்பட்டுள்ளது. பொதுவாக , Qemu ஒரு கிராஸ்-பிளாட்ஃபார்ம் புரோகிராம் மற்றும் , நான் நம்ப விரும்பினேன், அது ஏற்கனவே நிறைய வரையறுக்கப்படாத நடத்தைகளைக் கொண்டிருக்கவில்லை - அதை எடுத்து தொகுக்கவும், பின்னர் JIT உடன் சிறிது டிங்கர் செய்யவும் - நீங்கள் முடித்துவிட்டீர்கள்! ஆனால் அது இல்லை வழக்கு...

முதல் முயற்சி

பொதுவாக, ஜாவாஸ்கிரிப்ட்டுக்கு Qemu ஐ போர்ட் செய்யும் யோசனையுடன் வந்த முதல் நபர் நான் அல்ல. எம்ஸ்கிரிப்டனைப் பயன்படுத்தி இது சாத்தியமா என்று ReactOS மன்றத்தில் ஒரு கேள்வி கேட்கப்பட்டது. முன்னதாக, ஃபேப்ரைஸ் பெல்லார்ட் இதை தனிப்பட்ட முறையில் செய்ததாக வதந்திகள் வந்தன, ஆனால் நாங்கள் jslinux பற்றி பேசுகிறோம், இது எனக்குத் தெரிந்தவரை, JS இல் கைமுறையாக போதுமான செயல்திறனை அடைவதற்கான முயற்சியாகும், மேலும் இது புதிதாக எழுதப்பட்டது. பின்னர், மெய்நிகர் x86 எழுதப்பட்டது - தெளிவற்ற ஆதாரங்கள் அதற்கு வெளியிடப்பட்டன, மேலும் கூறியது போல், முன்மாதிரியின் அதிக "யதார்த்தம்" SeaBIOS ஐ ஃபார்ம்வேராகப் பயன்படுத்துவதை சாத்தியமாக்கியது. கூடுதலாக, எம்ஸ்கிரிப்டனைப் பயன்படுத்தி Qemu போர்ட் செய்ய குறைந்தபட்சம் ஒரு முயற்சி இருந்தது - நான் இதைச் செய்ய முயற்சித்தேன் சாக்கெட் ஜோடி, ஆனால் வளர்ச்சி, நான் புரிந்து கொண்ட வரையில், உறைந்திருந்தது.

எனவே, இங்கே ஆதாரங்கள் உள்ளன, இங்கே எம்ஸ்கிரிப்டன் உள்ளது - அதை எடுத்து தொகுக்கவும். ஆனால் கேமு சார்ந்திருக்கும் நூலகங்களும், அந்த நூலகங்கள் சார்ந்திருக்கும் நூலகங்களும் உள்ளன, அவற்றில் ஒன்று libffi, எந்த glib சார்ந்துள்ளது. எம்ஸ்கிரிப்டனுக்கான நூலகங்களின் துறைமுகங்களின் பெரிய தொகுப்பில் ஒன்று இருப்பதாக இணையத்தில் வதந்திகள் வந்தன, ஆனால் நம்புவது எப்படியோ கடினமாக இருந்தது: முதலாவதாக, இது ஒரு புதிய தொகுப்பாளராக இருக்க வேண்டும் என்று கருதப்படவில்லை, இரண்டாவதாக, இது மிகவும் குறைந்த மட்டத்தில் இருந்தது. நூலகம் எடுக்க, மற்றும் JS தொகுக்க. இது சட்டசபை செருகல்களின் விஷயம் மட்டுமல்ல - ஒருவேளை, நீங்கள் அதைத் திருப்பினால், சில அழைப்பு மரபுகளுக்கு நீங்கள் தேவையான வாதங்களை அடுக்கில் உருவாக்கலாம் மற்றும் அவை இல்லாமல் செயல்பாட்டை அழைக்கலாம். ஆனால் எம்ஸ்கிரிப்டன் ஒரு தந்திரமான விஷயம்: உருவாக்கப்பட்ட குறியீட்டை உலாவி JS இன்ஜின் ஆப்டிமைசருக்கு நன்கு தெரிந்திருக்க, சில தந்திரங்கள் பயன்படுத்தப்படுகின்றன. குறிப்பாக, relooping என்று அழைக்கப்படுவது - பெறப்பட்ட LLVM IR ஐப் பயன்படுத்தி சில சுருக்க மாற்ற வழிமுறைகளுடன் ஒரு குறியீடு ஜெனரேட்டர் நம்பத்தகுந்த ifs, loops போன்றவற்றை மீண்டும் உருவாக்க முயற்சிக்கிறது. சரி, வாதங்கள் எவ்வாறு செயல்பாட்டிற்கு அனுப்பப்படுகின்றன? இயற்கையாகவே, JS செயல்பாடுகளுக்கு வாதங்கள், அதாவது, முடிந்தால், ஸ்டாக் மூலம் அல்ல.

ஆரம்பத்தில் libffi க்கு மாற்றாக JS உடன் எழுதலாம் மற்றும் நிலையான சோதனைகளை இயக்கலாம் என்று ஒரு யோசனை இருந்தது, ஆனால் இறுதியில் எனது தலைப்பு கோப்புகளை எவ்வாறு உருவாக்குவது என்று குழப்பமடைந்தேன், அதனால் அவை ஏற்கனவே உள்ள குறியீட்டில் வேலை செய்யும் - நான் என்ன செய்ய முடியும், அவர்கள் சொல்வது போல், "பணிகள் மிகவும் சிக்கலானதா "நாங்கள் மிகவும் முட்டாள்களா?" நான் லிப்ஃபியை வேறொரு கட்டிடக்கலைக்கு போர்ட் செய்ய வேண்டியிருந்தது, எனவே பேசுவதற்கு - அதிர்ஷ்டவசமாக, எம்ஸ்கிரிப்டனில் இன்லைன் அசெம்பிளிக்கான மேக்ரோக்கள் உள்ளன (ஜாவாஸ்கிரிப்டில், ஆம் - சரி, கட்டிடக்கலை எதுவாக இருந்தாலும், அதனால் அசெம்ப்ளர்), மற்றும் பறக்கும்போது உருவாக்கப்பட்ட குறியீட்டை இயக்கும் திறன். பொதுவாக, பிளாட்ஃபார்ம் சார்ந்த libffi துண்டுகளுடன் சிறிது நேரம் டிங்கரிங் செய்த பிறகு, சில தொகுக்கக்கூடிய குறியீட்டைப் பெற்று, நான் கண்ட முதல் சோதனையிலேயே அதை இயக்கினேன். எனக்கு ஆச்சரியமாக, சோதனை வெற்றிகரமாக இருந்தது. எனது மேதையால் திகைத்தேன் - நகைச்சுவை இல்லை, இது முதல் ஏவலில் இருந்து வேலை செய்தது - நான், இன்னும் என் கண்களை நம்பவில்லை, அதன் விளைவாக வரும் குறியீட்டை மீண்டும் பார்க்க, அடுத்து எங்கு தோண்டுவது என்று மதிப்பீடு செய்யச் சென்றேன். இங்கே நான் இரண்டாவது முறையாக பயமுறுத்தினேன் - எனது செயல்பாடு செய்த ஒரே விஷயம் ffi_call - இது ஒரு வெற்றிகரமான அழைப்பைப் புகாரளித்தது. தானே அழைப்பு இல்லை. எனவே நான் எனது முதல் இழுப்புக் கோரிக்கையை அனுப்பினேன், இது எந்த ஒலிம்பியாட் மாணவருக்கும் தெளிவாகத் தெரிந்த தேர்வில் பிழையை சரிசெய்தது - உண்மையான எண்களை இவ்வாறு ஒப்பிடக்கூடாது a == b மற்றும் எப்படி a - b < EPS - நீங்கள் தொகுதியை நினைவில் கொள்ள வேண்டும், இல்லையெனில் 0 என்பது 1/3 க்கு மிகவும் சமமாக மாறும்... பொதுவாக, நான் ஒரு குறிப்பிட்ட லிப்ஃபி போர்ட்டைக் கொண்டு வந்தேன், இது எளிமையான சோதனைகளில் தேர்ச்சி பெறுகிறது, மேலும் எந்த கிளிப் உள்ளது. தொகுக்கப்பட்டது - இது அவசியம் என்று நான் முடிவு செய்தேன், பின்னர் சேர்க்கிறேன். முன்னோக்கிப் பார்க்கும்போது, ​​​​அது மாறியது போல், கம்பைலர் இறுதி குறியீட்டில் libffi செயல்பாட்டைக் கூட சேர்க்கவில்லை என்று கூறுவேன்.

ஆனால், நான் ஏற்கனவே கூறியது போல், சில வரம்புகள் உள்ளன, மேலும் பல்வேறு வரையறுக்கப்படாத நடத்தைகளின் இலவச பயன்பாட்டில், மிகவும் விரும்பத்தகாத அம்சம் மறைக்கப்பட்டுள்ளது - வடிவமைப்பின் மூலம் ஜாவாஸ்கிரிப்ட் பகிரப்பட்ட நினைவகத்துடன் மல்டித்ரெடிங்கை ஆதரிக்காது. கொள்கையளவில், இது பொதுவாக ஒரு நல்ல யோசனை என்று கூட அழைக்கப்படலாம், ஆனால் சி த்ரெட்களுடன் இணைக்கப்பட்ட கட்டமைப்பைக் கொண்ட போர்டிங் குறியீட்டிற்கு அல்ல. பொதுவாக, பயர்பாக்ஸ் பகிரப்பட்ட தொழிலாளர்களை ஆதரிப்பதில் பரிசோதித்து வருகிறது, மேலும் எம்ஸ்கிரிப்டன் அவர்களுக்கு pthread செயல்படுத்தலைக் கொண்டுள்ளது, ஆனால் நான் அதைச் சார்ந்திருக்க விரும்பவில்லை. நான் Qemu குறியீட்டிலிருந்து மல்டித்ரெடிங்கை மெதுவாக அகற்ற வேண்டியிருந்தது - அதாவது, நூல்கள் எங்கு இயங்குகின்றன என்பதைக் கண்டுபிடித்து, இந்த நூலில் இயங்கும் லூப்பின் உடலை ஒரு தனி செயல்பாட்டிற்கு நகர்த்தவும், மேலும் அத்தகைய செயல்பாடுகளை மெயின் லூப்பில் இருந்து ஒவ்வொன்றாக அழைக்கவும்.

இரண்டாவது முயற்சி

ஒரு கட்டத்தில், சிக்கல் இன்னும் உள்ளது என்பதும், குறியீட்டைச் சுற்றி ஊன்றுகோல்களை இடையூறாக நகர்த்துவது எந்த நன்மைக்கும் வழிவகுக்காது என்பது தெளிவாகியது. முடிவு: ஊன்றுகோல் சேர்க்கும் செயல்முறையை எப்படியாவது முறைப்படுத்த வேண்டும். எனவே, அந்த நேரத்தில் புதியதாக இருந்த பதிப்பு 2.4.1 எடுக்கப்பட்டது (2.5.0 அல்ல, ஏனென்றால், புதிய பதிப்பில் இன்னும் பிடிபடாத பிழைகள் இருக்கும், மேலும் எனது சொந்த பிழைகள் போதுமானவை. ), மற்றும் நான் செய்த முதல் விஷயம் அதை பாதுகாப்பாக மீண்டும் எழுதுவதுதான் thread-posix.c. சரி, அதாவது, பாதுகாப்பானது: தடுப்பதற்கு வழிவகுக்கும் ஒரு செயல்பாட்டை யாராவது செய்ய முயன்றால், செயல்பாடு உடனடியாக அழைக்கப்பட்டது abort() - நிச்சயமாக, இது எல்லா சிக்கல்களையும் ஒரே நேரத்தில் தீர்க்கவில்லை, ஆனால் சீரற்ற தரவை அமைதியாகப் பெறுவதை விட குறைந்தபட்சம் இது எப்படியோ மிகவும் இனிமையானது.

பொதுவாக, எம்ஸ்கிரிப்டன் விருப்பங்கள் JS க்கு குறியீட்டை போர்ட் செய்வதற்கு மிகவும் உதவியாக இருக்கும் -s ASSERTIONS=1 -s SAFE_HEAP=1 - சீரமைக்கப்படாத முகவரிக்கான அழைப்புகள் போன்ற சில வகையான வரையறுக்கப்படாத நடத்தைகளை அவர்கள் பிடிக்கிறார்கள் (இது போன்ற தட்டச்சு செய்யப்பட்ட வரிசைகளுக்கான குறியீட்டுடன் இது முற்றிலும் ஒத்துப்போவதில்லை. HEAP32[addr >> 2] = 1) அல்லது தவறான எண்ணிக்கையிலான வாதங்களைக் கொண்ட செயல்பாட்டை அழைப்பது.

மூலம், சீரமைப்பு பிழைகள் ஒரு தனி பிரச்சினை. நான் ஏற்கனவே கூறியது போல், குறியீடு உருவாக்கம் TCI (சிறிய குறியீடு மொழிபெயர்ப்பான்) க்கு Qemu ஒரு "சீரழிந்த" விளக்கமளிக்கும் பின்தளத்தைக் கொண்டுள்ளது, மேலும் Qemu ஐ புதிய கட்டமைப்பில் உருவாக்க மற்றும் இயக்க, நீங்கள் அதிர்ஷ்டசாலி என்றால், C கம்பைலர் போதுமானது. முக்கிய வார்த்தைகள் "நீங்கள் அதிர்ஷ்டசாலி என்றால்". நான் துரதிர்ஷ்டவசமாக இருந்தேன், மேலும் TCI அதன் பைட்கோடை பாகுபடுத்தும் போது சீரமைக்கப்படாத அணுகலைப் பயன்படுத்துகிறது. அதாவது, அனைத்து வகையான ARM மற்றும் அவசியமான அணுகலுடன் கூடிய பிற கட்டமைப்புகளிலும், Qemu தொகுக்கிறது, ஏனெனில் அவை நேட்டிவ் குறியீட்டை உருவாக்கும் சாதாரண TCG பின்தளத்தில் உள்ளது, ஆனால் TCI அவற்றில் வேலை செய்யுமா என்பது மற்றொரு கேள்வி. இருப்பினும், அது மாறியது போல், டிசிஐ ஆவணங்கள் இதேபோன்ற ஒன்றை தெளிவாகக் குறிப்பிடுகின்றன. இதன் விளைவாக, சீரமைக்கப்படாத வாசிப்புக்கான செயல்பாட்டு அழைப்புகள் குறியீட்டில் சேர்க்கப்பட்டன, அவை Qemu இன் மற்றொரு பகுதியில் கண்டுபிடிக்கப்பட்டன.

குவியல் அழிவு

இதன் விளைவாக, TCIக்கான சீரமைக்கப்படாத அணுகல் சரி செய்யப்பட்டது, ஒரு முக்கிய வளையம் உருவாக்கப்பட்டது, அதையொட்டி செயலி, RCU மற்றும் வேறு சில சிறிய விஷயங்கள். எனவே நான் விருப்பத்துடன் Qemu ஐ தொடங்குகிறேன் -d exec,in_asm,out_asm, அதாவது எந்த குறியீடு தொகுதிகள் செயல்படுத்தப்படுகின்றன என்பதை நீங்கள் சொல்ல வேண்டும், மேலும் ஒளிபரப்பு நேரத்தில் விருந்தினர் குறியீடு என்ன, ஹோஸ்ட் குறியீடு என்ன ஆனது (இந்த விஷயத்தில், பைட்கோட்) எழுத வேண்டும். இது தொடங்குகிறது, பல மொழிபெயர்ப்புத் தொகுதிகளை இயக்குகிறது, RCU இப்போது தொடங்கும் என்று நான் விட்ட பிழைத்திருத்த செய்தியை எழுதுகிறது மற்றும்... செயலிழக்கிறது abort() ஒரு செயல்பாட்டின் உள்ளே free(). செயல்பாட்டுடன் டிங்கரிங் செய்வதன் மூலம் free() ஹீப் பிளாக்கின் ஹெடரில், ஒதுக்கப்பட்ட நினைவகத்திற்கு முந்தைய எட்டு பைட்டுகளில், தொகுதி அளவு அல்லது அதுபோன்ற ஏதாவது ஒன்றுக்கு பதிலாக, குப்பை இருப்பதைக் கண்டுபிடிக்க முடிந்தது.

குவியல் அழிவு - எவ்வளவு அழகாக இருக்கிறது... அப்படியானால், ஒரு பயனுள்ள தீர்வு உள்ளது - (முடிந்தால்) அதே ஆதாரங்களில் இருந்து, ஒரு சொந்த பைனரியை அசெம்பிள் செய்து வால்கிரைண்டின் கீழ் இயக்கவும். சிறிது நேரம் கழித்து, பைனரி தயாராக இருந்தது. நான் அதை அதே விருப்பங்களுடன் தொடங்குகிறேன் - துவக்கத்தின் போது கூட, உண்மையில் செயல்படுத்தலை அடைவதற்கு முன்பு அது செயலிழக்கிறது. இது விரும்பத்தகாதது, நிச்சயமாக - வெளிப்படையாக, ஆதாரங்கள் ஒரே மாதிரியாக இல்லை, இது ஆச்சரியமல்ல, ஏனெனில் உள்ளமைவு சற்று வித்தியாசமான விருப்பங்களைத் தேடுகிறது, ஆனால் என்னிடம் Valgrind உள்ளது - முதலில் நான் இந்த பிழையை சரிசெய்வேன், பின்னர், நான் அதிர்ஷ்டசாலி என்றால் , அசல் ஒன்று தோன்றும். நான் Valgrind இன் கீழ் அதையே இயக்குகிறேன்... Y-y-y, y-y-y, uh-uh, இது தொடங்கியது, சாதாரணமாக துவக்கப்பட்டது மற்றும் தவறான நினைவக அணுகல் பற்றிய ஒரு எச்சரிக்கையும் இல்லாமல் அசல் பிழையைக் கடந்தது, வீழ்ச்சியைப் பற்றி குறிப்பிட தேவையில்லை. வாழ்க்கை, அவர்கள் சொல்வது போல், இதற்கு என்னைத் தயார்படுத்தவில்லை - வால்கிரைண்டின் கீழ் தொடங்கப்படும்போது செயலிழக்கும் நிரல் செயலிழப்பதை நிறுத்துகிறது. அது என்ன என்பது மர்மமாகவே உள்ளது. எனது கருதுகோள் என்னவென்றால், துவக்கத்தின் போது ஒரு செயலிழந்த பிறகு தற்போதைய அறிவுறுத்தலின் அருகே ஒருமுறை, gdb வேலை காட்டியது memset-a ஒரு சரியான சுட்டியை பயன்படுத்தி mmx, அல்லது xmm பதிவு செய்கிறது, ஒருவேளை இது ஒருவித சீரமைப்பு பிழையாக இருக்கலாம், இருப்பினும் நம்புவதற்கு இன்னும் கடினமாக உள்ளது.

சரி, Valgrind இங்கு உதவுவதாக தெரியவில்லை. இங்கே மிகவும் அருவருப்பான விஷயம் தொடங்கியது - எல்லாம் தொடங்குவதாகத் தெரிகிறது, ஆனால் மில்லியன் கணக்கான அறிவுறுத்தல்களுக்கு முன்பு நடந்த ஒரு நிகழ்வின் காரணமாக முற்றிலும் அறியப்படாத காரணங்களுக்காக செயலிழக்கிறது. எப்படி அணுகுவது என்று கூட நீண்ட நாட்களாகத் தெரியவில்லை. இறுதியில், நான் இன்னும் உட்கார்ந்து பிழைத்திருத்தம் செய்ய வேண்டியிருந்தது. தலைப்பு மீண்டும் எழுதப்பட்டதை அச்சிடுவது அது ஒரு எண்ணைப் போல் இல்லை, மாறாக சில வகையான பைனரி தரவுகளைக் காட்டுகிறது. மேலும், இதோ, இந்த பைனரி சரம் பயாஸ் கோப்பில் காணப்பட்டது - அதாவது, இப்போது இது ஒரு இடையக வழிதல் என்று நியாயமான நம்பிக்கையுடன் சொல்ல முடிந்தது, மேலும் இது இந்த இடையகத்திற்கு எழுதப்பட்டது என்பது கூட தெளிவாகத் தெரிகிறது. சரி, அப்படியானால், இது போன்ற ஒன்று - எம்ஸ்கிரிப்டனில், அதிர்ஷ்டவசமாக, முகவரி இடத்தின் சீரற்றமயமாக்கல் இல்லை, அதில் ஓட்டைகள் எதுவும் இல்லை, எனவே கடைசி வெளியீட்டிலிருந்து சுட்டி மூலம் தரவை வெளியிட குறியீட்டின் நடுவில் எங்காவது எழுதலாம், தரவைப் பார்க்கவும், சுட்டியைப் பார்க்கவும், அது மாறவில்லை என்றால், சிந்தனைக்கு உணவைப் பெறவும். உண்மை, ஏதேனும் மாற்றத்திற்குப் பிறகு இணைக்க இரண்டு நிமிடங்கள் ஆகும், ஆனால் நீங்கள் என்ன செய்யலாம்? இதன் விளைவாக, தற்காலிக இடையகத்திலிருந்து விருந்தினர் நினைவகத்திற்கு BIOS ஐ நகலெடுக்கும் ஒரு குறிப்பிட்ட வரி கண்டறியப்பட்டது - மேலும், உண்மையில், தாங்கலில் போதுமான இடம் இல்லை. அந்த விசித்திரமான இடையக முகவரியின் மூலத்தைக் கண்டறிவது ஒரு செயல்பாட்டில் விளைந்தது qemu_anon_ram_alloc கோப்பில் oslib-posix.c - தர்க்கம் இதுதான்: சில நேரங்களில் முகவரியை 2 எம்பி அளவிலான பெரிய பக்கத்திற்கு சீரமைப்பது பயனுள்ளதாக இருக்கும், இதற்காக நாங்கள் கேட்போம் mmap முதலில் இன்னும் கொஞ்சம், பிறகு உதவியோடு அதிகப்படியானவற்றைத் திருப்பித் தருவோம் munmap. அத்தகைய சீரமைப்பு தேவையில்லை என்றால், 2 எம்பிக்கு பதிலாக முடிவைக் குறிப்பிடுவோம் getpagesize() - mmap அது இன்னும் சீரமைக்கப்பட்ட முகவரியைக் கொடுக்கும்... எனவே எம்ஸ்கிரிப்டனில் mmap வெறும் அழைப்புகள் malloc, ஆனால் நிச்சயமாக அது பக்கத்தில் சீரமைக்காது. பொதுவாக, இரண்டு மாதங்களுக்கு என்னை விரக்தியடையச் செய்த ஒரு பிழை, மாற்றத்தால் சரி செய்யப்பட்டது двух கோடுகள்.

அழைப்பு செயல்பாடுகளின் அம்சங்கள்

இப்போது செயலி எதையாவது எண்ணுகிறது, கியூமு செயலிழக்கவில்லை, ஆனால் திரை இயக்கப்படவில்லை, மேலும் செயலி விரைவாக சுழல்களுக்குச் சென்று, வெளியீட்டின் அடிப்படையில் தீர்மானிக்கிறது -d exec,in_asm,out_asm. ஒரு கருதுகோள் உருவாகியுள்ளது: டைமர் குறுக்கீடுகள் (அல்லது, பொதுவாக, அனைத்து குறுக்கீடுகளும்) வராது. உண்மையில், சில காரணங்களால் வேலை செய்த நேட்டிவ் அசெம்பிளியிலிருந்து குறுக்கீடுகளை நீங்கள் அவிழ்த்துவிட்டால், இதேபோன்ற படத்தைப் பெறுவீர்கள். ஆனால் இது பதில் இல்லை: மேலே உள்ள விருப்பத்துடன் வழங்கப்பட்ட தடயங்களின் ஒப்பீடு, மரணதண்டனைப் பாதைகள் மிக விரைவாக வேறுபட்டது என்பதைக் காட்டுகிறது. துவக்கியைப் பயன்படுத்தி பதிவு செய்யப்பட்டவற்றின் ஒப்பீடு என்று இங்கே சொல்ல வேண்டும் emrun நேட்டிவ் அசெம்பிளியின் வெளியீட்டைக் கொண்டு பிழைத்திருத்த வெளியீடு முற்றிலும் இயந்திரச் செயல்முறை அல்ல. பிரவுசரில் இயங்கும் புரோகிராம் எப்படி இணைகிறது என்பது சரியாகத் தெரியவில்லை emrun, ஆனால் வெளியீட்டில் சில கோடுகள் மறுசீரமைக்கப்பட்டதாக மாறிவிடும், எனவே வேறுபாட்டின் வேறுபாடு இன்னும் பாதைகள் வேறுபட்டதாகக் கருதுவதற்கு ஒரு காரணம் அல்ல. பொதுவாக, அறிவுறுத்தல்களின்படி அது தெளிவாகியது ljmpl வெவ்வேறு முகவரிகளுக்கு ஒரு மாற்றம் உள்ளது, மேலும் உருவாக்கப்பட்ட பைட்கோட் அடிப்படையில் வேறுபட்டது: ஒன்றில் உதவியாளர் செயல்பாட்டை அழைப்பதற்கான அறிவுறுத்தல் உள்ளது, மற்றொன்று இல்லை. வழிமுறைகளை கூகுள் செய்து, இந்த வழிமுறைகளை மொழிபெயர்க்கும் குறியீட்டைப் படித்த பிறகு, முதலில், பதிவேட்டில் உடனடியாக முன், தெளிவாகத் தெரிந்தது. cr0 ஒரு உதவியாளரைப் பயன்படுத்தி ஒரு பதிவு செய்யப்பட்டது - இது செயலியை பாதுகாக்கப்பட்ட பயன்முறைக்கு மாற்றியது, இரண்டாவதாக, js பதிப்பு ஒருபோதும் பாதுகாக்கப்பட்ட பயன்முறைக்கு மாறவில்லை. ஆனால் உண்மை என்னவென்றால், எம்ஸ்கிரிப்டனின் மற்றொரு அம்சம், அறிவுறுத்தல்களை செயல்படுத்துவது போன்ற குறியீட்டை பொறுத்துக்கொள்ள தயக்கம் காட்டுவதாகும். call TCI இல், எந்தச் செயல்பாடு சுட்டிக்காட்டி வகையிலும் விளைகிறது long long f(int arg0, .. int arg9) - செயல்பாடுகள் சரியான எண்ணிக்கையிலான வாதங்களுடன் அழைக்கப்பட வேண்டும். இந்த விதி மீறப்பட்டால், பிழைத்திருத்த அமைப்புகளைப் பொறுத்து, நிரல் செயலிழக்கும் (இது நல்லது) அல்லது தவறான செயல்பாட்டை அழைக்கும் (இது பிழைத்திருத்தத்திற்கு வருத்தமாக இருக்கும்). மூன்றாவது விருப்பமும் உள்ளது - வாதங்களைச் சேர்க்கும் / அகற்றும் ரேப்பர்களின் தலைமுறையை இயக்கவும், ஆனால் மொத்தத்தில் இந்த ரேப்பர்கள் நிறைய இடத்தை எடுத்துக்கொள்கின்றன, உண்மையில் எனக்கு நூற்றுக்கும் மேற்பட்ட ரேப்பர்கள் மட்டுமே தேவை என்ற போதிலும். இது மிகவும் சோகமானது, ஆனால் மிகவும் தீவிரமான பிரச்சனையாக மாறியது: ரேப்பர் செயல்பாடுகளின் உருவாக்கப்பட்ட குறியீட்டில், வாதங்கள் மாற்றப்பட்டு மாற்றப்பட்டன, ஆனால் சில நேரங்களில் உருவாக்கப்பட்ட வாதங்களுடன் கூடிய செயல்பாடு அழைக்கப்படவில்லை - சரி, உள்ளதைப் போலவே எனது 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 குறியீடு கரோட்டின்களில் எழுதப்பட்டுள்ளது என்பதை இங்கே தெளிவுபடுத்துவது அவசியம். எம்ஸ்கிரிப்டன் அதன் சொந்த மிகவும் தந்திரமான செயலாக்கத்தைக் கொண்டுள்ளது, ஆனால் அது இன்னும் Qemu குறியீட்டில் ஆதரிக்கப்பட வேண்டும், மேலும் நீங்கள் இப்போது செயலியை பிழைத்திருத்தலாம்: Qemu விருப்பங்களை ஆதரிக்கிறது -kernel, -initrd, -append, நீங்கள் Linux ஐ துவக்கலாம் அல்லது, எடுத்துக்காட்டாக, memtest86+, பிளாக் சாதனங்களைப் பயன்படுத்தாமல். ஆனால் இங்கே சிக்கல் உள்ளது: நேட்டிவ் அசெம்பிளியில் ஒருவர் லினக்ஸ் கர்னல் வெளியீட்டை கன்சோலுக்கு விருப்பத்துடன் பார்க்கலாம் -nographic, மற்றும் உலாவியில் இருந்து டெர்மினலுக்கு அது தொடங்கப்பட்ட இடத்திலிருந்து வெளியீடு இல்லை emrun, வரவில்லை. அதாவது, அது தெளிவாக இல்லை: செயலி வேலை செய்யவில்லை அல்லது கிராபிக்ஸ் வெளியீடு வேலை செய்யவில்லை. பின்னர் கொஞ்சம் காத்திருக்க வேண்டும் என்று தோன்றியது. "செயலி தூங்கவில்லை, ஆனால் மெதுவாக சிமிட்டுகிறது" என்று மாறியது, மேலும் ஐந்து நிமிடங்களுக்குப் பிறகு கர்னல் ஒரு கொத்து செய்திகளை கன்சோலில் எறிந்து தொடர்ந்து செயலிழக்கச் செய்தது. செயலி, பொதுவாக, வேலை செய்கிறது என்பது தெளிவாகியது, மேலும் SDL2 உடன் வேலை செய்வதற்கான குறியீட்டை நாம் தோண்டி எடுக்க வேண்டும். துரதிர்ஷ்டவசமாக, இந்த நூலகத்தை எவ்வாறு பயன்படுத்துவது என்று எனக்குத் தெரியவில்லை, எனவே சில இடங்களில் நான் சீரற்ற முறையில் செயல்பட வேண்டியிருந்தது. சில சமயங்களில், நீல நிற பின்னணியில் திரையில் இணையான கோடு ஒளிர்ந்தது, இது சில எண்ணங்களை பரிந்துரைத்தது. இறுதியில், பிரச்சனை என்னவென்றால், Qemu பல மெய்நிகர் சாளரங்களை ஒரு இயற்பியல் சாளரத்தில் திறக்கிறது, அவற்றுக்கு இடையே நீங்கள் Ctrl-Alt-n ஐப் பயன்படுத்தி மாறலாம்: இது சொந்த கட்டமைப்பில் வேலை செய்கிறது, ஆனால் எம்ஸ்கிரிப்டனில் இல்லை. விருப்பங்களைப் பயன்படுத்தி தேவையற்ற சாளரங்களை அகற்றிய பிறகு -monitor none -parallel none -serial none ஒவ்வொரு சட்டகத்திலும் முழுத் திரையையும் வலுக்கட்டாயமாக மீண்டும் வரைவதற்கான வழிமுறைகள், அனைத்தும் திடீரென்று வேலை செய்தன.

கரோட்டின்கள்

எனவே, உலாவியில் எமுலேஷன் வேலை செய்கிறது, ஆனால் அதில் சுவாரஸ்யமான ஒற்றை நெகிழ்வை நீங்கள் இயக்க முடியாது, ஏனெனில் I/O தொகுதி இல்லை - நீங்கள் கரோட்டின்களுக்கான ஆதரவை செயல்படுத்த வேண்டும். Qemu ஏற்கனவே பல கரோட்டின் பின்தளங்களைக் கொண்டுள்ளது, ஆனால் ஜாவாஸ்கிரிப்ட் மற்றும் எம்ஸ்கிரிப்டன் குறியீடு ஜெனரேட்டரின் தன்மை காரணமாக, நீங்கள் ஸ்டேக்குகளை ஏமாற்றத் தொடங்க முடியாது. "எல்லாம் போய்விட்டது, பிளாஸ்டர் அகற்றப்படுகிறது" என்று தோன்றுகிறது, ஆனால் எம்ஸ்கிரிப்டன் டெவலப்பர்கள் ஏற்கனவே எல்லாவற்றையும் கவனித்துக்கொண்டனர். இது மிகவும் வேடிக்கையாக செயல்படுத்தப்படுகிறது: இது போன்ற ஒரு செயல்பாட்டு அழைப்பை சந்தேகத்திற்குரியதாக அழைப்போம் emscripten_sleep மற்றும் பலர் Asyncify பொறிமுறையைப் பயன்படுத்துகின்றனர், அத்துடன் முந்தைய இரண்டு நிகழ்வுகளில் ஒன்று ஸ்டேக்கிற்கு கீழே நிகழக்கூடிய எந்தவொரு செயல்பாட்டிற்கும் சுட்டிக்காட்டி அழைப்புகள் மற்றும் அழைப்புகள். இப்போது, ​​​​ஒவ்வொரு சந்தேகத்திற்கிடமான அழைப்பிற்கும் முன், நாங்கள் ஒரு ஒத்திசைவு சூழலைத் தேர்ந்தெடுப்போம், அழைப்புக்குப் பிறகு, ஒரு ஒத்திசைவற்ற அழைப்பு ஏற்பட்டதா என்பதைச் சரிபார்ப்போம், அது இருந்தால், இந்த ஒத்திசைவு சூழலில் அனைத்து உள்ளூர் மாறிகளையும் சேமிப்போம், எந்த செயல்பாட்டைக் குறிப்பிடுகிறோம் எப்பொழுது எப்பொழுது எக்சிகியூஷனைத் தொடர வேண்டும் என்பதற்கு கட்டுப்பாட்டை மாற்றவும், தற்போதைய செயல்பாட்டிலிருந்து வெளியேறவும். இங்குதான் விளைவைப் படிப்பதற்கான வாய்ப்பு உள்ளது வீணாக்குதல் - ஒத்திசைவற்ற அழைப்பிலிருந்து திரும்பிய பிறகு, குறியீட்டை செயல்படுத்துவதைத் தொடர, கம்பைலர் ஒரு சந்தேகத்திற்கிடமான அழைப்பிற்குப் பிறகு தொடங்கும் செயல்பாட்டின் "ஸ்டப்களை" உருவாக்குகிறது - இது போன்றது: n சந்தேகத்திற்கிடமான அழைப்புகள் இருந்தால், செயல்பாடு எங்காவது n/2 விரிவாக்கப்படும். முறை - இது இன்னும் இல்லை என்றால், ஒவ்வொரு ஒத்திசைவற்ற அழைப்பிற்கும் பிறகு, அசல் செயல்பாட்டில் சில உள்ளூர் மாறிகளைச் சேமிப்பதை நீங்கள் சேர்க்க வேண்டும் என்பதை நினைவில் கொள்ளுங்கள். பின்னர், நான் பைத்தானில் ஒரு எளிய ஸ்கிரிப்டை எழுத வேண்டியிருந்தது, இது "ஒத்திசைவின்மை தங்களைத் தாங்களே கடந்து செல்ல அனுமதிக்காது" (அதாவது, ஸ்டேக் ப்ரோமோஷன் மற்றும் நான் இப்போது விவரித்த அனைத்தும் இல்லை" என்று கூறப்படும் குறிப்பாக அதிகமாகப் பயன்படுத்தப்பட்ட செயல்பாடுகளின் தொகுப்பை அடிப்படையாகக் கொண்டது. அவற்றில் வேலை), சுட்டிகள் மூலம் அழைப்புகளைக் குறிக்கிறது, இதில் செயல்பாடுகள் கம்பைலரால் புறக்கணிக்கப்பட வேண்டும், இதனால் இந்த செயல்பாடுகள் ஒத்திசைவற்றதாக கருதப்படாது. பின்னர் 60 எம்பிக்குக் கீழ் உள்ள JS கோப்புகள் தெளிவாக அதிகமாக உள்ளன - குறைந்தபட்சம் 30 என்று வைத்துக்கொள்வோம். ஒருமுறை நான் ஒரு அசெம்பிளி ஸ்கிரிப்டை அமைக்கும்போது, ​​தற்செயலாக இணைப்பான் விருப்பங்களைத் தூக்கி எறிந்தேன். -O3. நான் உருவாக்கிய குறியீட்டை இயக்குகிறேன், மேலும் Chromium நினைவகத்தை அழிக்கிறது மற்றும் செயலிழக்கிறது. நான் தற்செயலாக அவர் தரவிறக்கம் செய்ய முயற்சிப்பதைப் பார்த்தேன்... சரி, நான் என்ன சொல்வது, 500+ MB ஜாவாஸ்கிரிப்டை நன்றாகப் படித்து மேம்படுத்தச் சொன்னால் நானும் உறைந்திருப்பேன்.

துரதிர்ஷ்டவசமாக, Asyncify ஆதரவு நூலகக் குறியீட்டில் உள்ள காசோலைகள் முற்றிலும் நட்பாக இல்லை longjmp-கள் மெய்நிகர் செயலி குறியீட்டில் பயன்படுத்தப்படுகின்றன, ஆனால் இந்தச் சரிபார்ப்புகளை செயலிழக்கச் செய்யும் சிறிய இணைப்புக்குப் பிறகு, எல்லாம் நன்றாக இருப்பது போல் சூழல்களை வலுக்கட்டாயமாக மீட்டெடுக்கிறது, குறியீடு வேலை செய்தது. பின்னர் ஒரு விசித்திரமான விஷயம் தொடங்கியது: சில சமயங்களில் ஒத்திசைவு குறியீட்டில் காசோலைகள் தூண்டப்பட்டன - செயல்படுத்தும் தர்க்கத்தின் படி, அது தடுக்கப்பட வேண்டும் என்றால், குறியீட்டை செயலிழக்கச் செய்யும் அதேவை - ஏற்கனவே கைப்பற்றப்பட்ட மியூடெக்ஸை யாரோ கைப்பற்ற முயன்றனர். அதிர்ஷ்டவசமாக, இது வரிசைப்படுத்தப்பட்ட குறியீட்டில் ஒரு தர்க்கரீதியான சிக்கலாக மாறவில்லை - நான் எம்ஸ்கிரிப்டன் வழங்கிய நிலையான பிரதான வளைய செயல்பாட்டைப் பயன்படுத்தினேன், ஆனால் சில சமயங்களில் ஒத்திசைவற்ற அழைப்பு ஸ்டாக்கை முழுவதுமாக அவிழ்த்துவிடும், அந்த நேரத்தில் அது தோல்வியடையும். setTimeout பிரதான சுழற்சியில் இருந்து - இதனால், குறியீடு முந்தைய மறு செய்கையை விட்டு வெளியேறாமல் பிரதான சுழற்சியில் நுழைந்தது. ஒரு எல்லையற்ற சுழற்சியில் மீண்டும் எழுதப்பட்டது மற்றும் emscripten_sleep, மற்றும் மியூடெக்ஸ் பிரச்சனைகள் நிறுத்தப்பட்டன. குறியீடு இன்னும் தர்க்கரீதியானதாகிவிட்டது - எல்லாவற்றிற்கும் மேலாக, அடுத்த அனிமேஷன் சட்டத்தைத் தயாரிக்கும் சில குறியீடு என்னிடம் இல்லை - செயலி எதையாவது கணக்கிடுகிறது மற்றும் திரை அவ்வப்போது புதுப்பிக்கப்படும். இருப்பினும், சிக்கல்கள் அங்கு நிற்கவில்லை: சில நேரங்களில் Qemu செயல்படுத்தல் எந்த விதிவிலக்குகள் அல்லது பிழைகள் இல்லாமல் அமைதியாக முடிவடையும். அந்த நேரத்தில் நான் அதை கைவிட்டேன், ஆனால், முன்னோக்கிப் பார்த்தால், பிரச்சனை இதுதான் என்று நான் கூறுவேன்: கரோட்டின் குறியீடு, உண்மையில், பயன்படுத்தாது setTimeout (அல்லது குறைந்தபட்சம் நீங்கள் நினைப்பது போல் அடிக்கடி இல்லை): செயல்பாடு emscripten_yield ஒத்திசைவற்ற அழைப்புக் கொடியை அமைக்கிறது. முழு விஷயமும் அதுதான் emscripten_coroutine_next ஒரு ஒத்திசைவற்ற செயல்பாடு அல்ல: உள்நாட்டில் அது கொடியை சரிபார்த்து, அதை மீட்டமைத்து, தேவையான இடத்திற்கு கட்டுப்பாட்டை மாற்றுகிறது. அதாவது, அடுக்கின் விளம்பரம் அங்கு முடிவடைகிறது. பிரச்சனை என்னவென்றால், பயன்பாட்டிற்குப் பிறகு-இலவசமானது, தற்போதுள்ள கரோட்டின் பின்தளத்தில் இருந்து முக்கியமான குறியீட்டு வரியை நகலெடுக்காததன் காரணமாக, coroutine பூல் முடக்கப்பட்டபோது தோன்றியது, செயல்பாடு qemu_in_coroutine உண்மையில் அது பொய்யாகத் திரும்பியிருக்க வேண்டிய போது உண்மை திரும்பியது. இது ஒரு அழைப்புக்கு வழிவகுத்தது emscripten_yield, அதற்கு மேல் அடுக்கில் யாரும் இல்லை emscripten_coroutine_next, ஸ்டாக் மிக மேலே விரிந்தது, ஆனால் இல்லை setTimeout, நான் ஏற்கனவே கூறியது போல், காட்சிப்படுத்தப்படவில்லை.

ஜாவாஸ்கிரிப்ட் குறியீடு உருவாக்கம்

இங்கே, உண்மையில், வாக்குறுதியளிக்கப்பட்ட "துண்டு துண்தாக வெட்டப்பட்ட இறைச்சியைத் திருப்புவது". உண்மையில் இல்லை. நிச்சயமாக, நாம் உலாவியில் Qemu ஐ இயக்கினால், அதில் Node.js ஐ இயக்கினால், இயற்கையாகவே, Qemu இல் குறியீடு உருவாக்கத்திற்குப் பிறகு நாம் முற்றிலும் தவறான ஜாவாஸ்கிரிப்டைப் பெறுவோம். ஆனால் இன்னும், ஒருவித தலைகீழ் மாற்றம்.

முதலில், கேமு எவ்வாறு செயல்படுகிறது என்பதைப் பற்றி கொஞ்சம். தயவு செய்து உடனடியாக என்னை மன்னியுங்கள்: நான் ஒரு தொழில்முறை Qemu டெவலப்பர் அல்ல மேலும் எனது முடிவுகள் சில இடங்களில் தவறாக இருக்கலாம். அவர்கள் சொல்வது போல், "மாணவரின் கருத்து ஆசிரியரின் கருத்து, பீனோவின் கோட்பாடு மற்றும் பொது அறிவு ஆகியவற்றுடன் ஒத்துப்போவதில்லை." Qemu ஆனது குறிப்பிட்ட எண்ணிக்கையிலான விருந்தினர் கட்டமைப்புகளை ஆதரிக்கிறது மற்றும் ஒவ்வொன்றிற்கும் ஒரு கோப்பகம் உள்ளது target-i386. உருவாக்கும்போது, ​​பல விருந்தினர் கட்டமைப்புகளுக்கான ஆதரவை நீங்கள் குறிப்பிடலாம், ஆனால் இதன் விளைவாக பல பைனரிகள் மட்டுமே இருக்கும். விருந்தினர் கட்டமைப்பை ஆதரிக்கும் குறியீடு, சில உள் Qemu செயல்பாடுகளை உருவாக்குகிறது, இது TCG (சிறிய குறியீடு ஜெனரேட்டர்) ஏற்கனவே ஹோஸ்ட் கட்டமைப்பிற்கான இயந்திர குறியீடாக மாறும். tcg கோப்பகத்தில் உள்ள readme கோப்பில் கூறப்பட்டுள்ளபடி, இது முதலில் வழக்கமான C கம்பைலரின் ஒரு பகுதியாக இருந்தது, பின்னர் இது JITக்கு மாற்றப்பட்டது. எனவே, எடுத்துக்காட்டாக, இந்த ஆவணத்தின் அடிப்படையில் இலக்கு கட்டமைப்பு இனி விருந்தினர் கட்டிடக்கலை அல்ல, மாறாக ஒரு ஹோஸ்ட் கட்டிடக்கலை. ஒரு கட்டத்தில், மற்றொரு கூறு தோன்றியது - டைனி கோட் மொழிபெயர்ப்பாளர் (டிசிஐ), இது ஒரு குறிப்பிட்ட ஹோஸ்ட் கட்டமைப்பிற்கான குறியீடு ஜெனரேட்டர் இல்லாத நிலையில் குறியீட்டை (கிட்டத்தட்ட அதே உள் செயல்பாடுகள்) இயக்க வேண்டும். உண்மையில், அதன் ஆவணங்கள் கூறுவது போல், இந்த மொழிபெயர்ப்பாளன் எப்போதும் ஒரு JIT குறியீடு ஜெனரேட்டரைப் போலவே செயல்படாமல் இருக்கலாம், வேகத்தின் அடிப்படையில் மட்டுமன்றி, தரத்திலும். அவரது விளக்கம் முற்றிலும் பொருத்தமானது என்று எனக்குத் தெரியவில்லை என்றாலும்.

முதலில் நான் ஒரு முழு அளவிலான TCG பின்தளத்தை உருவாக்க முயற்சித்தேன், ஆனால் மூலக் குறியீட்டில் விரைவாக குழப்பமடைந்தேன் மற்றும் பைட்கோட் வழிமுறைகளின் முழு விளக்கமும் இல்லை, எனவே TCI மொழிபெயர்ப்பாளரை மடக்க முடிவு செய்தேன். இது பல நன்மைகளைக் கொடுத்தது:

  • குறியீடு ஜெனரேட்டரை செயல்படுத்தும் போது, ​​நீங்கள் அறிவுறுத்தல்களின் விளக்கத்தை பார்க்க முடியாது, ஆனால் மொழிபெயர்ப்பாளர் குறியீட்டை பார்க்க முடியும்
  • நீங்கள் எதிர்கொள்ளும் ஒவ்வொரு மொழிபெயர்ப்புத் தொகுதிக்கும் செயல்பாடுகளை உருவாக்க முடியாது, எடுத்துக்காட்டாக, நூறாவது செயல்பாட்டிற்குப் பிறகுதான்
  • உருவாக்கப்பட்ட குறியீடு மாறினால் (இது சாத்தியமாகத் தோன்றினால், பேட்ச் என்ற சொல் உள்ள பெயர்களைக் கொண்ட செயல்பாடுகளின் மூலம் ஆராயலாம்), உருவாக்கப்பட்ட JS குறியீட்டை நான் செல்லாததாக்க வேண்டும், ஆனால் குறைந்தபட்சம் அதை மீண்டும் உருவாக்க ஏதாவது இருக்கும்.

மூன்றாவது புள்ளியைப் பொறுத்தவரை, குறியீட்டை முதன்முறையாக இயக்கிய பிறகு ஒட்டுதல் சாத்தியம் என்று எனக்குத் தெரியவில்லை, ஆனால் முதல் இரண்டு புள்ளிகள் போதுமானது.

ஆரம்பத்தில், அசல் பைட்கோட் அறிவுறுத்தலின் முகவரியில் ஒரு பெரிய சுவிட்ச் வடிவில் குறியீடு உருவாக்கப்பட்டது, ஆனால் பின்னர், எம்ஸ்கிரிப்டன், உருவாக்கப்பட்ட JS ஐ மேம்படுத்துதல் மற்றும் மறுதொடக்கம் பற்றிய கட்டுரையை நினைவில் வைத்து, அதிக மனித குறியீட்டை உருவாக்க முடிவு செய்தேன், குறிப்பாக அனுபவபூர்வமாக அது. மொழிபெயர்ப்புத் தொகுதியின் ஒரே நுழைவுப் புள்ளி அதன் தொடக்கமாகும். சிறிது நேரத்திற்குப் பிறகு, எங்களிடம் ஒரு குறியீடு ஜெனரேட்டர் இருந்தது, அது ifs உடன் குறியீட்டை உருவாக்கியது (லூப்கள் இல்லாமல் இருந்தாலும்). ஆனால் துரதிர்ஷ்டம், அது செயலிழந்தது, அறிவுறுத்தல்கள் சில தவறான நீளம் கொண்டவை என்று ஒரு செய்தியைக் கொடுத்தது. மேலும், இந்த மறுநிகழ்வு மட்டத்தில் கடைசி அறிவுறுத்தலாக இருந்தது brcond. சரி, சுழல்நிலை அழைப்பிற்கு முன்னும் பின்னும் இந்த அறிவுறுத்தலின் தலைமுறைக்கு ஒரே மாதிரியான சரிபார்ப்பைச் சேர்ப்பேன் மற்றும்... அவற்றில் ஒன்று கூட செயல்படுத்தப்படவில்லை, ஆனால் உறுதியான சுவிட்ச் பிறகும் அவை தோல்வியடைந்தன. இறுதியில், உருவாக்கப்பட்ட குறியீட்டைப் படித்த பிறகு, மாற்றத்திற்குப் பிறகு, தற்போதைய அறிவுறுத்தலுக்கான சுட்டிக்காட்டி அடுக்கிலிருந்து மீண்டும் ஏற்றப்பட்டு, உருவாக்கப்பட்ட ஜாவாஸ்கிரிப்ட் குறியீட்டால் மேலெழுதப்பட்டிருக்கலாம் என்பதை உணர்ந்தேன். அதனால் அது மாறியது. ஒரு மெகாபைட்டிலிருந்து பஃபரை அதிகரிப்பது எதற்கும் வழிவகுக்கவில்லை, மேலும் குறியீடு ஜெனரேட்டர் வட்டங்களில் இயங்குகிறது என்பது தெளிவாகியது. தற்போதைய காசநோயின் எல்லைக்கு அப்பால் செல்லவில்லை என்பதை நாங்கள் சரிபார்க்க வேண்டியிருந்தது, அவ்வாறு செய்தால், அடுத்த காசநோய்க்கான முகவரியை ஒரு கழித்தல் குறியுடன் வழங்கவும். கூடுதலாக, இது "இந்த பைட்கோடின் பகுதி மாறியிருந்தால் உருவாக்கப்பட்ட செயல்பாடுகள் செல்லாததாக்கப்பட வேண்டும்?" என்ற சிக்கலை தீர்க்கிறது. — இந்த மொழிபெயர்ப்புத் தொகுதியுடன் தொடர்புடைய செயல்பாடு மட்டுமே செல்லாததாக்கப்பட வேண்டும். நான் Chromium இல் உள்ள அனைத்தையும் பிழைத்திருத்தினாலும் (நான் பயர்பாக்ஸைப் பயன்படுத்துவதால், சோதனைகளுக்கு தனி உலாவியைப் பயன்படுத்துவது எனக்கு எளிதானது), பயர்பாக்ஸ் 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"]

முடிவுக்கு

எனவே, வேலை இன்னும் முடிவடையவில்லை, ஆனால் இந்த நீண்ட கால கட்டுமானத்தை ரகசியமாக முழுமையாக கொண்டு வருவதில் நான் சோர்வாக இருக்கிறேன். எனவே, தற்போது என்னிடம் இருப்பதை வெளியிட முடிவு செய்தேன். குறியீடு இடங்களில் கொஞ்சம் பயமாக இருக்கிறது, ஏனென்றால் இது ஒரு பரிசோதனையாகும், மேலும் என்ன செய்ய வேண்டும் என்பது முன்கூட்டியே தெளிவாகத் தெரியவில்லை. ஒருவேளை, Qemu இன் இன்னும் சில நவீன பதிப்பின் மேல் சாதாரண அணு கமிட்களை வழங்குவது மதிப்புக்குரியது. இதற்கிடையில், கீதையில் ஒரு வலைப்பதிவு வடிவத்தில் ஒரு நூல் உள்ளது: குறைந்தபட்சம் எப்படியாவது கடந்துவிட்ட ஒவ்வொரு "நிலை" க்கும், ரஷ்ய மொழியில் ஒரு விரிவான வர்ணனை சேர்க்கப்பட்டுள்ளது. உண்மையில், இந்த கட்டுரை ஒரு பெரிய அளவிற்கு முடிவை மறுபரிசீலனை செய்கிறது git log.

நீங்கள் அனைத்தையும் முயற்சி செய்யலாம் இங்கே (போக்குவரத்தில் ஜாக்கிரதை).

ஏற்கனவே என்ன வேலை செய்கிறது:

  • x86 மெய்நிகர் செயலி இயங்குகிறது
  • இயந்திரக் குறியீட்டிலிருந்து ஜாவாஸ்கிரிப்ட் வரை JIT குறியீடு ஜெனரேட்டரின் வேலை செய்யும் முன்மாதிரி உள்ளது
  • மற்ற 32-பிட் கெஸ்ட் ஆர்கிடெக்சர்களை அசெம்பிள் செய்வதற்கு ஒரு டெம்ப்ளேட் உள்ளது: லோடிங் கட்டத்தில் உலாவியில் MIPS கட்டமைப்பை உறைய வைக்க லினக்ஸை இப்போது நீங்கள் பாராட்டலாம்.

வேறென்ன செய்ய முடியும்

  • எமுலேஷனை விரைவுபடுத்துங்கள். JIT பயன்முறையில் கூட இது விர்ச்சுவல் x86 ஐ விட மெதுவாக இயங்குவது போல் தெரிகிறது (ஆனால் நிறைய எமுலேட்டட் ஹார்டுவேர் மற்றும் ஆர்கிடெக்சர்கள் கொண்ட முழு Qemu உள்ளது)
  • ஒரு சாதாரண இடைமுகத்தை உருவாக்க - வெளிப்படையாக, நான் ஒரு நல்ல வலை டெவலப்பர் அல்ல, எனவே தற்போது நான் நிலையான எம்ஸ்கிரிப்டன் ஷெல்லை என்னால் முடிந்தவரை ரீமேக் செய்துள்ளேன்.
  • மிகவும் சிக்கலான Qemu செயல்பாடுகளை தொடங்க முயற்சிக்கவும் - நெட்வொர்க்கிங், VM இடம்பெயர்வு போன்றவை.
  • யு பி எஸ்: Qemu மற்றும் பிற திட்டங்களின் முந்தைய போர்ட்டர்கள் செய்ததைப் போல, உங்கள் சில வளர்ச்சிகள் மற்றும் பிழை அறிக்கைகளை எம்ஸ்கிரிப்டன் அப்ஸ்ட்ரீமில் சமர்ப்பிக்க வேண்டும். எனது பணியின் ஒரு பகுதியாக எம்ஸ்கிரிப்டனுக்கு அவர்களின் பங்களிப்பை மறைமுகமாகப் பயன்படுத்த முடிந்ததற்கு அவர்களுக்கு நன்றி.

ஆதாரம்: www.habr.com

கருத்தைச் சேர்