نمایش میانی

نمایش میانی داده‌ساختار یا کدی است که کامپایلر یا ماشین مجازی کد منبع را پس از پردازش به آن تبدیل می‌کند و در نهایت به کد زبان مقصد که عموماً زبان اسمبلی است تبدیل می‌شود. می‌توان از نمایش میانی استفاده نکرد و کد کاربر را مستقیماً به نمایش مقصد تبدیل کرد اما از نمایش میانی برای ساده کردن برخی از تغییرات در کد، مانند ترجمه به زبان سطح پایین‌تر و بهینه‌سازی، استفاده می‌شود. نمایش میانی باید بتواند بدون از دست دادن اطلاعات کد مبدأ را توصیف کند و بتواند به راحتی به کد زبان مقصد تبدیل شود.
از آن جایی که نمایش میانی نمایش درونی کامپایلر است، تقریباً هیچ‌گاه کاربر برنامه‌ساز با آن ارتباطی ندارد (مگر در موارد خاص بهینه‌سازی)، پس نیازی نیست که نمایش میانی از ساخت‌های پیچیده که برنامه‌نویسی را برای کاربر برنامه‌ساز آسان می‌کنند بهره‌ای ببرد. این ساده‌سازی راه را برای بهینه‌سازی بهتر کد توسط کامپایلر باز می‌کند.
عملیات یک کامپایلر به دو بخش کلی عقب و جلو تقسیم می‌شود. در بخش جلو پس از پردازش کد کاربر، کد به نمایش میانی برده می‌شود و در بخش عقب کد از نمایش میانی به نمایش مقصد برده می‌شود. در صورتی که ویژگی‌های نمایش میانی مستقل از زبان منبع باشد، می‌توان از بخش عقب مشترکی برای کامپایلرهای مختلف استفاده کرد؛ بنابراین نمایش میانی خوب نمایش میانی‌ای است که مستقل از هر ویژگی نمایش مبدأ یا مقصد باشد؛ بنابراین اگر نیاز داشته باشیم کد n زبان متفاوت را به کد m زبان متفاوت ترجمه کنیم، بدون داشتن IR مناسب به mn کامپایلر نیاز داریم اما در صورت داشتن IR مناسب این امر با داشتن n بخش جلو و m بخش عقب قابل حل است.
نمایش میانی می‌تواند اشکال متفاوتی داشته باشد، اعم از داده‌ساختار موجود در رم یا کدی به زبان ماشینی مجازی که به مورد دوم زبان میانی نیز گفته می‌شود.

اهمیت نمایش میانی

وظیفهٔ ترجمهٔ زبان‌های متفاوت به یکدیگر وظیفه‌ای پیچیده‌است که با شکسته شدن به دو بخش ساده‌تر می‌شود و می‌تواند بهتر صورت پذیرد. این شکستن با اضافه شدن نمایش میانی به فرایند ترجمه و تبدیل ترجمه به دو مرحلهٔ ترجمه از زبان منبع به زبان میانی و ترجمه از زبان میانی به زبان مقصد صورت می‌پذیرد.
زبان میانی مناسب سطح معنایی‌ای نزدیک به اسمبلی دارد و باید بتواند با حفظ معنا در مسیر تبدیل کد زبان منبع به کد زبان مقصد (که عموماً کد اسمبلی است) قرار بگیرد. نمایش میانی‌ای که به درستی این خاصیت را داشته باشد می‌تواند به پودمانی کامپایلر کمک کند. در چنین شرایطی کامپایلر به دو بخش کلی عقب و جلو تقسیم می‌شود که کارشان از یکدیگر مجزا است. به واسطهٔ این جدایی در کامپایلر می‌توان علاوه بر بهره بردن از طراحی ساده‌تر و قابل نگه‌داری‌تر، از یک کد چند بار استفاده کرد و به این صورت هزینهٔ توسعهٔ کامپایلرها را کاهش داد. بخش جلو، که وظیفهٔ ترجمهٔ دقیق حافظ معنا از زبان مبدأ به نمایش میانی را بر عهده دارد، تنها به زبان منبع وابسته است و کاملاً از بخش عقب که وظیفهٔ ترجمه دقیق از نمایش میانی به زبان مقصد را برعهده دارد و به زبان مقصد وابسته است جداست؛ بنابراین می‌تواند برای زبان‌های مبدأ و مقصد متفاوت به صورت مجزا بخش‌های عقب و جلو را طراحی کرد و در نهایت به یکدیگر متصلشان کرد. از آنجایی که زبان مقصد عموماً زبان ماشین است، عموماً بخش عقب به زبان ماشین وابسته است.
با طراحی بخش‌های عقب متفاوت برای هر ماشین می‌توان زبان مبدأ را به صورت مجزا از طراحی داخلی ماشین بر روی ماشین‌های مختلف اجرا کرد و برای زبان‌های برنامه‌نویسی جدید تنها کافی است که با طراحی بخش جلو زبان مبدأ به نمایش میانی تبدیل شود و مابقی مسیر با بخش‌های عقبی که پیش از این وجود داشته‌است طی خواهد شد.
دوری نمایش میانی از زبان مبدأ و سادگی ذاتی آن زمینه را برای بهینه‌سازی کد فراهم می‌کند که نمایش میانی را به یکی از مهمترین پیش‌نیازهای بهینه‌سازی مناسب تبدیل می‌کند. با گسترش استفاده از یک نمایش میانی و استاندارد شدن آن زمینه برای بهبود پردازشگران شرکت‌های مختلف در سایهٔ این دانش فراهم می‌شود. نمایش میانی استاندارد می‌تواند به حل مشکل سازگاری نرم‌افزارها کمک کند. همچنین نمایش میانی استاندارد می‌تواند به بهبود روش‌های بهینه‌سازی کد با متمرکز کردن گروه‌های مختلف بر یک مسئلهٔ بهینه‌سازی بینجامد.
در حل مسائل توزیع شده نیز نمایش میانی به عنوان کدی مجزا از پلتفرم می‌تواند مورد استفاده قرار بگیرد و بر روی تمامی کامپیوترهای مجموعه اجرا شود.

نمایش میانی در برابر زبان میانی

در گونه‌های متفاوت کامپایلرها نمایش‌میانی چهره‌های متفاوتی به خود می‌گیرد. در صورتی که بخش عقب به عنوان یک زیربرنامه توسط بخش جلو صدا زده بشود، احتمالاً نمایش میانی یک درخت تجزیهٔ علامت‌گذاری شده به همراه جدول مربوطه خواهد بود؛ اما در صورتی که بخش عقب برنامه‌ای مجزا از بخش جلو باشد، احتمالاً نمایش میانی زبان ماشینی مجازی است و به آن زبان میانی گفته می‌شود. به دلیل جدایی بیشتر و بیشتر میان بخش عقب و بخش جلو، به مرور زمان عمدهٔ نمایش‌های میانی به صورت زبان میانی درآمده‌اند.

انواع مختلف نمایش میانی

نمایش میانی انواع مختلفی دارد که به دو دستهٔ کلی داده ساختارها و زبان میانی تقسیم می‌شوند. داده‌ساختارها عموماً به صورت گراف یا درخت به همراه توضیحات مربوطه که در جدول قرار دارند هستند. گراف معنا و گراف کنترل جریان از این دسته به‌شمار می‌آیند. زبان میانی نیز زبان یک ماشین فرضی است که عمدتاً به صورت پشته‌ای یا سه‌آدرسه است.
نمایش میانی می‌تواند سطوح مختلفی داشته باشد. در مثال زیر سه نمایش میانی در سه سطح متفاوت برای یک قطعه کد نمایش داده شده‌اند.

قطعات کد به ترتیب نشانگر سطح بالا، سطح میانی و سطح پایین هستند.
(COPY, 0, i) i := 0
(LABEL, L1) L1:
(JGE, i, n, L2) if i>= n goto L2
(INDEX, a, i, t0) t0 := a[i]
(ADD, j, 2, t1) t1 := j + 2
(INDEX, t0, t1, t2) t2 := t0[t1]
(COPY_TO_DEREF, j, t2) *t2 := j
(INCJUMP, i, di, L1) i += di, goto L1
(LABEL, L2) L2:

در سطح بالا ساختار زبان منبع به صورت صریح حفظ می‌شود، برنامهٔ منبع قابل بازسازی است، عملوندها اشیاء معنادار هستند، آرایه‌ها و محاسبات ساده نمی‌شوند، ثباتی در کار نیست و به زمان اجرای ماشین توجهی نمی‌شود.
(COPY, 0, i) i := 0
(LABEL, L1) L1:
(JGE, i, n, L2) if i>= n goto L2
(MUL, i, 80, t0) t0 := i * 80
(ADD, a, t0, t1) t1 := a + t0
(ADD, j, 2, t2) t2 := j + 2
(MUL, t2, 8, t3) t3 := t2 * 8
(ADD, t1, t3, t4) t4 := t1 + t3
(COPY_TO_DEREF, j, t4) *t4 := j
(ADD, i, di, i) i := i + di
(JUMP, L1) goto L1
(LABEL, L2) L2:

در سطح میانی نمایش از زبان و ماشین مستقل است، داده به اعداد صحیح و ممیز شناور فروکاسته می‌شود و بهینه‌سازی مستقل از معماری ماشین صورت می‌پذیرد.
(LDC, 0, r0) r0 := 0
(LOAD, j, r1) r1 := j
(LOAD, n, r2) r2 := n
(LOAD, di, r3) r3 := di
(LOAD, a, r4) r4 := a
(LABEL, L1) L1:
(JGE, r0, r2, L2) if r0>= r2 goto L2
(MUL, r0, 80, r5) r5 := r0 * 80
(ADD, r4, r5, r6) r6 := r4 + r5
(ADD, r1, 2, r7) r7 := r1 + 2
(MUL, r7, 8, r8) r8 := r7 * 8
(ADD, r6, r8, r9) r9 := r6 + r8
(TOFLOAT, r1, f0) f0 := tofloat r1
(STOREIND, f0, r9) *r9 := f0
(ADD, r0, r3, r0) r0 := r0 + r3
(JUMP, L1) goto L1
(LABEL, L2) L2:
در سطح پایین نمایش میانی به معماری ماشین نزدیک است و به معماری وابسته است، تفاوت اندکی با زبان مقصد دارد، درگیر مسائل زمان اجرا مانند مدیریت حافظه است و بهینه‌سازی وابسته به معماری انجام می‌دهد.