سؤال

سأقوم بتنفيذ جهاز افتراضي في x86 وأتساءل ما هو نوع التصميم الذي سيحقق أفضل النتائج.ما الذي يجب أن أركز عليه لإسقاط العصير؟سأقوم بتنفيذ الجهاز الظاهري بالكامل في تجميع x86.

ليس لدي الكثير من التعليمات ويمكنني اختيار شكلها.يتم عرض التعليمات مباشرة في بناء جملة Smalltalk على شكل كتل.أعطي تصميم التعليمات الذي كنت أفكر فيه:

^ ...       # return
^null     # return nothing
object    # address to object
... selector: ... # message pass (in this case arity:1 selector: #selector:)
var := ... # set
var # get

نوع VM الذي كنت أفكر فيه:

mov eax, [esi]
add esi, 2
mov ecx, eax
and eax, 0xff
and ecx, 0xff00 # *256
shr ecx, 5          # *8
jmp [ecx*4 + operations]
align 8:
    operations:
dd retnull
dd ret
# so on...
    retnull:          # jumps here at retnul
# ... retnull action
    ret:
# ... ret action
#etc.

لا تبدأ في التساؤل عن سبب حاجتي إلى تنفيذ جهاز افتراضي آخر.الإجراءات التفسيرية ليست أشياء مخزنة يمكنك التقاطها عندما تحتاج إليها.معظم الأجهزة الافتراضية التي تقترحها في مكان آخر تميل نحو قابلية النقل مع تكلفة الأداء.هدفي ليس قابلية النقل، هدفي هو الأداء.

السبب وراء الحاجة إلى هذا المترجم على الإطلاق هو أن كتل المحادثات الصغيرة لا يتم تفسيرها بنفس الطريقة:

A := B subclass: [
    def a:x [^ x*x]
    clmet b [...]
    def c [...]
    def d [...]
]

[ 2 < x ] whileTrue: [...]

(i isNeat) ifTrue: [...] ifFalse: [...]

List fromBlock: [
    "carrots"
    "apples"
    "oranges" toUpper
]

أحتاج إلى الفائدة الحقيقية القادمة من الإجراءات التفسيرية، وهي اختيار السياق الذي أقرأ فيه البرنامج.بالطبع، يجب على المترجم الجيد في معظم الأوقات أن يجمع الحالات الواضحة مثل:"ifTrue:ifFalse" أو "whileTrue:"، أو مثال القائمة.لا تختفي الحاجة إلى مترجم فوري لأنك قد تواجه دائمًا حالة لا يمكنك فيها التأكد من حصول الكتلة على العلاج الذي تتوقعه.

هل كانت مفيدة؟

المحلول

أرى أن هناك بعض الالتباس حول قابلية النقل هنا، لذا أشعر بأنني مضطر لتوضيح بعض الأمور.هذه آرائي المتواضعة، ولكم بالطبع الحرية في الاعتراض عليها.

أفترض أنك جئت عبر http://www.complang.tuwien.ac.at/forth/threading/ إذا كنت تفكر جديًا في كتابة جهاز افتراضي، فلن أسهب في الحديث عن التقنيات الموصوفة.

سبق ذكره، فإن استهداف جهاز افتراضي له بعض المزايا مثل تقليل حجم الكود، وتقليل تعقيد المترجم (غالبًا ما يُترجم إلى تجميع أسرع)، وقابلية النقل (لاحظ أن الهدف من الجهاز الافتراضي هو إمكانية نقل لغة, ، لذلك لا يهم إذا كان الجهاز الظاهري نفسه غير محمول).

بالنظر إلى الطبيعة الديناميكية لمثالك، فإن جهاز VM الخاص بك سيشبه ملف مترجم JIT أكثر من غيرها أكثر شعبية.لذا، على الرغم من أن إس. لوت قد أخطأ النقطة في هذه الحالة، إلا أن ذكره لفورث كان في محله تمامًا.إذا كنت سأصمم جهازًا افتراضيًا للغة ديناميكية للغاية، فسأقسم الترجمة الفورية إلى مرحلتين؛

  1. مرحلة الإنتاج التي تستشير تدفق AST عند الطلب وتحوله إلى شكل أكثر أهمية (على سبيل المثال، أخذ كتلة، تحديد ما إذا كان يجب تنفيذها على الفور أو تخزينها في مكان ما للتنفيذ لاحقًا) وربما تقدم أنواعًا جديدة من الرموز المميزة.تقوم بشكل أساسي باستعادة المعلومات الحساسة للسياق التي قد تضيع أثناء التحليل هنا.

  2. تقوم مرحلة المستهلك بجلب التدفق الذي تم إنشاؤه من 1 وتنفيذه بشكل أعمى مثل أي جهاز آخر.إذا قمت بذلك، يمكنك فقط دفع التدفق المخزن والانتهاء منه بدلاً من القفز بمؤشر التعليمات.

كما قلت، فإن مجرد محاكاة كيفية عمل المعالج اللعين بطريقة أخرى لا يحقق أي ديناميكية (أو أي ميزة أخرى تستحق العناء، مثل الأمان) التي تحتاجها.وإلا كنت ستكتب مترجمًا.

بالطبع، يمكنك إضافة تحسينات معقدة بشكل تعسفي في المرحلة 1.

نصائح أخرى

إذا كنت تريد شيئا سريعا حقا، حاول استخدام LLVM . يمكن أن تولد التعليمات البرمجية الأصلية لمعظم المعالجات من وصف برنامج على مستوى عال. يمكنك الذهاب إما مع لغة التجميع الخاصة بك أو توليد بنية llvm تخطي مرحلة التجميع، اعتمادا على ما تجده الأكثر ملاءمة.

وأنا لست متأكدا مما اذا كان هذا هو أفضل لمشكلتك، ولكن هذا بالتأكيد ما أود أن استخدام إذا كنت سوف تفعل بعض أداء تنفيذ الحرج من التعليمات البرمجية التي لا يمكن جمعها مع بقية البرنامج.

وجهة نظر المترجم يكون قابلية النقل، في أغلب الأحيان.أسرع طريقة يمكنني التفكير فيها هي إنشاء كود x86 في الذاكرة مباشرة، تمامًا كما يفعل مترجمو JIT، ولكن بعد ذلك، بالطبع، لن يكون لديك مترجم بعد الآن.لديك مترجم.

ومع ذلك، لست متأكدًا من أن كتابة المترجم في المجمّع ستمنحك أفضل أداء (إلا إذا كنت خبيرًا في المجمّع وكان مشروعك محدود النطاق للغاية).يمكن أن يساعدك استخدام لغة ذات مستوى أعلى في التركيز على خوارزميات أفضل، على سبيل المثال، للبحث عن الرموز واستراتيجيات تخصيص التسجيل.

ويمكنك تسريع روتين الإرسال الخاص بك مع تعليمات غير مشفر المقرر أن:

mov eax, [esi]
add esi, 4
add eax, pOpcodeTable
jmp eax

والذي ينبغي أن يكون لها فوق <4 دورات لكل ارسال على حدة المعالجة المركزية> بنتيوم 4.

وبالإضافة إلى ذلك، لأسباب تتعلق بالأداء فمن الأفضل لزيادة ESI (IP) في كل روتين بدائية لأن هناك احتمالات كبيرة بأن incrementation يمكن إرفاقها مع تعليمات أخرى:

mov eax, [esi]
add eax, pOpcodeTable
jmp eax

~ 1-2 cylces في سماء المنطقة.

وأود أن أسأل، لماذا إنشاء جهاز ظاهري مع التركيز على الأداء؟ لماذا لا مجرد كتابة التعليمات البرمجية إلى x86 مباشرة؟ لا شيء ربما يمكن أن يكون أسرع.

إذا كنت تريد جدا لغة تفسير سريع، والنظر في الرابع . تصميمها هو أنيق جدا وسهل جدا للنسخ.

إذا كنت لا ترغب JIT وهدفك ليس هو قابلية. أعتقد أنك قد تحصل على المهتمين في جوجل NativeClient المشروع. يفعلون ثابت المحلل ووضع الحماية وغيرها. فهي تسمح للمضيف تنفيذ تعليمات إلى x86 الخام.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top