مجموعه دستورالعملها فشرده

این مقاله در حال ترجمه از ویکی انگلیسی است

لطفا حذف نشود.

مجموعه‌دستورالعمل فشرده (یا به اختصار «دستورهای فشرده») به روشی در طراحی معماری مجموعه‌دستور یک ریزپردازنده گفته می‌شود که هدفش کوتاه‌تر کردن قالب دستورهاست تا کدِ برنامه فضای کمتری بگیرد. در عمل، در بسیاری از پردازنده‌ها دستورهای فشرده معمولاً ۱۶‌بیتی‌اند، در حالی که همان پردازنده به طور معمول دستورهای ۳۲‌بیتی را اجرا می‌کند. این مجموعهٔ ۱۶‌بیتی یک ISA جداگانه نیست؛ بلکه فقط بخشی از مجموعه‌دستور کاملِ ۳۲‌بیتی به شمار می‌آید. کوچک‌تر شدن دستورها البته با محدودیت‌هایی همراه است: معمولاً تعداد دستورهای قابل استفاده کمتر می‌شود، هر دستور اغلب به‌جای سه عملوند فقط دو عملوند دارد، و انتخاب ثبات‌های قابل دسترس هم محدودتر می‌شود.

این ایده را هیتاچی در ابتدا برای بالا بردن چگالی کُد در طراحی پردازنده‌های RISC خانوادهٔ SuperH مطرح کرد؛ زمانی که در نسخهٔ SH-5 قرار بود دستورها از قالب ۱۶‌بیتی به ۳۲‌بیتی منتقل شوند. طراحی جدید دو مجموعه‌دستور داشت: یکی مجموعه‌دستور کاملِ ۳۲‌بیتی و دیگری یک مجموعهٔ کوچک‌ترِ ۱۶‌بیتی با نام SHcompact که باعث می‌شد برنامه‌ها با حافظهٔ کمتری اجرا شوند. امروز دیگر کمبود حافظه مثل گذشته مسئلهٔ اصلی نیست، اما مزیت مهمِ فعلی این روش این است که مراجعه به حافظهٔ اصلی را کمتر می‌کند و در نتیجه مصرف انرژی—به‌ویژه در دستگاه‌های همراه—کاهش پیدا می‌کند.

پتنت‌های هیتاچی در این زمینه بعداً توسط شرکت **[[Arm Ltd]].** برای پردازنده‌هایش لایسنس شد و آرم این ایده را با نام **Thumb** به کار گرفت. روش‌های مشابهی هم در معماری‌های دیگر دیده می‌شود؛ مثل **[[MIPS16e]]** در خانوادهٔ MIPS و دستورهای **VLE** در PowerPC. از آن‌جا که پتنت‌های اولیه حالا منقضی شده‌اند، این مفهوم در طراحی‌های مدرنِ مختلفی به چشم می‌خورد؛ از جمله در **[[رایانه کم دستور|RISC-V]]** که از همان ابتدا با در نظر گرفتن امکان استفاده از دستورهای فشرده طراحی شده است. با رواج پردازنده‌های ۶۴بیتی، این اصطلاح کمتر از گذشته استفاده می‌شود: چون در این پردازنده‌ها معمولاً قالب دستورها همچنان ۳۲بیتی است و از نظر فنی می‌توان آن را نوعی ISA فشرده دانست، اما چون این طراحی‌ها غالباً نسخهٔ گسترش‌یافتهٔ همان ISAهای ۳۲بیتیِ قبلی در یک خانوادهٔ پردازشی هستند، عملاً «فشرده‌سازی» مستقلی در کار نیست.

## مفهوم
ریزپردازنده‌ها دستورهایشان را به صورت یک رشته از بیت‌ها کُد می‌کنند؛ این رشته معمولاً به بخش‌های ۸‌بیتی (یعنی بایت) تقسیم می‌شود. برای نمونه، در پردازندهٔ **MOS 6502**، دستور `ADC` جمعِ دودویی را بین یک عملوند و مقداری که از قبل در ثبات انباره (Accumulator) قرار دارد انجام می‌دهد. پردازنده می‌تواند این عملوند را از جاهای مختلفی بردارد: ممکن است در حافظهٔ اصلی باشد، یا در ناحیهٔ ویژهٔ «صفحهٔ صفر» ذخیره شده باشد، یا حتی یک مقدار ثابتِ صریح مثل «۱۰» باشد. هر کدام از این حالت‌ها در 6502 با یک کُد دستور ۸‌بیتیِ جداگانه (opcode) نمایش داده می‌شد؛ مثلاً اگر بخواهیم مقدار ثابت ۱۰ را به انباره اضافه کنیم، این دستور در حافظه به صورت `69$ 0A$` ذخیره می‌شود که در آن `0A``$` نمایش شانزدهیِ عدد ۱۰ است. اما اگر قرار باشد مقدارِ موجود در آدرس `4400``$` حافظهٔ اصلی به انباره اضافه شود، کُد دستور به شکل `$6D $00 $44` خواهد بود؛ یعنی آدرس به صورت «کم‌ارزش‌اول» (little-endian) بعد از opcode می‌آید.

توجه کنید که دستور دوم سه بایت لازم دارد، چون آدرس حافظه در آن ۱۶‌بیتی است. بسته به نوع دستور، ممکن است کُد آن یک بایت باشد یا دو بایت یا سه بایت. به چنین ساختاری امروز «مجموعه‌دستورالعمل با طول متغیر» (Variable-length instruction set) می‌گویند؛ هرچند در آن زمان این اصطلاح چندان رایج نبود، چون بیشتر پردازنده‌ها—از مین‌فریم‌ها گرفته تا مینی‌کامپیوترها—به نوعی از همین ایده استفاده می‌کردند و عملاً نیازی به نام جداگانه احساس نمی‌شد. حتی تا اواخر دههٔ ۱۹۷۰ هم که ریزپردازنده‌ها کم‌کم از قالب‌های ۸‌بیتی به ۱۶‌بیتی مهاجرت می‌کردند، دستورهای با طول متغیر همچنان رایج بودند؛ برای مثال **[[Intel 8086]]** هنوز opcodeهای ۸‌بیتی داشت که بسته به شیوهٔ آدرس‌دهی می‌توانستند بدون بایت اضافه بیایند یا تا پنج بایت دیگر بعد از خودشان داشته باشند.

با حرکت صنعت به سمت سیستم‌های ۳۲‌بیتی—به‌خصوص از وقتی که ایدهٔ **[[RISC]]** کم‌کم تبدیل به جریان اصلی طراحی پردازنده‌ها شد—دستورهای با طول متغیر آرام‌آرام کنار رفتند. برای نمونه، در معماری **[[MIPS]]** همهٔ دستورها دقیقاً یک مقدار ۳۲‌بیتی دارند: شش بیتِ بالایی نقش opcode را بازی می‌کند و ۲۶ بیت باقی‌مانده، بسته به نوع دستور، برای نمایش عملوندها و مجموعهٔ نسبتاً محدودِ حالت‌های آدرس‌دهی به کار می‌رود. بیشتر طراحی‌های RISC هم کم‌وبیش همین شکل را دنبال می‌کنند. ثابت‌طول کردن دستورها یکی از ایده‌های کلیدیِ پشتِ کاراییِ نسل‌های اولیهٔ RISC بود؛ چون در معماری‌های طول‌متغیر، خواندن یک دستور ممکن بود بین یک تا شش چرخهٔ حافظه زمان ببرد و لازم بود مدارها و ارتباطات کنترلیِ بیشتری وجود داشته باشد تا پردازنده قبل از آماده شدنِ همهٔ بایت‌های دستور، سراغ اجرای آن نرود. در طراحی‌های RISC معمولاً خواندن و رمزگشایی دستور در یک چرخه انجام می‌شود و همین موضوع کارِ رمزگشا را بسیار ساده‌تر می‌کند. صرفه‌جویی‌ای که از حذف آن مدارهای قفل و بست (interlock) به دست می‌آید، معمولاً صرف اضافه کردن منطق‌های مفیدتر یا افزایش تعداد ثبات‌ها می‌شود؛ چیزهایی که مستقیماً روی کارایی اثر می‌گذارند.

## تراکم کد
نقطه‌ضعف رویکرد RISC این است که خیلی از دستورها واقعاً به چهار بایت فضا احتیاج ندارند. مثلاً دستور **[[Logical Shift Left]]** فقط بیت‌های یک ثبات را به سمت چپ جابه‌جا می‌کند. در پردازندهٔ 6502 که عملاً فقط یک ثبات محاسباتیِ اصلی یعنی A دارد، این دستور صرفاً با همان opcode هشت‌بیتیِ خودش (`$06`) نمایش داده می‌شود. در پردازنده‌هایی که ثبات‌های بیشتری دارند، معمولاً کافی است در کنار opcode، شمارهٔ ثبات هم مشخص شود که آن هم چیزی حدود ۴ یا ۵ بیت جا می‌گیرد. در معماری MIPS، برای مثال، این دستور فقط به یک opcode شش‌بیتی و یک شمارهٔ ثبات پنج‌بیتی نیاز دارد؛ یعنی در مجموع ۱۱ بیت اطلاعات. با این حال، مثل بیشتر طراحی‌های RISC، همین دستور هم همچنان یک کلمهٔ کاملِ ۳۲‌بیتی را اشغال می‌کند. چون این نوع دستورهای ساده در برنامه‌ها زیاد تکرار می‌شوند، برنامه‌های RISC معمولاً نسبت به برنامهٔ مشابه روی یک پردازنده با دستورهای طول‌متغیر، حافظهٔ بیشتری مصرف می‌کنند.

در دههٔ ۱۹۸۰، وقتی مفهوم RISC تازه داشت شکل می‌گرفت، بزرگ‌تر شدن اندازهٔ برنامه‌ها یکی از شکایت‌های رایج بود. چون دستورها فضای بیشتری می‌گرفتند، سیستم مجبور بود زمان بیشتری را صرف خواندن دستورها از حافظه کند. حتی گفته می‌شد این مراجعه‌های اضافه ممکن است واقعاً اجرای برنامه را کندتر کنند. اما بعد از انجام بنچمارک‌ها و آزمون‌های گسترده، مشخص شد که RISC تقریباً در همهٔ موارد سریع‌تر است و این استدلال کم‌کم کنار گذاشته شد. با این حال، هنوز موقعیت‌هایی وجود دارد که صرف‌نظر از کارایی، مصرف حافظه یک نگرانی جدی باقی می‌ماند؛ مخصوصاً در سیستم‌های کوچک و کاربردهای نهفته. حتی در اوایل دههٔ ۲۰۰۰ هم قیمت DRAM آن‌قدر بالا بود که دستگاه‌های حساس به هزینه معمولاً حافظهٔ محدودی داشتند. هیتاچی طراحی SuperH را دقیقاً برای همین بازار توسعه داد.

در طراحی‌های اولیهٔ SuperH، یعنی از SH-1 تا SH-4، همهٔ دستورها همیشه ۱۶‌بیتی بودند. این مجموعه‌دستور در دنیای واقعی محدودیت‌هایی دارد؛ مثلاً فقط می‌تواند محاسبات دو‌عملوندی از شکلِ `A = A + B` را انجام دهد، در حالی که بیشتر پردازنده‌های هم‌دوره از قالب سه‌عملوندی مثل `A = B + C` استفاده می‌کردند. با حذف یک عملوند، چهار بیت از طول دستور کم می‌شود (چون ۱۶ ثبات وجود دارد و برای مشخص کردن هر ثبات ۴ بیت لازم است)، اما در عوض نوشتن کدهای محاسباتی تا حدی پیچیده‌تر می‌شود. برای بازارهایی که SuperH هدف گرفته بود، این معاوضه ساده و قابل قبول بود. مزیت مهمِ قالب ۱۶‌بیتی این است که در این حالت، حافظهٔ نهان دستور (instruction cache) با یک مقدار مشخص SRAM می‌تواند تقریباً دو برابر دستور نگه دارد. این موضوع اجازه می‌دهد سیستم با سرعت بالاتری کار کند، هرچند بخشی از این سود ممکن است به خاطر نیاز به دستورهای بیشتر برای انجام کارهایی که در قالب سه‌عملوندی با یک دستور انجام می‌شوند، تا حدی خنثی شود.

در SH-5، هیتاچی به قالب دستورهای ۳۲‌بیتی مهاجرت کرد. برای این‌که با طراحی‌های قبلی‌اش سازگار بماند و برنامه‌های قدیمی همچنان قابل اجرا باشند، یک مجموعه‌دستور دوم هم اضافه کرد که **[[SHcompact]]** نام داشت. در SHcompact، دستورهای اصلیِ ۱۶‌بیتی به‌صورت یک‌طرفه روی دستورهای ۳۲‌بیتیِ داخلی نگاشت می‌شدند؛ یعنی برخلاف پردازنده‌های قدیمیِ میکروکُددار، قرار نبود یک دستورِ فشرده به چند دستور تبدیل شود. در واقع SHcompact فقط همان دستورها را در قالبی کوچک‌تر بیان می‌کرد. این کار باعث می‌شد برنامه‌های قدیمیِ ۱۶‌بیتی به‌سادگی به SH-5 منتقل شوند، بدون این‌که پیچیدگی قابل‌توجهی به رمزگشای دستورها (instruction decoder) اضافه شود.

شرکت ARM تعدادی از پتنت‌های هیتاچی را که به جنبه‌هایی از طراحی این نوع دستورالعمل‌ها مربوط می‌شد لایسنس کرد و از آن‌ها برای پیاده‌سازی دستورهای **Thumb** استفاده نمود. پردازنده‌های ARM که در نامشان حرف **T** داشتند، علاوه بر مجموعه‌دستور اصلیِ ۳۲‌بیتی، این حالت فشرده را هم پشتیبانی می‌کردند و می‌شد در حین اجرا با دستور `BX` به‌صورت پویا بین حالت ۳۲‌بیتی و ۱۶‌بیتی جابه‌جا شد. وقتی پردازنده در حالت Thumb قرار دارد، فقط هشت ثباتِ بالایی از مجموع شانزده ثبات معمولِ ARM برای برنامه قابل مشاهده است؛ اما این‌ها همان ثبات‌هایی هستند که در حالت ۳۲‌بیتی هم وجود دارند، بنابراین می‌توان از همین ثبات‌ها برای انتقال داده بین کد Thumb و کد عادی ARM استفاده کرد. هر دستور Thumb همتای مستقیمی در نسخهٔ ۳۲‌بیتی دارد، پس Thumb یک زیرمجموعهٔ سخت‌گیرانه از ISA اصلیِ ARM به‌حساب می‌آید.

یک تفاوت مهم میان مدل ARM و SuperH این است که Thumb در قالب ۱۶‌بیتی هنوز بعضی از دستورهای سه‌عملوندی را نگه می‌دارد. این کار با محدود کردن ثبات‌های قابل دسترس به هشت تا ممکن شده، چون در این حالت برای انتخاب هر ثبات فقط ۳ بیت لازم است.

معماری **MIPS** هم یک مجموعه‌دستور فشردهٔ مشابه را با نام **[[MIPS16e]]** اضافه کرد که از نظر ایده و سازوکار کلی بسیار شبیه Thumb است. در این حالت نیز فقط هشت ثبات در دسترس‌اند، اما این هشت‌تا صرفاً «هشت ثبات اول» نیستند؛ چون در طراحی MIPS، ثبات شمارهٔ ۰ نقش ثبات صفر (zero register) را دارد و همیشه مقدار صفر برمی‌گرداند. به همین دلیل، در حالت ۱۶‌بیتی، ثبات‌های ۰ و ۱ به‌جای آن‌که به همان شماره‌ها در MIPS32 اشاره کنند، روی ثبات‌های ۱۶ و ۱۷ نگاشت می‌شوند. بقیهٔ جزئیات این روش هم تا حد زیادی مشابه Thumb است.

به همین ترتیب، در نسخه‌های جدید **Power ISA** (که قبلاً با نام PowerPC شناخته می‌شد)، دستورهای **VLE** اضافه شده‌اند که از نظر ماهیت تقریباً همان کار را انجام می‌دهند. این قابلیت عمدتاً به درخواست شرکت **Freescale Semiconductor** به مجموعه‌دستور Power اضافه شد؛ شرکتی که تمرکز اصلی‌اش روی بازار سیستم‌های نهفته بود.

## کاربردهای امروزی
از حدود سال ۲۰۱۵ به بعد، بسیاری از پردازنده‌ها به سمت قالب‌های ۶۴‌بیتی رفتند. در اغلب این طراحی‌ها، قالب دستورها همچنان ۳۲‌بیتی باقی مانده است، اما ثبات‌های داخلی به اندازهٔ ۶۴ بیت گسترش پیدا کرده‌اند. با تعریف اولیه، می‌شود این دستورها را «فشرده» دانست، چون طولشان از اندازهٔ کلمهٔ داده کوچک‌تر است. با این حال، در این کاربرد معمولاً از اصطلاح «دستورالعمل فشرده» استفاده نمی‌شود؛ چون وقتی دربارهٔ دستورهای فشرده صحبت می‌شود، تقریباً همیشه منظور نسخه‌های ۱۶‌بیتیِ ISA است.

## منابع