بازنمایی میانی چیست – یک اصل

در اینجا یک بیانیه مشکل وجود دارد: اگر مشخصات زبان برنامه نویسی داشته باشید و بخواهید برای آن یک کامپایلر بنویسید چه؟ اگر دو زبان دارید چطور؟ یا، سه؟ یا بیشتر؟
در حالی که هر زبان کاملاً با یکدیگر متفاوت است، همه آنها شباهت هایی در زیر هود خواهند داشت. مطمئنا، ممکن است تغییرات ساده ای مانند نحو اولیه و ساختار فایل وجود داشته باشد. و، ممکن است تغییرات پیچیدهتری مانند قوانین بالا بردن متغیر، طول عمر و جمعآوری زبالهها وجود داشته باشد، اما وقتی همه چیز از بین میرود، کد شما باید در نهایت به چیزی قابل خواندن توسط ماشین تبدیل شود.
#include
int main(void) {
const std::string message = "Hello, world!";
std::cout << message << std::endl;
return 0;
}
const message = 'Hello, world!';
console.log(message);
این دو قطعه ممکن است متفاوت به نظر برسند، و دلیل آن این است که آنها احتمالاً دورترین فاصله را از هم دارند (به غیر از یک زبان کاربردی) همانطور که می توانید در زبان های برنامه نویسی بدون وارد شدن به قسمت های عمیق به دست آورید. و با این حال، هدف یک IR ایده آل این است که اینها را در قالبی واحد، کامل و بدون ضرر یکی کند.
بنابراین، چرا سعی نکنید برای همه چیز یک کامپایلر عمومی بنویسید؟ اصلاح این ایده را می توان با استفاده از نمایش میانی یا IR اجرا کرد.
IR چیست؟
برخلاف تصور رایج، IR لزوماً یک زبان برنامهنویسی نیست، بلکه میتواند یک ساختار داده (درختهای نحوی انتزاعی) یا کدی باشد که در داخل توسط یک کامپایلر برای نمایش کد منبع واقعی استفاده میشود.
پروژه LLVM احتمالاً محبوب ترین نمونه از یک IR پرکاربرد است: LLVM IR.
LLVM از سه بخش اصلی تشکیل شده است: frontends، IR و backends. وظیفه فرانتاندها گرفتن کد منبع از زبان مادری و تبدیل آن به LLVM IR است. و کار پشتیبان های مختلف تولید کد ماشین از LLVM IR است که مجموعه دستورالعمل های مورد نیاز را هدف قرار می دهد.
چرا آی آر؟
بزرگترین و رایج ترین دلیل برای استفاده از IR، بهینه سازی های رایج است. اگر بتوان کد منبع زبان واقعی را به یک IR تبدیل کرد، مجموعه ای کلی از بهینه سازی ها را می توان روی آن IR انجام داد. این با روش سنتی که به معنای نوشتن بهینهسازیهای خاص زبان برای هر کامپایلر است، مخالف است.
دومین مزیت عمده IR این است که بسته به مورد استفاده، کامپایل کردن به موقع (JIT) و پیش از زمان (AOT) را امکان پذیر می کند.
در اینجا یک مثال از اصل LLVM IR برای یک برنامه ساده C “Hello, world” آورده شده است.
#include
int main(void) {
printf("Hello, world!\n");
return 0;
}
@.str = private unnamed_addr constant [15 x i8] c"Hello, world!\0A\00", align 1
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, ptr %1, align 4
%2 = call i32 (ptr, ...) @printf(ptr noundef @.str)
ret i32 0
}
declare dso_local i32 @printf(ptr noundef, ...) #1