JIT మద్దతుతో Qemu.js: మీరు ఇప్పటికీ మాంసఖండాన్ని వెనుకకు తిప్పవచ్చు

కొన్ని సంవత్సరాల క్రితం ఫాబ్రిస్ బెల్లార్డ్ jslinux చే వ్రాయబడింది జావాస్క్రిప్ట్‌లో వ్రాయబడిన PC ఎమ్యులేటర్. ఆ తర్వాత కనీసం ఎక్కువ వర్చువల్ x86. అయితే వారందరూ, నాకు తెలిసినంత వరకు, వ్యాఖ్యాతలు, అయితే Qemu, అదే ఫాబ్రిస్ బెల్లార్డ్ ద్వారా చాలా ముందుగా వ్రాయబడింది మరియు బహుశా, ఏదైనా స్వీయ-గౌరవనీయ ఆధునిక ఎమ్యులేటర్, అతిథి కోడ్ యొక్క JIT సంకలనాన్ని హోస్ట్ సిస్టమ్ కోడ్‌గా ఉపయోగిస్తుంది. బ్రౌజర్‌లు పరిష్కరించే దానికి సంబంధించి వ్యతిరేక పనిని అమలు చేయడానికి ఇది సమయం అని నాకు అనిపించింది: జావాస్క్రిప్ట్‌లోకి మెషిన్ కోడ్ యొక్క JIT సంకలనం, దీని కోసం పోర్ట్ Qemuకి ఇది చాలా లాజికల్‌గా అనిపించింది. ఇది కనిపిస్తుంది, ఎందుకు Qemu, సరళమైన మరియు యూజర్ ఫ్రెండ్లీ ఎమ్యులేటర్లు ఉన్నాయి - అదే VirtualBox, ఉదాహరణకు - ఇన్స్టాల్ మరియు పనిచేస్తుంది. కానీ Qmu అనేక ఆసక్తికరమైన లక్షణాలను కలిగి ఉంది

  • ఓపెన్ సోర్స్
  • కెర్నల్ డ్రైవర్ లేకుండా పని చేసే సామర్థ్యం
  • ఇంటర్‌ప్రెటర్ మోడ్‌లో పని చేసే సామర్థ్యం
  • పెద్ద సంఖ్యలో హోస్ట్ మరియు గెస్ట్ ఆర్కిటెక్చర్‌లకు మద్దతు

మూడవ అంశానికి సంబంధించి, నేను ఇప్పుడు వివరించగలను, వాస్తవానికి, TCI మోడ్‌లో, అతిథి యంత్రం సూచనలను తాము అర్థం చేసుకోలేము, కానీ వాటి నుండి పొందిన బైట్‌కోడ్, కానీ ఇది సారాంశాన్ని మార్చదు - నిర్మించడానికి మరియు అమలు చేయడానికి. కొత్త ఆర్కిటెక్చర్‌పై Qemu, మీరు అదృష్టవంతులైతే, A C కంపైలర్ సరిపోతుంది - కోడ్ జెనరేటర్ రాయడం వాయిదా వేయవచ్చు.

మరియు ఇప్పుడు, నా ఖాళీ సమయంలో Qemu సోర్స్ కోడ్‌తో తీరికగా టింకరింగ్ చేసిన రెండు సంవత్సరాల తరువాత, పని చేసే నమూనా కనిపించింది, దీనిలో మీరు ఇప్పటికే అమలు చేయవచ్చు, ఉదాహరణకు, Kolibri OS.

ఎంస్క్రిప్ట్ అంటే ఏమిటి

ఈ రోజుల్లో, అనేక కంపైలర్లు కనిపించాయి, దీని తుది ఫలితం జావాస్క్రిప్ట్. కొన్ని, టైప్ స్క్రిప్ట్ వంటివి, నిజానికి వెబ్ కోసం వ్రాయడానికి ఉత్తమ మార్గంగా ఉద్దేశించబడ్డాయి. అదే సమయంలో, ఎమ్‌స్క్రిప్టెన్ అనేది ఇప్పటికే ఉన్న C లేదా C++ కోడ్‌ని తీసుకొని దానిని బ్రౌజర్-రీడబుల్ రూపంలోకి కంపైల్ చేయడానికి ఒక మార్గం. పై ఈ పేజీ మేము ప్రసిద్ధ ప్రోగ్రామ్‌ల యొక్క అనేక పోర్ట్‌లను సేకరించాము: ఇక్కడఉదాహరణకు, మీరు PyPyని చూడవచ్చు - మార్గం ద్వారా, వారు ఇప్పటికే JITని కలిగి ఉన్నారని పేర్కొన్నారు. నిజానికి, ప్రతి ప్రోగ్రామ్ కేవలం కంపైల్ చేయబడదు మరియు బ్రౌజర్లో అమలు చేయబడదు - ఒక సంఖ్య ఉన్నాయి లక్షణాలు, ఇది మీరు సహించవలసి ఉంటుంది, అయితే, అదే పేజీలోని శాసనం ప్రకారం “ఎంస్క్రిప్టెన్ దాదాపు ఏదైనా కంపైల్ చేయడానికి ఉపయోగించవచ్చు పోర్టబుల్ జావాస్క్రిప్ట్‌కి C/C++ కోడ్". అంటే, స్టాండర్డ్ ప్రకారం నిర్వచించబడని అనేక కార్యకలాపాలు ఉన్నాయి, కానీ సాధారణంగా x86లో పని చేస్తాయి - ఉదాహరణకు, వేరియబుల్స్‌కు అన్‌లైన్డ్ యాక్సెస్, ఇది సాధారణంగా కొన్ని ఆర్కిటెక్చర్‌లలో నిషేధించబడింది. సాధారణంగా , Qemu అనేది క్రాస్-ప్లాట్‌ఫారమ్ ప్రోగ్రామ్ మరియు , నేను విశ్వసించాలనుకుంటున్నాను మరియు ఇది ఇప్పటికే చాలా నిర్వచించబడని ప్రవర్తనను కలిగి లేదు - దాన్ని తీసుకొని కంపైల్ చేయండి, ఆపై JITతో కొద్దిగా టింకర్ చేయండి - మరియు మీరు పూర్తి చేసారు! కానీ అది కాదు కేసు...

మొదటి ప్రయత్నం

సాధారణంగా చెప్పాలంటే, Qemuని జావాస్క్రిప్ట్‌కి పోర్ట్ చేయాలనే ఆలోచనతో వచ్చిన మొదటి వ్యక్తి నేను కాదు. ఎమ్‌స్క్రిప్టెన్‌ని ఉపయోగించి ఇది సాధ్యమేనా అని ReactOS ఫోరమ్‌లో ఒక ప్రశ్న అడిగారు. ఇంతకుముందు కూడా, ఫాబ్రిస్ బెల్లార్డ్ దీన్ని వ్యక్తిగతంగా చేశాడని పుకార్లు వచ్చాయి, కాని మేము jslinux గురించి మాట్లాడుతున్నాము, ఇది నాకు తెలిసినంతవరకు, JSలో తగినంత పనితీరును మాన్యువల్‌గా సాధించడానికి చేసిన ప్రయత్నం మరియు మొదటి నుండి వ్రాయబడింది. తరువాత, వర్చువల్ x86 వ్రాయబడింది - దాని కోసం అస్పష్టమైన మూలాలు పోస్ట్ చేయబడ్డాయి మరియు పేర్కొన్నట్లుగా, ఎమ్యులేషన్ యొక్క గొప్ప “వాస్తవికత” సీబయోస్‌ను ఫర్మ్‌వేర్‌గా ఉపయోగించడం సాధ్యం చేసింది. అదనంగా, Emscriptenని ఉపయోగించి Qemuని పోర్ట్ చేయడానికి కనీసం ఒక ప్రయత్నం జరిగింది - నేను దీన్ని చేయడానికి ప్రయత్నించాను సాకెట్ పెయిర్, కానీ అభివృద్ధి, నేను అర్థం చేసుకున్నంత వరకు, స్తంభింపజేయబడింది.

కాబట్టి, ఇక్కడ మూలాలు ఉన్నాయి, ఇక్కడ ఎమ్‌స్క్రిప్టెన్ ఉంది - దానిని తీసుకొని కంపైల్ చేయండి. అయితే Qemu ఆధారపడిన గ్రంథాలయాలు మరియు ఆ గ్రంథాలయాలపై ఆధారపడిన లైబ్రరీలు మొదలైనవి కూడా ఉన్నాయి మరియు వాటిలో ఒకటి libffi, ఏ గ్లిబ్ ఆధారపడి ఉంటుంది. ఎమ్‌స్క్రిప్టెన్ కోసం లైబ్రరీల పోర్ట్‌ల యొక్క పెద్ద సేకరణలో ఒకటి ఉందని ఇంటర్నెట్‌లో పుకార్లు వచ్చాయి, కానీ నమ్మడం కష్టం: మొదటిది, ఇది కొత్త కంపైలర్‌గా ఉద్దేశించబడలేదు, రెండవది, ఇది చాలా తక్కువ స్థాయి లైబ్రరీని తీయడానికి మరియు JSకి కంపైల్ చేయడానికి. మరియు ఇది కేవలం అసెంబ్లీ ఇన్సర్ట్‌ల విషయం కాదు - బహుశా, మీరు దాన్ని ట్విస్ట్ చేస్తే, కొన్ని కాలింగ్ సమావేశాల కోసం మీరు స్టాక్‌పై అవసరమైన ఆర్గ్యుమెంట్‌లను రూపొందించవచ్చు మరియు అవి లేకుండా ఫంక్షన్‌కు కాల్ చేయవచ్చు. కానీ ఎమ్‌స్క్రిప్టెన్ అనేది ఒక గమ్మత్తైన విషయం: రూపొందించిన కోడ్‌ని బ్రౌజర్ JS ఇంజిన్ ఆప్టిమైజర్‌కు సుపరిచితమైనదిగా చేయడానికి, కొన్ని ఉపాయాలు ఉపయోగించబడతాయి. ప్రత్యేకించి, రీలూపింగ్ అని పిలవబడేది - కొన్ని నైరూప్య పరివర్తన సూచనలతో స్వీకరించబడిన LLVM IR ఉపయోగించి కోడ్ జనరేటర్ ఆమోదయోగ్యమైన ifs, లూప్‌లు మొదలైనవాటిని పునఃసృష్టి చేయడానికి ప్రయత్నిస్తుంది. సరే, ఫంక్షన్‌కు వాదనలు ఎలా ఆమోదించబడ్డాయి? సహజంగానే, JS ఫంక్షన్‌లకు ఆర్గ్యుమెంట్‌లుగా, అంటే, వీలైతే, స్టాక్ ద్వారా కాదు.

ప్రారంభంలో jSతో libffiకి రీప్లేస్‌మెంట్‌ని వ్రాసి ప్రామాణిక పరీక్షలను అమలు చేయాలనే ఆలోచన ఉంది, కానీ చివరికి నా హెడర్ ఫైల్‌లను ఎలా తయారు చేయాలనే దానిపై నేను గందరగోళానికి గురయ్యాను, తద్వారా అవి ఇప్పటికే ఉన్న కోడ్‌తో పని చేస్తాయి - నేను ఏమి చేయగలను, వారు చెప్పినట్లు, "పనులు చాలా క్లిష్టంగా ఉన్నాయా "మేము చాలా తెలివితక్కువవా?" నేను లిబ్ఫీని మరొక ఆర్కిటెక్చర్‌కి పోర్ట్ చేయాల్సి వచ్చింది, చెప్పాలంటే - అదృష్టవశాత్తూ, ఎమ్‌స్క్రిప్టెన్‌లో ఇన్‌లైన్ అసెంబ్లీ కోసం మాక్రోలు రెండూ ఉన్నాయి (జావాస్క్రిప్ట్‌లో, అవును - సరే, ఆర్కిటెక్చర్ ఏదైనా సరే, కాబట్టి అసెంబ్లర్), మరియు ఫ్లైలో ఉత్పత్తి చేయబడిన కోడ్‌ని అమలు చేయగల సామర్థ్యం. సాధారణంగా, ప్లాట్‌ఫారమ్-ఆధారిత libffi శకలాలతో కొంత సమయం పాటు టింకర్ చేసిన తర్వాత, నేను కొంత కంపైల్ చేయదగిన కోడ్‌ని పొందాను మరియు నేను చూసిన మొదటి పరీక్షలో దాన్ని అమలు చేసాను. నా ఆశ్చర్యానికి, పరీక్ష విజయవంతమైంది. నా మేధావిని చూసి ఆశ్చర్యపోయాను - జోక్ లేదు, ఇది మొదటి ప్రయోగం నుండి పనిచేసింది - నేను, ఇప్పటికీ నా కళ్లను నమ్మలేదు, ఫలిత కోడ్‌ను మళ్లీ చూడడానికి, తదుపరి ఎక్కడ తవ్వాలో అంచనా వేయడానికి వెళ్ళాను. ఇక్కడ నేను రెండవ సారి నట్స్ వెళ్ళాను - నా ఫంక్షన్ చేసింది ఒక్కటే ffi_call - ఇది విజయవంతమైన కాల్‌ని నివేదించింది. స్వయంగా ఎటువంటి పిలుపు రాలేదు. కాబట్టి నేను నా మొదటి పుల్ అభ్యర్థనను పంపాను, ఇది పరీక్షలో ఏదైనా ఒలింపియాడ్ విద్యార్థికి స్పష్టంగా కనిపించే లోపాన్ని సరిదిద్దింది - వాస్తవ సంఖ్యలను ఇలా పోల్చకూడదు a == b మరియు ఎలా కూడా a - b < EPS - మీరు మాడ్యూల్‌ను కూడా గుర్తుంచుకోవాలి, లేకపోతే 0 1/3కి చాలా సమానంగా మారుతుంది... సాధారణంగా, నేను లిబ్ఫీ యొక్క నిర్దిష్ట పోర్ట్‌తో ముందుకు వచ్చాను, ఇది సరళమైన పరీక్షలలో ఉత్తీర్ణత సాధిస్తుంది మరియు దానితో గ్లిబ్ ఉంటుంది. సంకలనం చేయబడింది - ఇది అవసరమని నేను నిర్ణయించుకున్నాను, నేను దానిని తర్వాత జోడిస్తాను. ముందుకు చూస్తే, కంపైలర్ తుది కోడ్‌లో libffi ఫంక్షన్‌ను కూడా చేర్చలేదని నేను చెబుతాను.

కానీ, నేను ఇప్పటికే చెప్పినట్లుగా, కొన్ని పరిమితులు ఉన్నాయి మరియు వివిధ నిర్వచించబడని ప్రవర్తన యొక్క ఉచిత ఉపయోగంలో, మరింత అసహ్యకరమైన లక్షణం దాచబడింది - డిజైన్ ద్వారా జావాస్క్రిప్ట్ షేర్డ్ మెమరీతో మల్టీథ్రెడింగ్‌కు మద్దతు ఇవ్వదు. సూత్రప్రాయంగా, దీనిని సాధారణంగా మంచి ఆలోచన అని కూడా పిలుస్తారు, అయితే C థ్రెడ్‌లతో ఆర్కిటెక్చర్ ముడిపడి ఉన్న పోర్టింగ్ కోడ్ కోసం కాదు. సాధారణంగా చెప్పాలంటే, ఫైర్‌ఫాక్స్ భాగస్వామ్య కార్మికులకు మద్దతు ఇవ్వడంతో ప్రయోగాలు చేస్తోంది మరియు ఎమ్‌స్క్రిప్టెన్ వారి కోసం pthread అమలును కలిగి ఉంది, కానీ నేను దానిపై ఆధారపడదలుచుకోలేదు. నేను Qemu కోడ్ నుండి మల్టీథ్రెడింగ్‌ని నెమ్మదిగా రూట్ చేయవలసి వచ్చింది - అంటే, థ్రెడ్‌లు ఎక్కడ రన్ అవుతున్నాయో కనుక్కోవాలి, ఈ థ్రెడ్‌లో నడుస్తున్న లూప్ యొక్క బాడీని ప్రత్యేక ఫంక్షన్‌లోకి తరలించి, అటువంటి ఫంక్షన్‌లను మెయిన్ లూప్ నుండి ఒక్కొక్కటిగా కాల్ చేయాలి.

రెండవ ప్రయత్నం

ఏదో ఒక సమయంలో, సమస్య ఇంకా ఉందని మరియు కోడ్ చుట్టూ అకస్మాత్తుగా క్రచ్‌లను తిప్పడం వల్ల మంచి జరగదని స్పష్టమైంది. తీర్మానం: క్రచెస్ జోడించే ప్రక్రియను మనం ఏదో ఒకవిధంగా క్రమబద్ధీకరించాలి. అందువల్ల, ఆ సమయంలో తాజాగా ఉన్న వెర్షన్ 2.4.1 తీయబడింది (2.5.0 కాదు, ఎందుకంటే, ఎవరికి తెలుసు, కొత్త వెర్షన్‌లో ఇంకా క్యాచ్ చేయని బగ్‌లు ఉంటాయి మరియు నా స్వంత బగ్‌లు తగినంతగా ఉన్నాయి ), మరియు మొదటి విషయం సురక్షితంగా తిరిగి వ్రాయడం thread-posix.c. బాగా, అంటే, సురక్షితంగా: ఎవరైనా నిరోధించడానికి దారితీసే ఆపరేషన్ చేయడానికి ప్రయత్నించినట్లయితే, ఫంక్షన్ వెంటనే పిలువబడుతుంది abort() - వాస్తవానికి, ఇది ఒకేసారి అన్ని సమస్యలను పరిష్కరించలేదు, కానీ కనీసం అస్థిరమైన డేటాను నిశ్శబ్దంగా స్వీకరించడం కంటే ఇది మరింత ఆహ్లాదకరంగా ఉంటుంది.

సాధారణంగా, JSకి కోడ్‌ను పోర్టింగ్ చేయడంలో ఎమ్‌స్క్రిప్టెన్ ఎంపికలు చాలా సహాయకారిగా ఉంటాయి -s ASSERTIONS=1 -s SAFE_HEAP=1 - అవి సమలేఖనం చేయని చిరునామాకు కాల్‌ల వంటి కొన్ని రకాల నిర్వచించబడని ప్రవర్తనను క్యాచ్ చేస్తాయి (ఇది టైప్ చేసిన శ్రేణుల కోడ్‌తో అస్సలు స్థిరంగా ఉండదు HEAP32[addr >> 2] = 1) లేదా తప్పుడు ఆర్గ్యుమెంట్‌లతో ఫంక్షన్‌కి కాల్ చేయడం.

మార్గం ద్వారా, అమరిక లోపాలు ఒక ప్రత్యేక సమస్య. నేను ఇప్పటికే చెప్పినట్లుగా, Qemu కోడ్ జనరేషన్ TCI (చిన్న కోడ్ ఇంటర్‌ప్రెటర్) కోసం “డిజెనరేట్” ఇంటర్‌ప్రెటివ్ బ్యాకెండ్‌ను కలిగి ఉంది మరియు Qemuని కొత్త ఆర్కిటెక్చర్‌లో నిర్మించడానికి మరియు అమలు చేయడానికి, మీరు అదృష్టవంతులైతే, C కంపైలర్ సరిపోతుంది. కీవర్డ్‌లు "మీరు అదృష్టవంతులైతే". నేను దురదృష్టవంతుడిని మరియు TCI దాని బైట్‌కోడ్‌ను అన్వయించేటప్పుడు సమలేఖనం చేయని ప్రాప్యతను ఉపయోగిస్తుందని తేలింది. అంటే, అన్ని రకాల ARM మరియు ఇతర ఆర్కిటెక్చర్‌లలో తప్పనిసరిగా లెవెల్డ్ యాక్సెస్‌తో, Qemu కంపైల్ చేస్తుంది ఎందుకంటే అవి స్థానిక కోడ్‌ను రూపొందించే సాధారణ TCG బ్యాకెండ్‌ని కలిగి ఉంటాయి, అయితే TCI వాటిపై పని చేస్తుందా అనేది మరొక ప్రశ్న. అయినప్పటికీ, 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 ఫైల్‌లో కనుగొనబడింది - అంటే, ఇప్పుడు ఇది బఫర్ ఓవర్‌ఫ్లో అని సహేతుకమైన విశ్వాసంతో చెప్పడం సాధ్యమైంది మరియు ఇది ఈ బఫర్‌కు వ్రాయబడిందని కూడా స్పష్టంగా తెలుస్తుంది. బాగా, అప్పుడు ఇలాంటిది - ఎమ్‌స్క్రిప్టెన్‌లో, అదృష్టవశాత్తూ, చిరునామా స్థలం యొక్క యాదృచ్ఛికీకరణ లేదు, దానిలో రంధ్రాలు కూడా లేవు, కాబట్టి మీరు చివరి ప్రయోగ నుండి పాయింటర్ ద్వారా డేటాను అవుట్‌పుట్ చేయడానికి కోడ్ మధ్యలో ఎక్కడో వ్రాయవచ్చు, డేటాను చూడండి, పాయింటర్‌ను చూడండి మరియు , అది మారకపోతే, ఆలోచన కోసం ఆహారాన్ని పొందండి. నిజమే, ఏదైనా మార్పు తర్వాత లింక్ చేయడానికి కొన్ని నిమిషాలు పడుతుంది, అయితే మీరు ఏమి చేయగలరు? ఫలితంగా, తాత్కాలిక బఫర్ నుండి అతిథి మెమరీకి BIOSని కాపీ చేసే నిర్దిష్ట లైన్ కనుగొనబడింది - మరియు, నిజానికి, బఫర్‌లో తగినంత స్థలం లేదు. ఆ వింత బఫర్ చిరునామా యొక్క మూలాన్ని కనుగొనడం వలన ఒక ఫంక్షన్ ఏర్పడింది qemu_anon_ram_alloc ఫైల్‌లో oslib-posix.c - తర్కం ఇది: కొన్నిసార్లు చిరునామాను 2 MB పరిమాణంలో ఉన్న భారీ పేజీకి సమలేఖనం చేయడం ఉపయోగకరంగా ఉంటుంది, దీని కోసం మేము అడుగుతాము mmap మొదట కొంచెం ఎక్కువ, ఆపై మేము సహాయంతో అదనపు మొత్తాన్ని తిరిగి ఇస్తాము munmap. మరియు అటువంటి అమరిక అవసరం లేకపోతే, మేము 2 MBకి బదులుగా ఫలితాన్ని సూచిస్తాము getpagesize() - mmap ఇది ఇప్పటికీ సమలేఖనం చేయబడిన చిరునామాను ఇస్తుంది... కాబట్టి ఎమ్‌స్క్రిప్టెన్‌లో mmap కేవలం కాల్స్ malloc, అయితే ఇది పేజీలో సమలేఖనం చేయదు. సాధారణంగా, కొన్ని నెలలపాటు నన్ను నిరాశపరిచిన ఒక బగ్ మార్పు ద్వారా సరిదిద్దబడింది двух పంక్తులు.

కాల్ ఫంక్షన్ల లక్షణాలు

మరియు ఇప్పుడు ప్రాసెసర్ ఏదో లెక్కిస్తోంది, Qemu క్రాష్ అవ్వదు, కానీ స్క్రీన్ ఆన్ చేయదు మరియు ప్రాసెసర్ త్వరగా లూప్‌లలోకి వెళుతుంది, అవుట్‌పుట్ ద్వారా నిర్ణయించబడుతుంది -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 కోడ్ కొరౌటిన్‌లలో వ్రాయబడిందని ఇక్కడ స్పష్టం చేయడం అవసరం. Emscripten దాని స్వంత చాలా గమ్మత్తైన అమలును కలిగి ఉంది, కానీ Qemu కోడ్‌లో దీనికి ఇంకా మద్దతు అవసరం మరియు మీరు ఇప్పుడు ప్రాసెసర్‌ను డీబగ్ చేయవచ్చు: Qemu ఎంపికలకు మద్దతు ఇస్తుంది -kernel, -initrd, -append, దీనితో మీరు Linux లేదా, ఉదాహరణకు, memtest86+, బ్లాక్ పరికరాలను ఉపయోగించకుండా బూట్ చేయవచ్చు. కానీ ఇక్కడ సమస్య ఉంది: స్థానిక అసెంబ్లీలో ఎంపికతో కన్సోల్‌కు Linux కెర్నల్ అవుట్‌పుట్‌ను చూడవచ్చు -nographic, మరియు బ్రౌజర్ నుండి టెర్మినల్‌కు ఇది ప్రారంభించబడిన ప్రదేశం నుండి అవుట్‌పుట్ లేదు emrun, రాలేదు. అంటే, ఇది స్పష్టంగా లేదు: ప్రాసెసర్ పనిచేయడం లేదా గ్రాఫిక్స్ అవుట్పుట్ పనిచేయడం లేదు. ఆపై కొంచెం వేచి ఉండాలని నాకు అనిపించింది. "ప్రాసెసర్ నిద్రపోలేదు, కానీ నెమ్మదిగా మెరిసిపోతోంది" అని తేలింది మరియు సుమారు ఐదు నిమిషాల తర్వాత కెర్నల్ కొన్ని సందేశాలను కన్సోల్‌పైకి విసిరి, వేలాడదీయడం కొనసాగించింది. ప్రాసెసర్ సాధారణంగా పనిచేస్తుందని స్పష్టమైంది మరియు SDL2తో పని చేయడానికి మేము కోడ్‌ను తీయాలి. దురదృష్టవశాత్తూ, ఈ లైబ్రరీని ఎలా ఉపయోగించాలో నాకు తెలియదు, కాబట్టి కొన్ని ప్రదేశాలలో నేను యాదృచ్ఛికంగా పని చేయాల్సి వచ్చింది. ఏదో ఒక సమయంలో, నీలిరంగు నేపథ్యంలో స్క్రీన్‌పై సమాంతర 0 అనే రేఖ మెరుస్తుంది, ఇది కొన్ని ఆలోచనలను సూచించింది. చివరికి, సమస్య ఏమిటంటే, Qemu ఒక భౌతిక విండోలో అనేక వర్చువల్ విండోలను తెరుస్తుంది, వాటి మధ్య మీరు Ctrl-Alt-nని ఉపయోగించి మారవచ్చు: ఇది స్థానిక బిల్డ్‌లో పనిచేస్తుంది, కానీ ఎమ్‌స్క్రిప్టెన్‌లో కాదు. ఎంపికలను ఉపయోగించి అనవసరమైన విండోలను వదిలించుకున్న తర్వాత -monitor none -parallel none -serial none మరియు ప్రతి ఫ్రేమ్‌లో మొత్తం స్క్రీన్‌ను బలవంతంగా మళ్లీ గీయడానికి సూచనలు, ప్రతిదీ అకస్మాత్తుగా పని చేసింది.

కొరోటీన్స్

కాబట్టి, బ్రౌజర్‌లో ఎమ్యులేషన్ పనిచేస్తుంది, కానీ మీరు దానిలో ఆసక్తికరమైన సింగిల్-ఫ్లాపీని ఏదైనా అమలు చేయలేరు, ఎందుకంటే బ్లాక్ I/O లేదు - మీరు కొరౌటిన్‌లకు మద్దతును అమలు చేయాలి. Qemu ఇప్పటికే అనేక కరోటిన్ బ్యాకెండ్‌లను కలిగి ఉంది, కానీ జావాస్క్రిప్ట్ మరియు ఎమ్‌స్క్రిప్టెన్ కోడ్ జెనరేటర్ యొక్క స్వభావం కారణంగా, మీరు స్టాక్‌లను గారడీ చేయడం ప్రారంభించలేరు. "ప్రతిదీ పోయింది, ప్లాస్టర్ తొలగించబడుతోంది" అని అనిపించవచ్చు, కాని ఎమ్‌స్క్రిప్టెన్ డెవలపర్లు ఇప్పటికే ప్రతిదాన్ని జాగ్రత్తగా చూసుకున్నారు. ఇది చాలా ఫన్నీగా అమలు చేయబడింది: ఇలాంటి ఫంక్షన్ కాల్‌ని అనుమానాస్పదంగా పిలుద్దాం emscripten_sleep మరియు అనేక ఇతర Asyncify మెకానిజమ్‌ని ఉపయోగిస్తుంది, అలాగే మునుపటి రెండు సందర్భాలలో ఒకటి స్టాక్‌కు దిగువన సంభవించే ఏదైనా ఫంక్షన్‌కి పాయింటర్ కాల్‌లు మరియు కాల్‌లు. ఇప్పుడు, ప్రతి అనుమానాస్పద కాల్‌కు ముందు, మేము అసమకాలిక సందర్భాన్ని ఎంచుకుంటాము మరియు కాల్ చేసిన వెంటనే, అసమకాలిక కాల్ సంభవించిందో లేదో తనిఖీ చేస్తాము మరియు అది జరిగితే, మేము ఈ అసమకాలిక సందర్భంలో అన్ని స్థానిక వేరియబుల్‌లను సేవ్ చేస్తాము, ఏ ఫంక్షన్‌ని సూచిస్తాము మేము ఎగ్జిక్యూషన్‌ను కొనసాగించాల్సినప్పుడు నియంత్రణను బదిలీ చేయడానికి మరియు ప్రస్తుత ఫంక్షన్ నుండి నిష్క్రమించడానికి. ఇక్కడే ప్రభావాన్ని అధ్యయనం చేయడానికి అవకాశం ఉంది దుబారా చేయడం — అసమకాలిక కాల్ నుండి తిరిగి వచ్చిన తర్వాత కోడ్ అమలును కొనసాగించే అవసరాల కోసం, కంపైలర్ అనుమానాస్పద కాల్ తర్వాత ప్రారంభమయ్యే ఫంక్షన్ యొక్క “స్టబ్‌లను” ఉత్పత్తి చేస్తుంది — ఇలా: n అనుమానాస్పద కాల్‌లు ఉంటే, అప్పుడు ఫంక్షన్ ఎక్కడో n/2 విస్తరించబడుతుంది. సార్లు — ఇది ఇప్పటికీ ఉంది, కాకపోతే ప్రతి సంభావ్య అసమకాలిక కాల్ తర్వాత, మీరు అసలు ఫంక్షన్‌కు కొన్ని స్థానిక వేరియబుల్‌లను సేవ్ చేయవలసి ఉంటుందని గుర్తుంచుకోండి. తదనంతరం, నేను పైథాన్‌లో ఒక సాధారణ స్క్రిప్ట్‌ను కూడా వ్రాయవలసి వచ్చింది, ఇది "అసమకాలీకరణను తమ గుండా అనుమతించవద్దు" (అంటే, స్టాక్ ప్రమోషన్ మరియు నేను ఇప్పుడే వివరించిన ప్రతిదానిని కాదు" అని భావించే ప్రత్యేకంగా ఎక్కువగా ఉపయోగించిన ఫంక్షన్‌ల సెట్ ఆధారంగా వాటిలో పని చేయండి), పాయింటర్ల ద్వారా కాల్‌లను సూచిస్తుంది, దీనిలో ఫంక్షన్‌లను కంపైలర్ విస్మరించాలి, తద్వారా ఈ ఫంక్షన్‌లు అసమకాలికంగా పరిగణించబడవు. ఆపై 60 MB లోపు ఉన్న JS ఫైల్‌లు స్పష్టంగా చాలా ఎక్కువగా ఉన్నాయి - కనీసం 30 అని చెప్పండి. అయితే, ఒకసారి నేను అసెంబ్లీ స్క్రిప్ట్‌ని సెటప్ చేస్తున్నప్పుడు మరియు అనుకోకుండా లింకర్ ఎంపికలను విసిరివేసారు, వాటిలో -O3. నేను రూపొందించిన కోడ్‌ని అమలు చేస్తున్నాను మరియు Chromium మెమరీని నాశనం చేస్తుంది మరియు క్రాష్ అవుతుంది. నేను అనుకోకుండా అతను డౌన్‌లోడ్ చేయడానికి ప్రయత్నిస్తున్నదాన్ని చూశాను... సరే, నేను ఏమి చెప్పగలను, నేను 500+ MB జావాస్క్రిప్ట్‌ని ఆలోచనాత్మకంగా అధ్యయనం చేసి, ఆప్టిమైజ్ చేయమని అడిగితే నేను కూడా స్తంభించి ఉండేవాడిని.

దురదృష్టవశాత్తూ, Asyncify మద్దతు లైబ్రరీ కోడ్‌లోని తనిఖీలు పూర్తిగా స్నేహపూర్వకంగా లేవు longjmp-s వర్చువల్ ప్రాసెసర్ కోడ్‌లో ఉపయోగించబడతాయి, అయితే ఈ తనిఖీలను నిలిపివేసే చిన్న ప్యాచ్ తర్వాత మరియు ప్రతిదీ బాగానే ఉన్నట్లుగా సందర్భాలను బలవంతంగా పునరుద్ధరిస్తుంది, కోడ్ పని చేస్తుంది. ఆపై ఒక విచిత్రమైన విషయం ప్రారంభమైంది: కొన్నిసార్లు సింక్రొనైజేషన్ కోడ్‌లోని తనిఖీలు ప్రేరేపించబడతాయి - అమలు తర్కం ప్రకారం, అది బ్లాక్ చేయబడితే కోడ్‌ను క్రాష్ చేసేవి - ఎవరైనా ఇప్పటికే సంగ్రహించిన మ్యూటెక్స్‌ను పట్టుకోవడానికి ప్రయత్నించారు. అదృష్టవశాత్తూ, ఇది సీరియల్ కోడ్‌లో తార్కిక సమస్య కాదని తేలింది - నేను కేవలం ఎమ్‌స్క్రిప్టెన్ అందించిన ప్రామాణిక ప్రధాన లూప్ కార్యాచరణను ఉపయోగిస్తున్నాను, కానీ కొన్నిసార్లు అసమకాలిక కాల్ స్టాక్‌ను పూర్తిగా విప్పుతుంది మరియు ఆ సమయంలో అది విఫలమవుతుంది. setTimeout ప్రధాన లూప్ నుండి - అందువలన, కోడ్ మునుపటి పునరావృతం నుండి వదలకుండా ప్రధాన లూప్ పునరావృతంలోకి ప్రవేశించింది. అనంతమైన లూప్‌లో తిరిగి వ్రాయబడింది మరియు emscripten_sleep, మరియు మ్యూటెక్స్‌లతో సమస్యలు ఆగిపోయాయి. కోడ్ మరింత లాజికల్‌గా మారింది - వాస్తవానికి, తదుపరి యానిమేషన్ ఫ్రేమ్‌ను సిద్ధం చేసే కొన్ని కోడ్ నా దగ్గర లేదు - ప్రాసెసర్ ఏదో లెక్కిస్తుంది మరియు స్క్రీన్ క్రమానుగతంగా నవీకరించబడుతుంది. అయినప్పటికీ, సమస్యలు అక్కడితో ఆగలేదు: కొన్నిసార్లు Qemu అమలు ఎటువంటి మినహాయింపులు లేదా లోపాలు లేకుండా నిశ్శబ్దంగా ముగుస్తుంది. ఆ సమయంలో నేను దానిని వదులుకున్నాను, కానీ, ముందుకు చూస్తే, సమస్య ఇదే అని నేను చెప్తాను: కొరోటిన్ కోడ్, వాస్తవానికి, ఉపయోగించదు setTimeout (లేదా కనీసం మీరు అనుకున్నంత తరచుగా కాదు): ఫంక్షన్ emscripten_yield కేవలం అసమకాలిక కాల్ ఫ్లాగ్‌ను సెట్ చేస్తుంది. మొత్తం విషయం ఏమిటంటే emscripten_coroutine_next అసమకాలిక ఫంక్షన్ కాదు: అంతర్గతంగా ఇది ఫ్లాగ్‌ను తనిఖీ చేస్తుంది, దాన్ని రీసెట్ చేస్తుంది మరియు అవసరమైన చోట నియంత్రణను బదిలీ చేస్తుంది. అంటే, స్టాక్ యొక్క ప్రమోషన్ అక్కడ ముగుస్తుంది. సమస్య ఏమిటంటే, నేను ఇప్పటికే ఉన్న కరోటిన్ బ్యాకెండ్, ఫంక్షన్ నుండి కోడ్ యొక్క ముఖ్యమైన లైన్‌ను కాపీ చేయనందున కొరూటిన్ పూల్ నిలిపివేయబడినప్పుడు కనిపించిన తర్వాత-ఉచిత ఉపయోగం qemu_in_coroutine నిజానికి అది తప్పు అని తిరిగి వచ్చినప్పుడు నిజం తిరిగి వచ్చింది. ఇది కాల్‌కు దారితీసింది emscripten_yield, దాని పైన స్టాక్‌లో ఎవరూ లేరు emscripten_coroutine_next, స్టాక్ చాలా పైకి విప్పబడింది, కానీ లేదు setTimeout, నేను ఇప్పటికే చెప్పినట్లుగా, ప్రదర్శించబడలేదు.

జావాస్క్రిప్ట్ కోడ్ ఉత్పత్తి

మరియు ఇక్కడ, వాస్తవానికి, "ముక్కలు చేసిన మాంసాన్ని వెనక్కి తిప్పడం" అని వాగ్దానం చేయబడింది. నిజంగా కాదు. అయితే, మేము బ్రౌజర్‌లో Qemuని మరియు దానిలో Node.jsని అమలు చేస్తే, సహజంగానే, Qemuలో కోడ్ ఉత్పత్తి తర్వాత మేము పూర్తిగా తప్పు JavaScriptని పొందుతాము. కానీ ఇప్పటికీ, ఒక రకమైన రివర్స్ పరివర్తన.

ముందుగా, క్యూము ఎలా పనిచేస్తుందనే దాని గురించి కొంచెం. దయచేసి వెంటనే నన్ను క్షమించండి: నేను ప్రొఫెషనల్ Qemu డెవలపర్‌ని కాదు మరియు నా తీర్మానాలు కొన్ని చోట్ల తప్పుగా ఉండవచ్చు. వారు చెప్పినట్లుగా, "విద్యార్థి యొక్క అభిప్రాయం ఉపాధ్యాయుని అభిప్రాయం, పీనో యొక్క అక్షాంశం మరియు ఇంగితజ్ఞానంతో ఏకీభవించాల్సిన అవసరం లేదు." Qemu నిర్దిష్ట సంఖ్యలో మద్దతు ఉన్న అతిథి నిర్మాణాలను కలిగి ఉంది మరియు ప్రతిదానికి ఒక డైరెక్టరీ వంటిది ఉంటుంది target-i386. నిర్మించేటప్పుడు, మీరు అనేక అతిథి నిర్మాణాలకు మద్దతును పేర్కొనవచ్చు, కానీ ఫలితం అనేక బైనరీలు మాత్రమే. గెస్ట్ ఆర్కిటెక్చర్‌కు మద్దతు ఇచ్చే కోడ్, కొన్ని అంతర్గత Qemu ఆపరేషన్‌లను ఉత్పత్తి చేస్తుంది, TCG (చిన్న కోడ్ జనరేటర్) ఇప్పటికే హోస్ట్ ఆర్కిటెక్చర్ కోసం మెషిన్ కోడ్‌గా మారుతుంది. tcg డైరెక్టరీలో ఉన్న రీడ్‌మీ ఫైల్‌లో పేర్కొన్నట్లుగా, ఇది వాస్తవానికి సాధారణ C కంపైలర్‌లో భాగం, ఇది తరువాత JIT కోసం స్వీకరించబడింది. కాబట్టి, ఉదాహరణకు, ఈ డాక్యుమెంట్ పరంగా టార్గెట్ ఆర్కిటెక్చర్ ఇకపై అతిథి ఆర్కిటెక్చర్ కాదు, హోస్ట్ ఆర్కిటెక్చర్. ఏదో ఒక సమయంలో, మరొక భాగం కనిపించింది - చిన్న కోడ్ ఇంటర్‌ప్రెటర్ (టిసిఐ), ఇది నిర్దిష్ట హోస్ట్ ఆర్కిటెక్చర్ కోసం కోడ్ జెనరేటర్ లేనప్పుడు కోడ్ (దాదాపు అదే అంతర్గత కార్యకలాపాలు) అమలు చేయాలి. వాస్తవానికి, దాని డాక్యుమెంటేషన్ పేర్కొన్నట్లుగా, ఈ వ్యాఖ్యాత ఎల్లప్పుడూ JIT కోడ్ జెనరేటర్ వలె పని చేయకపోవచ్చు, వేగం పరంగా పరిమాణాత్మకంగా మాత్రమే కాకుండా గుణాత్మకంగా కూడా. అతని వివరణ పూర్తిగా సంబంధితమైనదని నాకు ఖచ్చితంగా తెలియకపోయినా.

మొదట నేను పూర్తి స్థాయి TCG బ్యాకెండ్‌ని రూపొందించడానికి ప్రయత్నించాను, కానీ సోర్స్ కోడ్‌లో మరియు బైట్‌కోడ్ సూచనల గురించి పూర్తిగా స్పష్టంగా వివరించనందున త్వరగా గందరగోళానికి గురయ్యాను, కాబట్టి నేను TCI ఇంటర్‌ప్రెటర్‌ను చుట్టాలని నిర్ణయించుకున్నాను. ఇది అనేక ప్రయోజనాలను ఇచ్చింది:

  • కోడ్ జెనరేటర్‌ను అమలు చేస్తున్నప్పుడు, మీరు సూచనల వివరణను చూడలేరు, కానీ వ్యాఖ్యాత కోడ్‌ను చూడవచ్చు
  • మీరు ఎదుర్కొన్న ప్రతి అనువాద బ్లాక్ కోసం కాదు, ఉదాహరణకు, వందవ అమలు తర్వాత మాత్రమే మీరు ఫంక్షన్‌లను రూపొందించగలరు
  • ఉత్పత్తి చేయబడిన కోడ్ మారితే (మరియు ఇది సాధ్యమయ్యేలా కనిపిస్తుంది, ప్యాచ్ అనే పదాన్ని కలిగి ఉన్న పేర్లతో కూడిన ఫంక్షన్‌ల ద్వారా అంచనా వేయడం), నేను రూపొందించిన JS కోడ్‌ని చెల్లుబాటు చేయవలసి ఉంటుంది, కానీ కనీసం దాని నుండి పునరుత్పత్తి చేయడానికి నేను ఏదైనా కలిగి ఉంటాను.

మూడవ పాయింట్‌కి సంబంధించి, కోడ్‌ని మొదటిసారి అమలు చేసిన తర్వాత ప్యాచింగ్ సాధ్యమవుతుందని నాకు ఖచ్చితంగా తెలియదు, కానీ మొదటి రెండు పాయింట్‌లు సరిపోతాయి.

మొదట్లో, అసలైన బైట్‌కోడ్ సూచనల చిరునామాలో పెద్ద స్విచ్ రూపంలో కోడ్ రూపొందించబడింది, కానీ తర్వాత, ఎమ్‌స్క్రిప్టెన్ గురించిన కథనాన్ని గుర్తుంచుకోవడం, రూపొందించిన JS యొక్క ఆప్టిమైజేషన్ మరియు రీలూపింగ్ గురించి, నేను మరింత మానవ కోడ్‌ను రూపొందించాలని నిర్ణయించుకున్నాను, ముఖ్యంగా అనుభవపూర్వకంగా ఇది అనువాద బ్లాక్‌లోని ఏకైక ప్రవేశ స్థానం దాని ప్రారంభం మాత్రమే అని తేలింది. ఇంకేముంది చెప్పలేదు, కొంతకాలం తర్వాత మేము ఒక కోడ్ జెనరేటర్‌ని కలిగి ఉన్నాము, అది ifsతో కోడ్‌ను రూపొందించింది (లూప్‌లు లేకుండా). కానీ దురదృష్టం, అది క్రాష్ అయ్యింది, సూచనల పొడవు కొంత తప్పు అని సందేశం ఇచ్చింది. అంతేకాకుండా, ఈ పునరావృత స్థాయిలో చివరి సూచన brcond. సరే, నేను రికర్సివ్ కాల్‌కు ముందు మరియు తర్వాత ఈ సూచనల జనరేషన్‌కి ఒకేలా చెక్‌ను జోడిస్తాను మరియు... వాటిలో ఒకటి కూడా అమలు చేయబడలేదు, కానీ నిర్ధారిత స్విచ్ తర్వాత అవి ఇప్పటికీ విఫలమయ్యాయి. చివరికి, ఉత్పత్తి చేయబడిన కోడ్‌ను అధ్యయనం చేసిన తర్వాత, స్విచ్ తర్వాత, ప్రస్తుత సూచనలకు సంబంధించిన పాయింటర్ స్టాక్ నుండి మళ్లీ లోడ్ చేయబడిందని మరియు బహుశా రూపొందించబడిన జావాస్క్రిప్ట్ కోడ్ ద్వారా భర్తీ చేయబడుతుందని నేను గ్రహించాను. మరియు అది మారినది. బఫర్‌ను ఒక మెగాబైట్ నుండి పదికి పెంచడం దేనికీ దారితీయదు మరియు కోడ్ జనరేటర్ సర్కిల్‌లలో నడుస్తోందని స్పష్టమైంది. మేము ప్రస్తుత TB యొక్క సరిహద్దులను దాటి వెళ్లలేదని మేము తనిఖీ చేయాలి మరియు ఒకవేళ అలా చేస్తే, తదుపరి TB యొక్క చిరునామాను మైనస్ గుర్తుతో జారీ చేయండి, తద్వారా మేము అమలును కొనసాగించగలము. అదనంగా, ఇది “ఈ బైట్‌కోడ్ ముక్క మారినట్లయితే ఉత్పత్తి చేయబడిన ఏ ఫంక్షన్‌లు చెల్లుబాటు కాకూడదు?” అనే సమస్యను పరిష్కరిస్తుంది. — ఈ అనువాద బ్లాక్‌కి సంబంధించిన ఫంక్షన్ మాత్రమే చెల్లుబాటు కాదు. మార్గం ద్వారా, నేను 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"]

తీర్మానం

కాబట్టి, పని ఇంకా పూర్తి కాలేదు, కానీ ఈ దీర్ఘకాలిక నిర్మాణాన్ని రహస్యంగా పరిపూర్ణంగా తీసుకురావడంలో నేను అలసిపోయాను. అందువల్ల, ప్రస్తుతానికి నా వద్ద ఉన్న వాటిని ప్రచురించాలని నిర్ణయించుకున్నాను. కోడ్ ప్రదేశాలలో కొంచెం భయానకంగా ఉంది, ఎందుకంటే ఇది ఒక ప్రయోగం, మరియు ఏమి చేయాలో ముందుగానే స్పష్టంగా తెలియదు. బహుశా, Qemu యొక్క మరికొంత ఆధునిక వెర్షన్ పైన సాధారణ అటామిక్ కమిట్‌లను జారీ చేయడం విలువైనదే. ఈ సమయంలో, గీతలో బ్లాగ్ ఆకృతిలో ఒక థ్రెడ్ ఉంది: కనీసం ఏదో ఒకవిధంగా ఆమోదించబడిన ప్రతి “స్థాయి” కోసం, రష్యన్‌లో వివరణాత్మక వ్యాఖ్యానం జోడించబడింది. వాస్తవానికి, ఈ కథనం చాలా వరకు ముగింపు యొక్క పునశ్చరణ git log.

మీరు అన్నింటినీ ప్రయత్నించవచ్చు ఇక్కడ (ట్రాఫిక్ జాగ్రత్త).

ఇప్పటికే ఏమి పని చేస్తోంది:

  • x86 వర్చువల్ ప్రాసెసర్ రన్ అవుతోంది
  • మెషిన్ కోడ్ నుండి జావాస్క్రిప్ట్ వరకు JIT కోడ్ జెనరేటర్ యొక్క వర్కింగ్ ప్రోటోటైప్ ఉంది
  • ఇతర 32-బిట్ గెస్ట్ ఆర్కిటెక్చర్‌లను అసెంబ్లింగ్ చేయడానికి ఒక టెంప్లేట్ ఉంది: ప్రస్తుతం మీరు లోడ్ అవుతున్న దశలో బ్రౌజర్‌లో MIPS ఆర్కిటెక్చర్ ఫ్రీజింగ్ కోసం Linuxని మెచ్చుకోవచ్చు.

ఇంకా ఏం చేయగలవు

  • ఎమ్యులేషన్‌ని వేగవంతం చేయండి. JIT మోడ్‌లో కూడా ఇది వర్చువల్ x86 కంటే నెమ్మదిగా నడుస్తుంది (కానీ చాలా ఎమ్యులేటెడ్ హార్డ్‌వేర్ మరియు ఆర్కిటెక్చర్‌లతో మొత్తం Qemu ఉంది)
  • ఒక సాధారణ ఇంటర్‌ఫేస్‌ను రూపొందించడానికి - స్పష్టంగా చెప్పాలంటే, నేను మంచి వెబ్ డెవలపర్‌ని కాదు, కాబట్టి ప్రస్తుతానికి నేను ప్రామాణికమైన ఎమ్‌స్క్రిప్టెన్ షెల్‌ను నేను చేయగలిగినంతగా రీమేడ్ చేసాను
  • మరింత క్లిష్టమైన Qemu ఫంక్షన్‌లను ప్రారంభించేందుకు ప్రయత్నించండి - నెట్‌వర్కింగ్, VM మైగ్రేషన్, మొదలైనవి.
  • యుపిడి: Qemu మరియు ఇతర ప్రాజెక్ట్‌ల మునుపటి పోర్టర్‌లు చేసినట్లుగా, మీరు మీ కొన్ని డెవలప్‌మెంట్‌లు మరియు బగ్ నివేదికలను ఎమ్‌స్క్రిప్టెన్ అప్‌స్ట్రీమ్‌కు సమర్పించాలి. నా టాస్క్‌లో భాగంగా ఎమ్‌స్క్రిప్టెన్‌కి వారి సహకారాన్ని పరోక్షంగా ఉపయోగించగలిగినందుకు వారికి ధన్యవాదాలు.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి