سؤال

في C ++، يتم تخصيص المتغيرات المحلية دائما على المكدس. تعد المكدس جزءا من الذاكرة المسموح بها التي يمكن أن يشغلها طلبك. يتم الاحتفاظ بهذه الذاكرة في ذاكرة الوصول العشوائي الخاصة بك (إن لم تكن مبادلة على القرص). الآن، هل يقوم برنامج التحويل البرمجي C ++ بإنشاء رمز المجمع الذي يخزن المتغيرات المحلية على المكدس؟

خذ، على سبيل المثال، الرمز البسيط التالي:

int foo( int n ) {
   return ++n;
}

في رمز مجمع MIPS، قد يبدو هذا مثل هذا:

foo:
addi $v0, $a0, 1
jr $ra

كما ترون، لم أكن بحاجة لاستخدام المكدس على الإطلاق ل n. هل يتعرف مترجم C ++ على ذلك، واستخدام سجلات وحدة المعالجة المركزية مباشرة؟

يحرر: نجاح باهر، شكرا جزيلا إجاباتك الفورية والاستزال عليها تقريبا! يجب أن تكون هيئة وظيفة فو بالطبع return ++n;, ، ليس return n++;. :)

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

المحلول

إخلاء المسئولية: لا أعرف mips، لكنني أعرف بعض X86، وأعتقد أن المبدأ يجب أن يكون هو نفسه ..

في اتفاقية دعوة الوظيفة المعتادة، سيدفع المحول البرمجي قيمة n على المكدس لتمريرها إلى الوظيفة foo. وبعد ومع ذلك، هناك fastcall الاتفاقية التي يمكنك استخدامها لإخبار GCC بتمرير القيمة من خلال السجلات بدلا من ذلك. (MSVC لديها أيضا هذا الخيار، لكنني لست متأكدا من بناء جملةها.)

test.cpp:

int foo1 (int n) { return ++n; }
int foo2 (int n) __attribute__((fastcall));
int foo2 (int n) {
    return ++n;
}

تجميع ما سبق مع g++ -O3 -fomit-frame-pointer -c test.cpp, ، أحصل ل foo1:

mov eax,DWORD PTR [esp+0x4]
add eax,0x1
ret

كما ترون، يقرأ في القيمة من المكدس.

وهنا foo2:

lea eax,[ecx+0x1]
ret

الآن يستغرق القيمة مباشرة من السجل.

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

إخلاء المسئولية 2: أنا لا أقول أنه يجب عليك باستمرار تخمين المحول البرمجي. ربما ليس عمليا وضروريا في معظم الحالات. ولكن لا تفترض أنها تنتج رمز مثالي.

تحرير 1: إذا كنت تتحدث عن المتغيرات المحلية العادية (وليس الوسائط الوظيفية)، فستكون التحويل البرمجي في تسجيلهم في السجلات أو على المكدس لأنه يرى مناسبا.

تحرير 2: يبدو أن اتفاقية الدعوة خاصة بالهندسة المعمارية، وسوف تمر MIPs الحجج الأربعة الأولى على المكدس، حيث ذكر ريتشارد بينينجتون في إجابته. لذلك في حالتك، لا تضطر إلى تحديد السمة الإضافية (والتي هي في الواقع سمة محددة X86.)

نصائح أخرى

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

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

المترجم ليس غبيا. ؛)

نعم، جيد، تحسين C / C ++ سوف يحسن ذلك. وحتى كثير أكثر: انظر هنا: مسح Felix Von Leitners Compiler.

لن يضع برنامج التحويل البرمجي C / C ++ عادي كل متغير على المكدس على أي حال. المشكلة مع الخاص بك foo() يمكن أن تكون الوظيفة أن المتغير قد يتم تمريره عبر المكدس إلى الوظيفة (يحدد ABI من نظامك (الأجهزة / نظام التشغيل) ذلك).

مع ج register الكلمة الأساسية يمكنك إعطاء المحول البرمجي ملحوظة أنه من المحتمل أن يكون من الجيد تخزين متغير في السجل. عينة:

register int x = 10;

ولكن تذكر: مترجم مجاني لا تخزن x في سجل إذا أراد ذلك!

الجواب نعم، ربما. ذلك يعتمد على المحول البرمجي، مستوى التحسين، ومعالج الهدف.

في حالة MIPS، يتم تمرير المعلمات الأربعة الأولى، إذا تم إرجاع السجلات، يتم إرجاع قيمة الإرجاع في سجل. لذلك ليس لديك مثالك أي شرط لتخصيص أي شيء على المكدس.

في الواقع، الحقيقة غريبة من الخيال. في حالتك، يتم إرجاع المعلمة دون تغيير: القيمة التي تم إرجاعها هي أن N قبل المشغل ++:

foo:
    .frame  $sp,0,$ra
    .mask   0x00000000,0
    .fmask  0x00000000,0

    addu    $2, $zero, $4
    jr      $ra
    nop

منذ مثالك foo الوظيفة هي وظيفة الهوية (فقط إرجاع حجة أنها)، فإن برنامج التحويل البرمجي C ++ الخاص بي (VS 2008) يزيل تماما مكالمة الوظيفة هذه. إذا قمت بتغييره إلى:

int foo( int n ) {
   return ++n;
}

الترجمة تستقل هذا مع

lea edx, [eax+1] 

نعم، يتم استخدام السجلات في C ++. يحتوي MDR (سجلات بيانات الذاكرة) على البيانات التي يتم جلبها وتخزينها. على سبيل المثال، لاسترداد محتويات الخلية 123، سنقوم بتحميل القيمة 123 (في ثنائي) في مارس وأداء عملية جلب. عندما يتم العملية، ستكون نسخة من محتويات الخلية 123 في MDR. لتخزين القيمة 98 في الخلية 4، نقوم بتحميل 4 في Mar و 98 في MDR وأداء متجر. عند اكتمال العملية، سيتم تعيين محتويات الخلية 4 إلى 98، عن طريق التخلص من أي شيء كان هناك سابقا. سجلات البيانات والعنوان تعمل معها لتحقيق ذلك. في C ++ أيضا، عندما نقوم بتهيئة Var بقيمة أو اسأل قيمتها، تحدث نفس الظواهر.

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

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