قرارداد تماسگرفتن
قرارداد فراخوانی (به انگلیسی: calling convention) در علوم رایانه، یک طرح سطح پیادهسازی (یعنی سطح پایین) برای تعیین آنکه زیرروالها چگونه پارامترها را از فراخوان کننده دریافت میکنند و نیز چگونه یک نتیجه را بازمیگردانند، است. این تفاوتهای پیادهسازی شامل انواع قرارگیری محل پارامتر، مقادیر بازگشتی، آدرسهای بازگشتی، و پیوند دامنه (مثلا در ثبات، پشته، یا حافظه و غیره) میشود، همچنین شامل اینکه چگونه فعالیتهای آمادهشدن برای یک فراخوانی با تابع، و بازیابی محیط پس از آن، بین فراخواننده و فراخوان شده تقسیم بشود، است.
قراردادهای فراخوانی ممکن است به یک استراتژی ارزیابی زبان برنامهنویسی خاص مربوط باشند، اما اغلب آنها بخشی از آن (یا برعکس) نیستند، زیرا استراتژی ارزیابی معمولاً در سطح انتزاعی تری (بالاتری) تعریف شدهاست و به عنوان بخشی از زبان دیده میشود به جای جزئیات پیادهسازی (سطح پایین) یک زبان خاص کامپایلر.
تغییرات
قراردادهای فراخوانی ممکن است در موارد زیر متفاوت باشند:
- پارامترها، مقادیر بازگشتی و آدرسهای برگشتی (در ثباتها، در پشته فراخوانی، ترکیبی از هر دو یا در سایر ساختارهای حافظه) قرار میگیرند
- منظور که در آن آرگومانهای واقعی برای پارامترهای رسمی منتقل میشوند (یا بخشی از یک آرگومان بزرگ یا پیچیده)
- چطور یک مقدار بازگشتی (احتمالاً طولانی یا پیچیده) از صدا زده شده به صدا زننده (در پشته، در یک ثبات یا درون پشته) تحویل داده میشود
- چگونگی وظیفه تنظیم و تمیز کردن پس از یک فراخوانی تابع بین صدا زننده و صدا زده شده تقسیم میشود
- این که چگونه فرادادههای توصیفکننده المانها پاس داده میشوند
- جایی که مقدار اشاره گربه قاب(frame pointer) قبلی ذخیره شدهاست، که برای بازگرداندن اشاره گر به قاب هنگامی که روال به پایان میرسد (در پشته قاب یا در برخی از ثباتها)
- چگونگی اختصاص دادن متغیرهای محلی میتواند بخشی از قرار داد فراخوانی باشد (زمانی که صدا زننده برای صدا زده شده اختصاص میدهد)
در برخی موارد، تفاوتها زیر نیز شامل موارد میشوند:
- قراردادهایی که در آن ثباتها میتوانند بهطور مستقیم توسط تابع صدا زده شده مورد استفاده قرار گیرد، بدون رزرو قبلی (در غیر این صورت به عنوان جزئیات ABI در نظر گرفته میشود)
- کدام ثباتها به عنوان تغییرپذیر(volatile) در نظر گرفته شود و اگر تغییرپذیر باشد باید توسط صدا زده شده بازگردانی نشود (اغلب به عنوان جزئیاتABI در نظر گرفته میشود)
تنوع کامپایلر
اگر چه برخی زبان در واقع ممکن است این تا حدی در مشخص زبان برنامهنویسی مشخصات (یا در برخی اجرای محوری)، پیادهسازیهای مختلف از جمله زبان (به عنوان مثال مختلف کامپایلر) بهطور معمول ممکن است هنوز هم استفاده از کنوانسیونهای مختلف فراخوانی، اغلب انتخاب. دلایل این کار عملکرد، سازگاری مکرر با توافقنامههای دیگر زبانهای محبوب (با توجه به یا بدون دلایل فنی)، و محدودیتها یا قراردادهای اعمال شده توسط «سیستم عاملهای مختلف» (ترکیبی از معماری CPU و سیستم عامل) است.
تنوع معماری
معماریهای CPU همیشه دارای بیش از یک قرارداد فراخوانی ممکن هستند. با تعداد زیادی از ثباتهای کلی و سایر ویژگیها، پتانسیل چند قرارداد فراخوانی زیاد است، اگر چه برخی معماری بهطور رسمی مشخص شدهاست که فقط از یک قرارداد فراخوانی استفاده کند که توسط معمار ارائه شدهاست.
x86 (32 بیتی)
معماری x86 از قراردادهای فراخوانی مختلفی استفاده میکند. با توجه به کم بودن ثباتها، قراردادهای فراخوانی x86 اغلب آرگومان را در پشته منتقل میکنند، در حالی که مقدار بازگشتی (یا اشاره گر به آن) در یک ثبات منتقل میشود. برخی از قراردادها از ثباتها برای چند پارامتر اول استفاده میکنند که ممکن است باعث بهبود عملکرد و سریعتر شدن برای زیرروال - برگ (یعنی روالهایی که با روشی دیگر فراخوانی نشده و نیازی به بازنگری ندارند) که مکرراً استفاده میشود شود.
مثال فراخوانی:
push EAX ; pass some register result
push byte[EBP+20] ; pass some memory variable (FASM/TASM syntax)
push 3 ; pass some constant
call calc ; the returned result is now in EAX
ساختار callee معمولی: (برخی یا همه (به جز ret) دستورالعملهای زیر ممکن است در روشهای ساده بهینهسازی شوند)
calc:
push EBP ; save old frame pointer
mov EBP,ESP ; get new frame pointer
sub ESP,localsize ; reserve place for locals
.
. ; perform calculations, leave result in EAX
.
mov ESP,EBP ; free space for locals
pop EBP ; restore old frame pointer
ret paramsize ; free parameter space and return
PowerPC
معماری PowerPC دارای تعداد زیادی از ثباتها است، بنابراین اکثر توابع میتوانند تمام آرگومنتها را در رجیسترها برای فراخوانیهای تک مرحله ای منتقل کنند. آرگومانهای اضافی در پشته منتقل میشوند و برای آرگومانهای رجیستر-پایه (مقادیری باید در رجیستر ذخیره شود) همیشه فضای مناسب در پشته تخصیص داده میشود تا فراخوانی تابع در موارد فراخوانی چند مرحله ای (بازگشتی) راحتتر شودو ثباتها باید ذخیره شوند. همچنین در توابع variadic مانند printf()
، جایی که پارامترهای تابع باید به صورت آرایه قابل دسترسی باشند. تنها یک قرارداد فراخوانی برای تمام زبانهای رویه ای استفاده میشود.
SPARC
معماری SPARC، بر خلاف اکثر معماریهای RISC، بر روی ثباتهای پنجره ای ساخته شدهاست. ۲۴ ثبات قابل دسترسی در هر پنجره ثبات وجود دارد: ۸ ثبات در (in) , 8 ثبات ثبات محلی هستند و ۸ تا ثبات (out).
ثبات "in" برای ارسال آرگومنتها به تابع مورد نظر استفاده میشود و هر آرگومان اضافی باید بر روی پشته قرار گیرد. با این حال، فضا همیشه توسط تابع صدا زده شده اختصاص داده میشود برای مدیریت سرریز احتمالی ثبات پنجره ای، متغیرهای محلی، و (در SPARC 32 بیتی) بازگشت ساختار بر اساس ارزش(by value). برای فراخوانی یک تابع، یک مکان که در آن آرگومنتها قرار میگیرد ثباتهای پنجره ای out هستند؛ هنگامی که تابع نامیده میشود، ثبات "out" تبدیل به ثبات"in" میشود و تابع فراخوانی شده به آرگومانهای موجود در ثبات "in" دسترسی دارد. هنگامی که تابع صدا زده شده تمام میشود، مقدار برگشتی را در اولین ثبات "in" قرار میدهد، که تبدیل به اولیت ثبات "out " میشود هنگام برگشتن از تابع فراخوانی شده.
ملاحظات پیادهسازی
این تغییرپذیری باید در هنگام ترکیب ماژولهای نوشته شده در چندین زبان یا هنگام فراخوانی سیستم عامل یا کتابخانهAPIها از یک زبان غیر از آن که در آن نوشته شده باشد، در نظر گرفته شود. در این موارد، باید مراقبتهای ویژه ای را برای هماهنگکردن قراردادهای فراخوانی مورد استفاده توسط صدا زننده و صدا زده شده انجام شود. حتی یک برنامه که از یک زبان برنامهنویسی استفاده میکند، میتواند از قراردادهای فراخوانی چندگانه یا انتخاب شده توسط کامپایلر، برای بهینهسازی کد یا برنامهنویس مشخص شدهاستفاده کند.
کد ریسه ای
کد ریسه ای همه مسئولیت را برای تنظیم و تمیز کردن کد پس از فراخوانی تابع بر روی کد صدا زده شده دارد. کد صدا زده شده چیزی نیست جز فهرست زیرروالهایی که باید فراخوانی شوند. این باعث میشود تمام تابع تنظیم و تمیز کردن در یک مکان - prolog و epilog تابع - و نه در بسیاری از مکانهایی که عملکرد نامیده میشود. این باعث میشود کد رشته کمترین قرارداد فراخوانی.
کد ریسه ای تمام آرگومانهای مربوط به پشته را منتقل میکند. تمام مقادیر برگشتی در پشته بازگشتهاند. این باعث میشود پیادهسازی ساده از قرارداد فراخوانیهایی که مقادیرهای بیشتری در ثباتها دارند، کندتر شود. با این حال، پیادهسازی کد ریسه ای که چندین مقدار پشته بالا را در رجیسترها ذخیره میکند - به ویژه، آدرس بازگشت - معمولاً سریع تر از قراردادهای فراخوانی است که همیشه آدرس بازگشت به پشته را push و popمیکند.
PL / I
قرارداد فراخوانی پیش فرض برای برنامههای نوشته شده در زبان PL / I تمام آرگومان را با مرجع منتقل میکند (by reference)، هرچند ممکن است قراردادهای دیگر مشخص شود. رفتار کردن با آرگومنتها برای کامپایلرها و سیستم عاملها مختلف متفاوت است، اما معمولاً آدرسهای آرگومنتها توسط یک لیست آرگومان در حافظه منتقل میشوند. یک آدرس نهایی یا پنهان ممکن است به یک منطقه منتقل شود تا حاوی مقدار بازگشتی باشد. به دلیل طیف گستردهای از دادههای پشتیبانی شده توسط PL / I، توصیفگر داده نیز ممکن است برای تعریف پاس داده شود، به عنوان مثال، طول رشتههای کاراکتر یا بیت، ابعاد و طول آرایهها. آرگومانهای ساختگی آرگومانهایی هستند که یا ثابت هستند یا با نوع ورودی ای که انتظار میرود متفاوتاند.
منابع
- ↑ براد رودریگز. "حرکت به جلو، قسمت 1: تصمیمگیری طراحی در هسته چهارم". نقل قول: "در 6809 یا Zilog Super8 DTC سریعتر از STC است."
- ↑ آنتور اورتل "سرعت روشهای مختلف ارسال تفسیر".
- ↑ متیو زالسکی. "YETI: یک مترجم پیشرفته قابل گسترش". فصل 4: طراحی و اجرای تفسیر کارآمد. نقل قول: "اگرچه مترجمان مستقیم-رشته شناخته شده دارای ویژگیهای پیش بینی شاخه ضعیف هستند … تاخیر تماس و بازگشت ممکن است بیشتر از یک حرکت غیر مستقیم باشد."