وقتی یک تابع فراخوانی میشود چه اتفاقی میافتد
فراخوانی یک Procedure (یک تابع یا زیرروال) به بیش از صرفاً پریدن به کد آن نیاز دارد. پردازنده باید به خاطر بسپارد بعداً کجا برگردد، آرگومانهای ورودی را منتقل کند، یک نتیجه دریافت کند، و مطمئن شود مقادیر رجیستر کد فراخواننده بهطور نامحسوس توسط رویه فراخواندهشده بازنویسی نمیشوند. RISC-V این کار را از طریق یک قرارداد مشخصشده انجام میدهد، نه سحر و جادوی سختافزاری خاص.
دستور Jump-and-Link
RISC-V یک دستور اختصاصی برای فراخوانی رویهها فراهم میکند که بهطور خودکار موقعیت بازگشت را ذخیره میکند:
jal ra, ProcedureAddressاین دستور اجرا را به ProcedureAddress میپراند و همزمان آدرس دستور بعدی را در یک رجیستر مشخص، معمولاً به نام ra (return address)، ذخیره میکند. وقتی رویه تمام میشود، از این آدرس ذخیرهشده برای پریدن به عقب استفاده میکند:
jalr x0, 0(ra)این کار کنترل را دقیقاً به دستوری که پس از فراخوانی اصلی میآید بازمیگرداند.
قراردادهای رجیستر برای انتقال داده
برای قابلپیشبینی نگهداشتن فراخوانی رویهها در کامپایلرها و برنامههای مختلف، RISC-V از یک Register Convention ثابت پیروی میکند:
- یک بازه مشخص از رجیسترها برای انتقال
Argument Valuesبه یک رویه رزرو شده است. - یک رجیستر مشخص برای بازگرداندن
Result Valueبه فراخواننده رزرو شده است. - برخی رجیسترها
Saved Registersنامیده میشوند، که یک رویه فراخواندهشده باید در صورت استفاده از آنها، پیش از بازگشت آنها را حفظ و بازیابی کند. - سایر رجیسترها
Temporary Registersهستند، که یک رویه فراخواندهشده آزاد است بدون هیچ تعهدی برای بازیابی مقدار اصلیشان، آنها را بازنویسی کند.
پشته: مدیریت فراخوانیهای تودرتو و بازگشتی
یک مجموعه واحد از رجیسترها زمانی که رویهها رویههای دیگر را فراخوانی میکنند، یا خودشان را بهصورت بازگشتی فراخوانی میکنند، کافی نیست. برای مدیریت این موضوع، RISC-V از ناحیهای از حافظه به نام Stack استفاده میکند، که از طریق یک رجیستر اختصاصی Stack Pointer مدیریت میشود.
پیش از اینکه یک رویه یک رجیستر ذخیرهشدهای که نیاز به استفاده مجدد از آن دارد را بازنویسی کند، مقدار فعلی آن رجیستر را روی پشته push میکند؛ پیش از بازگشت، آن مقدار را با pop کردن بازیابی میکند:
رزرو فضای پشته:
addi sp, sp, -8
ذخیره مقدار یک رجیستر:
sd s0, 0(sp)
بازیابی آن بعداً:
ld s0, 0(sp)
آزادسازی فضای پشته:
addi sp, sp, 8این مکانیزم اجازه میدهد عمق دلخواهی از فراخوانیهای تودرتو یا بازگشتی وجود داشته باشد، چون هر فراخوانی ناحیه خصوصی خودش از حافظه پشته را برای هر چیزی که نیاز به حفظ آن دارد دریافت میکند.
نمایش متن: کاراکترها و کدگذاری
فراتر از اعداد، کامپیوترها همچنین نیاز دارند متن قابلفهم برای انسان را نمایش دهند. هر کاراکتر منفرد با استفاده از یک استاندارد کدگذاری عددی ذخیره میشود، رایجترین آنها ASCII برای متن پایه لاتین یا Unicode برای طیف بسیار گستردهتری از خطها و نمادهاست.
یک دنباله از کاراکترها که متن را تشکیل میدهند String نامیده میشود، و معمولاً بهصورت بایتهای پیاپی در حافظه ذخیره میشود، یک بایت برای هر کاراکتر، که اغلب با یک مقدار پایاندهنده خاص که مشخص میکند رشته کجا تمام میشود، خاتمه مییابد.
چرا این قراردادها اهمیت دارند
چون استفاده از رجیستر و مدیریت پشته از یک قرارداد سختگیرانه و توافقشده پیروی میکند بهجای اینکه هر برنامه بهصورت موردی تصمیم بگیرد، کدی که با کامپایلرهای مختلف کامپایل شده، یا به زبانهای مختلف نوشته شده، میتواند رویههای نوشتهشده توسط یکدیگر را بهدرستی و با امنیت فراخوانی کند — ویژگیای ضروری برای ساخت سیستمهای نرمافزاری بزرگ از اجزای توسعهیافته بهطور مستقل.