قرارداد فراخوانی (به انگلیسی: calling convention) در علوم رایانه، یک طرح سطح پیادهسازی (یعنی سطح پایین) برای تعیین آنکه زیرروالها چگونه پارامترها را از فراخوان کننده دریافت میکنند و نیز چگونه یک نتیجه را بازمیگردانند، است. این تفاوتهای پیادهسازی شامل انواع قرارگیری محل پارامتر، مقادیر بازگشتی، آدرسهای بازگشتی، و پیوند دامنه (مثلا در ثبات، پشته، یا حافظه و غیره) میشود، همچنین شامل اینکه چگونه فعالیتهای آمادهشدن برای یک فراخوانی با تابع، و بازیابی محیط پس از آن، بین فراخواننده و فراخوان شده تقسیم بشود، است.
قراردادهای فراخوانی ممکن است به یک استراتژی ارزیابی زبان برنامهنویسی خاص مربوط باشند، اما اغلب آنها بخشی از آن (یا برعکس) نیستند، زیرا استراتژی ارزیابی معمولاً در سطح انتزاعی تری (بالاتری) تعریف شدهاست و به عنوان بخشی از زبان دیده میشود به جای جزئیات پیادهسازی (سطح پایین) یک زبان خاص کامپایلر.
قراردادهای فراخوانی ممکن است در موارد زیر متفاوت باشند:
در برخی موارد، تفاوتها زیر نیز شامل موارد میشوند:
اگر چه برخی زبان در واقع ممکن است این تا حدی در مشخص زبان برنامهنویسی مشخصات (یا در برخی اجرای محوری)، پیادهسازیهای مختلف از جمله زبان (به عنوان مثال مختلف کامپایلر) بهطور معمول ممکن است هنوز هم استفاده از کنوانسیونهای مختلف فراخوانی، اغلب انتخاب. دلایل این کار عملکرد، سازگاری مکرر با توافقنامههای دیگر زبانهای محبوب (با توجه به یا بدون دلایل فنی)، و محدودیتها یا قراردادهای اعمال شده توسط «سیستم عاملهای مختلف» (ترکیبی از معماری CPU و سیستم عامل) است.
معماریهای CPU همیشه دارای بیش از یک قرارداد فراخوانی ممکن هستند. با تعداد زیادی از ثباتهای کلی و سایر ویژگیها، پتانسیل چند قرارداد فراخوانی زیاد است، اگر چه برخی معماری بهطور رسمی مشخص شدهاست که فقط از یک قرارداد فراخوانی استفاده کند که توسط معمار ارائه شدهاست.
معماری 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 دارای تعداد زیادی از ثباتها است، بنابراین اکثر توابع میتوانند تمام آرگومنتها را در رجیسترها برای فراخوانیهای تک مرحله ای منتقل کنند. آرگومانهای اضافی در پشته منتقل میشوند و برای آرگومانهای رجیستر-پایه (مقادیری باید در رجیستر ذخیره شود) همیشه فضای مناسب در پشته تخصیص داده میشود تا فراخوانی تابع در موارد فراخوانی چند مرحله ای (بازگشتی) راحتتر شودو ثباتها باید ذخیره شوند. همچنین در توابع variadic مانند printf()
، جایی که پارامترهای تابع باید به صورت آرایه قابل دسترسی باشند. تنها یک قرارداد فراخوانی برای تمام زبانهای رویه ای استفاده میشود.
معماری SPARC، بر خلاف اکثر معماریهای RISC، بر روی ثباتهای پنجره ای ساخته شدهاست. ۲۴ ثبات قابل دسترسی در هر پنجره ثبات وجود دارد: ۸ ثبات در (in) , 8 ثبات ثبات محلی هستند و ۸ تا ثبات (out).
ثبات "in" برای ارسال آرگومنتها به تابع مورد نظر استفاده میشود و هر آرگومان اضافی باید بر روی پشته قرار گیرد. با این حال، فضا همیشه توسط تابع صدا زده شده اختصاص داده میشود برای مدیریت سرریز احتمالی ثبات پنجره ای، متغیرهای محلی، و (در SPARC 32 بیتی) بازگشت ساختار بر اساس ارزش(by value). برای فراخوانی یک تابع، یک مکان که در آن آرگومنتها قرار میگیرد ثباتهای پنجره ای out هستند؛ هنگامی که تابع نامیده میشود، ثبات "out" تبدیل به ثبات"in" میشود و تابع فراخوانی شده به آرگومانهای موجود در ثبات "in" دسترسی دارد. هنگامی که تابع صدا زده شده تمام میشود، مقدار برگشتی را در اولین ثبات "in" قرار میدهد، که تبدیل به اولیت ثبات "out " میشود هنگام برگشتن از تابع فراخوانی شده.
این تغییرپذیری باید در هنگام ترکیب ماژولهای نوشته شده در چندین زبان یا هنگام فراخوانی سیستم عامل یا کتابخانهAPIها از یک زبان غیر از آن که در آن نوشته شده باشد، در نظر گرفته شود. در این موارد، باید مراقبتهای ویژه ای را برای هماهنگکردن قراردادهای فراخوانی مورد استفاده توسط صدا زننده و صدا زده شده انجام شود. حتی یک برنامه که از یک زبان برنامهنویسی استفاده میکند، میتواند از قراردادهای فراخوانی چندگانه یا انتخاب شده توسط کامپایلر، برای بهینهسازی کد یا برنامهنویس مشخص شدهاستفاده کند.
کد ریسه ای همه مسئولیت را برای تنظیم و تمیز کردن کد پس از فراخوانی تابع بر روی کد صدا زده شده دارد. کد صدا زده شده چیزی نیست جز فهرست زیرروالهایی که باید فراخوانی شوند. این باعث میشود تمام تابع تنظیم و تمیز کردن در یک مکان - prolog و epilog تابع - و نه در بسیاری از مکانهایی که عملکرد نامیده میشود. این باعث میشود کد رشته کمترین قرارداد فراخوانی.
کد ریسه ای تمام آرگومانهای مربوط به پشته را منتقل میکند. تمام مقادیر برگشتی در پشته بازگشتهاند. این باعث میشود پیادهسازی ساده از قرارداد فراخوانیهایی که مقادیرهای بیشتری در ثباتها دارند، کندتر شود. با این حال، پیادهسازی کد ریسه ای که چندین مقدار پشته بالا را در رجیسترها ذخیره میکند - به ویژه، آدرس بازگشت - معمولاً سریع تر از قراردادهای فراخوانی است که همیشه آدرس بازگشت به پشته را push و popمیکند.[۱][۲][۳]
قرارداد فراخوانی پیش فرض برای برنامههای نوشته شده در زبان PL / I تمام آرگومان را با مرجع منتقل میکند (by reference)، هرچند ممکن است قراردادهای دیگر مشخص شود. رفتار کردن با آرگومنتها برای کامپایلرها و سیستم عاملها مختلف متفاوت است، اما معمولاً آدرسهای آرگومنتها توسط یک لیست آرگومان در حافظه منتقل میشوند. یک آدرس نهایی یا پنهان ممکن است به یک منطقه منتقل شود تا حاوی مقدار بازگشتی باشد. به دلیل طیف گستردهای از دادههای پشتیبانی شده توسط PL / I، توصیفگر داده نیز ممکن است برای تعریف پاس داده شود، به عنوان مثال، طول رشتههای کاراکتر یا بیت، ابعاد و طول آرایهها. آرگومانهای ساختگی آرگومانهایی هستند که یا ثابت هستند یا با نوع ورودی ای که انتظار میرود متفاوتاند.