LLVM-MCA(1) LLVM LLVM-MCA(1)

llvm-mca - تحلیل‌گر کد ماشین LLVM

llvm-mca [گزینه‌ها] [ورودی]

llvm-mca یک ابزار تحلیل کارایی است که با استفاده از اطلاعات موجود در LLVM (مانند مدل‌های زمان‌بندی)، کارایی کد ماشین را در یک CPU مشخص به‌صورت ایستا می‌سنجد.

کارایی بر حسب توان عملیاتی (throughput) و همچنین میزان مصرف منابع پردازنده سنجیده می‌شود. این ابزار در حال حاضر برای پردازنده‌هایی کار می‌کند که دارای یک backend با مدل زمان‌بندی در دسترس در LLVM باشند.

هدف اصلی این ابزار نه تنها پیش‌بینی کارایی کد در زمان اجرا روی مقصد، بلکه کمک به عیب‌یابی و شناسایی مشکلات احتمالی کارایی است.

با دریافت یک دنباله کد اسمبلی، llvm-mca تعداد دستورات در هر چرخه (IPC یا Instructions Per Cycle) و همچنین فشار بر منابع سخت‌افزاری را تخمین می‌زند. سبک تحلیل و گزارش‌دهی این ابزار از ابزار IACA شرکت Intel الهام گرفته شده است.

برای نمونه، می‌توانید کد را با clang کامپایل کنید، خروجی اسمبلی بگیرید و آن را مستقیماً از طریق لوله (pipe) برای تحلیل به llvm-mca هدایت کنید:

$ clang foo.c -O2 --target=x86_64 -S -o - | llvm-mca -mcpu=btver2

یا برای نحو (سینتکس) Intel:

$ clang foo.c -O2 --target=x86_64 -masm=intel -S -o - | llvm-mca -mcpu=btver2

(llvm-mca نحو Intel را از طریق وجود یک دستورالعمل .intel_syntax در ابتدای ورودی تشخیص می‌دهد. به‌طور پیش‌فرض نحو خروجی آن با نحو ورودی مطابقت دارد.)

مدل‌های زمان‌بندی نه تنها برای محاسبه تاخیر (latency) و توان عملیاتی دستورالعمل‌ها، بلکه برای درک منابع دردسترس پردازنده و نحوه شبیه‌سازی آن‌ها استفاده می‌شوند.

به لحاظ طراحی، کیفیت تحلیل انجام‌شده توسط llvm-mca ناگزیر تحت تأثیر کیفیت مدل‌های زمان‌بندی در LLVM است.

اگر مشاهده کردید که گزارش کارایی برای یک پردازنده دقیق نیست، لطفاً یک گزارش خطا در https://github.com/llvm/llvm-project/issues برای backend مربوطه ارسال کنید..SH "گزینه‌ها (OPTIONS)"

اگر input برابر با "-" باشد یا مشخص نشود، llvm-mca از ورودی استاندارد می‌خواند. در غیر این صورت، از نام فایل مشخص‌شده خواهد خواند.

اگر گزینهٔ -o مشخص نشود، در صورتی که ورودی از ورودی استاندارد باشد، llvm-mca خروجی خود را به خروجی استاندارد ارسال می‌کند. اگر گزینهٔ -o مقدار "-" را مشخص کند، خروجی نیز به خروجی استاندارد ارسال خواهد شد.

چاپ خلاصه‌ای از گزینه‌های خط فرمان.
استفاده از <filename> به عنوان نام فایل خروجی. برای جزئیات بیشتر به خلاصهٔ بالا مراجعه کنید.
مشخص کردن رشتهٔ target triple.
مشخص کردن معماری مورد نظر برای تحلیل کد. مقدار پیش‌فرض آن، هدف پیش‌فرض میزبان است.
مشخص کردن پردازنده برای تحلیل کد. به صورت پیش‌فرض، نام CPU به طور خودکار از روی میزبان شناسایی می‌شود.
مشخص کردن گونهٔ اسمبلی خروجی برای گزارش تولیدشده توسط این ابزار. در x86، مقادیر ممکن [0, 1] هستند. مقدار 0 (به ترتیب 1) برای این پرچم، قالب اسمبلی AT&T (به ترتیب Intel) را برای کدی که توسط ابزار در گزارش تحلیل چاپ می‌شود فعال می‌کند.
ترجیح قالب هگزادسیمال (hex) برای مقادیر عددی صریح در خروجی اسمبلی چاپ‌شده به عنوان بخشی از گزارش.
مشخص کردن پهنای دیسپچ متفاوتی برای پردازنده. پهنای دیسپچ به طور پیش‌فرض روی فیلد 'IssueWidth' در مدل زمان‌بندی پردازنده تنظیم است. اگر پهنا صفر باشد، پهنای دیسپچ پیش‌فرض استفاده می‌شود.
مشخص کردن اندازهٔ register file. در صورت تعیین، این پرچم تعداد ثبات‌های فیزیکی موجود برای اهداف تغییر نام ثبات را محدود می‌کند. مقدار صفر برای این پرچم به معنای "unlimited number of physical registers" است.
مشخص کردن تعداد تکرارها برای اجرا. اگر این پرچم روی 0 تنظیم شود، ابزار تعداد تکرارها را روی مقدار پیش‌فرض (یعنی 100) تنظیم می‌کند.
در صورت تنظیم، ابزار فرض می‌کند که عملیات‌های بارگذاری (loads) و ذخیره‌سازی (stores) تداخل آدرس (alias) ندارند. این رفتار پیش‌فرض است.
مشخص کردن اندازهٔ صف بارگذاری در واحد load/store شبیه‌سازی‌شده توسط ابزار. به صورت پیش‌فرض، ابزار تعداد نامحدودی ورودی را در صف بارگذاری فرض می‌کند. مقدار صفر برای این پرچم نادیده گرفته شده و در عوض اندازهٔ پیش‌فرض صف بارگذاری استفاده می‌شود.
مشخص کردن اندازهٔ صف ذخیره‌سازی در واحد load/store شبیه‌سازی‌شده توسط ابزار. به صورت پیش‌فرض، ابزار تعداد نامحدودی ورودی را در صف ذخیره‌سازی فرض می‌کند. مقدار صفر برای این پرچم نادیده گرفته شده و در عوض اندازهٔ پیش‌فرض صف ذخیره‌سازی استفاده می‌شود.
فعال کردن نمای خط زمانی (timeline).
-timeline-max-iterations=<iterations>
محدود کردن تعداد تکرارهایی که در نمای خط زمانی چاپ می‌شوند. به صورت پیش‌فرض، نمای خط زمانی اطلاعات را حداکثر تا ۱۰ تکرار چاپ می‌کند.
-timeline-max-cycles=<cycles>
محدود کردن تعداد چرخه‌ها در نمای خط زمانی، یا استفاده از 0 برای بدون محدودیت. به صورت پیش‌فرض، تعداد چرخه‌ها روی ۸۰ تنظیم شده است.
فعال کردن نمای فشار منابع (resource pressure). این نما به صورت پیش‌فرض فعال است.
فعال کردن آمار استفاده از register file.
فعال کردن آمارهای اضافهٔ دیسپچ. این نما رویدادهای دیسپچ دستورالعمل و همچنین رویدادهای توقف (stall) دیسپچ ایستا/پویا را جمع‌آوری و تحلیل می‌کند. این نما به طور پیش‌فرض غیرفعال است.
فعال کردن آمارهای اضافهٔ زمان‌بند. این نما رویدادهای صدور دستورالعمل (instruction issue) را جمع‌آوری و تحلیل می‌کند. این نما به طور پیش‌فرض غیرفعال است.
فعال کردن آمارهای اضافهٔ واحد کنترل بازنشستگی (retire control unit). این نما به صورت پیش‌فرض غیرفعال است.
فعال کردن نمای اطلاعات دستورالعمل. این نما به صورت پیش‌فرض فعال است.
فعال کردن چاپ کدگذاری‌های دستورالعمل در نمای اطلاعات دستورالعمل.
فعال کردن چاپ پرچم‌های LoadBarrier و StoreBarrier در نمای اطلاعات دستورالعمل.
چاپ تمام آمارهای سخت‌افزاری. این گزینه آمارهای اضافی مربوط به منطق دیسپچ، زمان‌بندهای سخت‌افزاری، register file(ها)، و واحد کنترل بازنشستگی را فعال می‌کند. این گزینه به طور پیش‌فرض غیرفعال است.
فعال کردن همهٔ نماها.
اطلاعات فشار منابع را بر اساس اطلاعات ایستای موجود از مدل پردازنده چاپ می‌کند. این با نمای فشار منابع متفاوت است زیرا نیازی به شبیه‌سازی کد ندارد. در عوض توزیع یکنواخت نظری فشار منابع را برای هر دستورالعمل به ترتیب چاپ می‌کند.

انتخاب <level> میزان اطلاعات چاپ‌شده را کنترل می‌کند. <level> می‌تواند none (پیش‌فرض)، normal، یا full باشد. نکته: اگر این گزینه بدون <label> استفاده شود، مقدار پیش‌فرض normal است (روش موروثی).

هنگامی که <level> برابر با full باشد، اطلاعات اضافی عبارتند از: - <Bypass Latency>: زمان تأخیر هنگام پیاده‌سازی یک بای‌پس میان عملوندها در خطوط لوله (به SchedReadAdvance مراجعه کنید). - <LLVM Opcode Name>: نام نمادین (mnemonic) به همراه شناسهٔ عملوندها. - <Resources units>: منابع مصرف‌شدهٔ مرتبط با LLVM Opcode. - <instruction comment>: در صورت وجود، توضیحات (comment) اسمبلی مبدأ را گزارش می‌کند..sp نحو <Resources units> می‌تواند به صورت زیر باشد: - <Resource Name>: مقدار ReleaseAtCycle برابر 1 است. - <Resource Name>[<ReleaseAtCycle>]: مقدار ReleaseAtCycle بزرگ‌تر از 1 و AcquireAtCycle برابر 0 است. - <Resource Name>[<AcquireAtCycle>,<ReleaseAtCycle>]: مقدار ReleaseAtCycle بزرگ‌تر از 1 و AcquireAtCycle بزرگ‌تر از 0 است.

چاپ اطلاعات مربوط به گلوگاه‌هایی که بر توان عملیاتی تأثیر می‌گذارند. این تحلیل می‌تواند پرهزینه باشد و به‌طور پیش‌فرض غیرفعال است. گلوگاه‌ها در نمای خلاصه برجسته می‌شوند. تحلیل گلوگاه در حال حاضر برای پردازنده‌های دارای بخش پسین درون‌ترتیبی (in-order backend) پشتیبانی نمی‌شود.
چاپ نماهای درخواست‌شده در قالب معتبر JSON. دستورالعمل‌ها و منابع پردازنده به عنوان اعضای اشیاء سطح بالای ویژه JSON چاپ می‌شوند. نماهای مجزا با نمایه به آن‌ها ارجاع می‌دهند. با این حال، در حال حاضر همه نماها پشتیبانی نمی‌شوند. برای مثال، گزارش حاصل از تحلیل گلوگاه در قالب JSON چاپ نمی‌شود. تمامی نماهای پیش‌فرض در حال حاضر پشتیبانی می‌شوند.
اجبار به استفاده از کلاس‌های عمومی CustomBehaviour و InstrPostProcess به جای استفاده از پیاده‌سازی مختص به هدف (target-specific). کلاس‌های عمومی هرگز هیچ مخاطره (hazard) سفارشی را تشخیص نمی‌دهند و هیچ تغییر پس‌پردازشی بر روی دستورالعمل‌ها اعمال نمی‌کنند.
اجبار به استفاده از InstrumentManager عمومی به جای استفاده از پیاده‌سازی مختص به هدف. کلاس عمومی Instrumentهایی ایجاد می‌کند که هیچ اطلاعات اضافی ارائه نمی‌دهند، و InstrumentManager هرگز کلاس زمان‌بندی پیش‌فرض را برای یک دستورالعمل مشخص بازنویسی نمی‌کند.
دستور llvm-mca را وادار می‌کند تا در حضور دستورالعمل‌هایی که تجزیه نمی‌شوند یا فاقد اطلاعات کلیدی زمان‌بندی هستند، ادامه دهد. توجه داشته باشید که تحلیل نهایی متأثر می‌شود زیرا این دستورالعمل‌های پشتیبانی‌نشده نادیده گرفته می‌شوند، گویی بخشی از ورودی نبوده‌اند.

انتخاب <reason> زمان گزارش خطا توسط mca را کنترل می‌کند. <reason> می‌تواند none (پیش‌فرض)، lack-sched، parse-failure یا any باشد.

در صورت موفقیت، llvm-mca مقدار 0 را برمی‌گرداند. در غیر این صورت، پیام خطایی در خروجی خطای استاندارد چاپ شده و ابزار مقدار 1 را برمی‌گرداند.

ابزار llvm-mca امکان استفاده اختیاری از کامنت‌های ویژه کد را برای نشانه‌گذاری نواحی کدهای اسمبلی که باید تحلیل شوند فراهم می‌کند. کامنتی که با زیررشته LLVM-MCA-BEGIN آغاز می‌شود، شروع یک ناحیه تحلیلی را مشخص می‌کند. کامنتی که با زیررشته LLVM-MCA-END آغاز می‌شود، پایان یک ناحیه را مشخص می‌کند. برای مثال:

# LLVM-MCA-BEGIN
  ...
# LLVM-MCA-END

اگر هیچ ناحیه تعریف‌شده توسط کاربر مشخص نشود، llvm-mca یک ناحیه پیش‌فرض را در نظر می‌گیرد که شامل تمام دستورالعمل‌های موجود در فایل ورودی است. هر ناحیه به صورت مجزا تحلیل می‌شود و گزارش نهایی کارایی، اجتماع تمام گزارش‌های تولیدشده برای هر ناحیه تحلیلی است.

نواحی تحلیلی می‌توانند نام داشته باشند. برای مثال:

# LLVM-MCA-BEGIN A simple example
  add %eax, %eax
# LLVM-MCA-END

کد مثال بالا ناحیه‌ای به نام "A simple example" را با یک دستورالعمل منفرد در آن تعریف می‌کند. توجه کنید که نیازی نیست نام ناحیه در دستورالعمل کمکی LLVM-MCA-END تکرار شود. در غیاب نواحی هم‌پوشان، یک دستورالعمل کمکی بی‌نام LLVM-MCA-END همواره به ناحیه فعال فعلی تعریف‌شده توسط کاربر پایان می‌دهد.

مثالی از نواحی تودرتو:

# LLVM-MCA-BEGIN foo
  add %eax, %edx
# LLVM-MCA-BEGIN bar
  sub %eax, %edx
# LLVM-MCA-END bar
# LLVM-MCA-END foo

مثالی از نواحی هم‌پوشان:

# LLVM-MCA-BEGIN foo
  add %eax, %edx
# LLVM-MCA-BEGIN bar
  sub %eax, %edx
# LLVM-MCA-END foo
  add %eax, %edx
# LLVM-MCA-END bar

توجه داشته باشید که چندین ناحیه بی‌نام نمی‌توانند با یکدیگر هم‌پوشانی داشته باشند. همچنین، نواحی هم‌پوشان نمی‌توانند نام یکسانی داشته باشند.

هیچ پشتیبانی‌ای برای نشانه‌گذاری نواحی از درون کد منبع سطح بالا، مانند C یا C++ وجود ندارد. به عنوان یک راهکار جایگزین، می‌توان از دستورات اسمبلی درون‌خطی استفاده کرد:

int foo(int a, int b) {
  __asm volatile("# LLVM-MCA-BEGIN foo":::"memory");
  a += 42;
  __asm volatile("# LLVM-MCA-END":::"memory");
  a *= b;
  return a;
}

با این حال، این کار با بهینه‌سازی‌هایی مانند برداری‌سازی حلقه (loop vectorization) تداخل ایجاد می‌کند و ممکن است بر کد تولیدشده تأثیر بگذارد. دلیل این است که عبارات __asm به عنوان کد واقعی با اثرات جانبی مهم در نظر گرفته می‌شوند، که نحوه تغییر کدهای پیرامون آن‌ها را محدود می‌کند. اگر کاربران می‌خواهند از اسمبلی درون‌خطی برای درج نشانگرها استفاده کنند، توصیه می‌شود همیشه بررسی کنند که اسمبلی خروجی معادل با اسمبلی تولیدشده در غیاب نشانگرها باشد. گزینه‌های Clang برای تولید گزارش‌های بهینه‌سازی https://clang.llvm.org/docs/UsersManual.html#options-to-emit-optimization-reports نیز می‌توانند در شناسایی بهینه‌سازی‌های از دست رفته کمک کنند..SH "ناحیه‌های ابزارسازی (INSTRUMENT REGIONS)"

یک InstrumentRegion ناحیه‌ای از کد اسمبلی را توصیف می‌کند که توسط دستورالعمل‌های تفسیری ویژهٔ LLVM-MCA محافظت می‌شود.

# LLVM-MCA-<INSTRUMENT_TYPE> <data>
  ...  ## asm

که در آن INSTRUMENT_TYPE نوعی است که توسط معماری مقصد تعریف شده و انتظار دارد از data استفاده کند.

تفسیری که با زیررشتهٔ LLVM-MCA-<INSTRUMENT_TYPE> آغاز می‌شود، داده‌ها را در حوزهٔ دید (scope) قرار می‌دهد تا llvm-mca در تحلیل خود برای تمام دستورالعمل‌های بعدی از آن‌ها استفاده کند.

اگر تفسیری با همان INSTRUMENT_TYPE بعداً در فهرست دستورالعمل‌ها یافت شود، آن‌گاه InstrumentRegion اولیه به‌طور خودکار پایان یافته و یک InstrumentRegion جدید آغاز خواهد شد.

اگر تفسیرهایی حاوی INSTRUMENT_TYPE متفاوتی وجود داشته باشند، هر دو مجموعهٔ داده در دسترس باقی می‌مانند. برخلاف یک AnalysisRegion، یک InstrumentRegion برای پایان دادن به ناحیه به تفسیری نیاز ندارد.

تفسیرهایی که با پیشوند LLVM-MCA- مشخص شده‌اند اما با یک INSTRUMENT_TYPE معتبر برای مقصد مطابقت ندارند، موجب بروز خطا می‌شوند، به استثنای BEGIN و END، چرا که آن‌ها به AnalysisRegionها تعلق دارند. تفسیرهایی که با LLVM-MCA- آغاز نمی‌شوند توسط llvm-mca نادیده گرفته می‌شوند.

یک دستورالعمل (یک MCInst) تنها زمانی به یک InstrumentRegion به نام R اضافه می‌شود که موقعیت مکانی آن در بازهٔ [R.RangeStart, R.RangeEnd] باشد.

یک ابزار وجود دارد که می‌تواند در تمامی معماری‌های مقصد برای تنظیم صریح زمان‌های تأخیر دستورالعمل‌ها استفاده شود. برای مثال، می‌توان از آن برای مدل‌سازی خطاهای حافظهٔ نهان (cache misses) که بر تأخیرهای بارگذاری (load latencies) تأثیر می‌گذارند استفاده کرد. ساختار آن بدین شکل است:

# LLVM-MCA-LATENCY 100
mov (%edi), %eax
# LLVM-MCA-LATENCY

این کار تأخیر دستورالعمل mov را روی 100 تنظیم می‌کند. LLVM-MCA-LATENCY بدون آرگومان، ناحیهٔ با تأخیر صریح را به پایان می‌رساند؛ پس از آن، از تأخیرهای پیش‌فرض مقصد استفاده می‌شود.

در مقصدهای RISCV، دستورالعمل‌های برداری بسته به LMUL رفتار متفاوتی دارند. کد می‌تواند با تفسیری به شکل زیر ابزارسازی شود:

# LLVM-MCA-RISCV-LMUL <M1|M2|M4|M8|MF2|MF4|MF8>

بخش RISCV InstrumentManager کلاس زمان‌بندی (schedule class) را برای دستورالعمل‌های برداری بازنویسی خواهد کرد تا از رفتار زمان‌بندی شبه‌دستورالعمل وابسته به LMUL آن استفاده کند. منطقی است که تفسیرهای ابزارسازی RISCV مستقیماً پس از دستورالعمل‌های vset{i}vl{i} قرار داده شوند، هرچند می‌توان آن‌ها را در هر کجای برنامه قرار داد.

مثال از برنامه بدون فراخوانی vset{i}vl{i}:

# LLVM-MCA-RISCV-LMUL M2
vadd.vv v2, v2, v2

مثال از برنامه با فراخوانی vset{i}vl{i}:

vsetvli zero, a0, e8, m1, tu, mu
# LLVM-MCA-RISCV-LMUL M1
vadd.vv v2, v2, v2

مثال از برنامه با چندین فراخوانی برای vset{i}vl{i}:

vsetvli zero, a0, e8, m1, tu, mu
# LLVM-MCA-RISCV-LMUL M1
vadd.vv v2, v2, v2
vsetvli zero, a0, e8, m8, tu, mu
# LLVM-MCA-RISCV-LMUL M8
vadd.vv v2, v2, v2

مثال از برنامه با فراخوانی vsetvl:

vsetvl rd, rs1, rs2
# LLVM-MCA-RISCV-LMUL M1
vadd.vv v12, v12, v12
vsetvl rd, rs1, rs2
# LLVM-MCA-RISCV-LMUL M4
vadd.vv v12, v12, v12

llvm-mca کد اسمبلی را به‌عنوان ورودی دریافت می‌کند. کد اسمبلی با کمک تجزیه‌کننده‌های اسمبلی مقصد (target) موجود در LLVM، به دنباله‌ای از MCInst تجزیه می‌شود. سپس دنباله تجزیه‌شده MCInst توسط یک ماژول Pipeline تحلیل می‌شود تا گزارش کارایی تولید گردد.

ماژول Pipeline اجرای دنباله کد ماشین را در حلقه‌ای از تکرارها (پیش‌فرض 100 است) شبیه‌سازی می‌کند. در طول این فرآیند، خط لوله آمارهای متعددی در ارتباط با اجرا جمع‌آوری می‌نماید. در پایان این فرآیند، خط لوله گزارشی از آمارهای جمع‌آوری‌شده را تولید و چاپ می‌کند.

در اینجا نمونه‌ای از گزارش کارایی تولیدشده توسط این ابزار برای ضرب داخلی (dot-product) دو بردار ممیز شناور بسته‌بندی‌شده (packed float) چهار عنصری آورده شده است. این تحلیل برای مقصد x86 و پردازنده btver2 انجام شده است. نتیجه زیر را می‌توان با استفاده از دستور زیر و مثالی که در test/tools/llvm-mca/X86/BtVer2/dot-product.s قرار دارد، تولید کرد:

$ llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=btver2 -iterations=300 dot-product.s
Iterations:        300
Instructions:      900
Total Cycles:      610
Total uOps:        900
Dispatch Width:    2
uOps Per Cycle:    1.48
IPC:               1.48
Block RThroughput: 2.0
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
 1      2     1.00                        vmulps      %xmm0, %xmm1, %xmm2
 1      3     1.00                        vhaddps     %xmm2, %xmm2, %xmm3
 1      3     1.00                        vhaddps     %xmm3, %xmm3, %xmm4
Resources:
[0]   - JALU0
[1]   - JALU1
[2]   - JDiv
[3]   - JFPA
[4]   - JFPM
[5]   - JFPU0
[6]   - JFPU1
[7]   - JLAGU
[8]   - JMul
[9]   - JSAGU
[10]  - JSTC
[11]  - JVALU0
[12]  - JVALU1
[13]  - JVIMUL
Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12]   [13]
 -      -      -     2.00   1.00   2.00   1.00    -      -      -      -      -      -      -
Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12]   [13]   Instructions:
 -      -      -      -     1.00    -     1.00    -      -      -      -      -      -      -     vmulps      %xmm0, %xmm1, %xmm2
 -      -      -     1.00    -     1.00    -      -      -      -      -      -      -      -     vhaddps     %xmm2, %xmm2, %xmm3
 -      -      -     1.00    -     1.00    -      -      -      -      -      -      -      -     vhaddps     %xmm3, %xmm3, %xmm4

بر اساس این گزارش، هسته ضرب داخلی 300 بار اجرا شده است که در مجموع شامل 900 دستورالعمل شبیه‌سازی‌شده است. تعداد کل ریزعمل‌های (uOps) شبیه‌سازی‌شده نیز 900 عدد است.

این گزارش در سه بخش اصلی ساختاربندی شده است. بخش نخست، چندین شاخص کارایی را گردآوری می‌کند؛ هدف این بخش، ارائه یک نمای کلی و بسیار سریع از توان عملیاتی کارایی است. شاخص‌های کارایی مهم عبارتند از IPC، uOps Per Cycle و Block RThroughput (معکوس توان عملیاتی بلوک یا Block Reciprocal Throughput).

فیلد DispatchWidth حداکثر تعداد ریزعمل‌هایی است که در هر چرخه شبیه‌سازی‌شده به بخش پسین (backend) نامرتب (out-of-order) ارسال (dispatch) می‌شوند. برای پردازنده‌های دارای پسین مرتب (in-order)، DispatchWidth حداکثر تعداد ریزعمل‌هایی است که در هر چرخه شبیه‌سازی‌شده به بخش پسین صادر (issue) می‌شوند.

شاخص IPC با تقسیم تعداد کل دستورالعمل‌های شبیه‌سازی‌شده بر تعداد کل چرخه‌ها محاسبه می‌شود.

فیلد Block RThroughput معکوسِ توان عملیاتی بلوک (block throughput) است. توان عملیاتی بلوک یک کمیت نظری است که به‌عنوان حداکثر تعداد بلوک‌هایی (یعنی تکرارها) که می‌توانند در هر چرخه ساعت شبیه‌سازی‌شده در نبود وابستگی‌های منتقل‌شده در حلقه (loop-carried dependencies) اجرا شوند، محاسبه می‌گردد. توان عملیاتی بلوک از بالا توسط نرخ ارسال (dispatch rate) و دسترسی‌پذیری منابع سخت‌افزاری محدود می‌شود.

در غیاب وابستگی‌های داده‌ایِ منتقل‌شده در حلقه، مقدار مشاهده‌شده IPC به یک حداکثر نظری میل می‌کند که می‌توان آن را با تقسیم تعداد دستورالعمل‌های یک تک‌تکرار بر Block RThroughput محاسبه کرد.

فیلد 'uOps Per Cycle' با تقسیم تعداد کل ریزعمل‌های شبیه‌سازی‌شده بر تعداد کل چرخه‌ها به دست می‌آید. اختلاف (delta) میان Dispatch Width و این فیلد، نشان‌دهنده یک مشکل کارایی است. در نبود وابستگی‌های داده‌ای منتقل‌شده در حلقه، مقدار مشاهده‌شده 'uOps Per Cycle' باید به یک حداکثر توان عملیاتی نظری میل کند که از تقسیم تعداد uOps یک تک‌تکرار بر Block RThroughput به دست می‌آید.

فیلد uOps Per Cycle از بالا توسط پهنای ارسال (dispatch width) محدود می‌شود، زیرا پهنای ارسال حداکثر اندازه یک گروه ارسالی را محدود می‌کند. هر دو مقدار IPC و 'uOps Per Cycle' با میزان موازی‌سازی سخت‌افزاری محدود می‌شوند. در دسترس بودن منابع سخت‌افزاری بر توزیع فشار منابع اثر می‌گذارد و تعداد دستورالعمل‌هایی را که می‌توانند در هر چرخه به‌صورت موازی اجرا شوند محدود می‌سازد. اختلاف میان Dispatch Width و حداکثر نظری uOps per Cycle (که از تقسیم تعداد uOps یک تک‌تکرار بر Block RThroughput به دست می‌آید)، نشان‌دهنده یک گلوگاه کارایی ناشی از کمبود منابع سخت‌افزاری است. به‌طور کلی، هرچه مقدار Block RThroughput کمتر باشد، کارایی بهتر است.

در این مثال، uOps per iteration/Block RThroughput برابر با 1.50 است. از آنجا که هیچ وابستگی منتقل‌شده در حلقه‌ای وجود ندارد، انتظار می‌رود زمانی که تعداد تکرارها به سمت بی‌نهایت میل می‌کند، مقدار مشاهده‌شده uOps Per Cycle به 1.50 نزدیک شود. اختلاف بین Dispatch Width (برابر با 2.00) و حداکثر توان عملیاتی نظری uOp (برابر با 1.50) نشان‌دهنده یک گلوگاه کارایی ناشی از کمبود منابع سخت‌افزاری است، و نمای Resource pressure view می‌تواند به شناسایی استفاده مشکل‌ساز از منابع کمک کند.

بخش دوم گزارش، instruction info view (نمای اطلاعات دستورالعمل) است. این بخش تأخیر (latency) و معکوس توان عملیاتی (reciprocal throughput) هر دستورالعمل موجود در دنباله را نشان می‌دهد. همچنین اطلاعات بیشتری در ارتباط با تعداد ریزعمل‌ها و خصوصیات کد دستور (مانند 'MayLoad'، 'MayStore' و 'HasSideEffects') گزارش می‌کند.

فیلد RThroughput معکوسِ توان عملیاتی دستورالعمل است. توان عملیاتی به‌عنوان حداکثر تعداد دستورالعمل‌های از یک نوع که می‌توانند در نبود وابستگی عملوندها در هر چرخه ساعت اجرا شوند محاسبه می‌شود. در این مثال، معکوس توان عملیاتی یک ضرب ممیز شناور برداری برابر با 1 cycles/instruction است؛ علت این امر آن است که ضرب‌کننده ممیز شناور JFPM تنها از طریق خط لوله JFPU1 در دسترس است.

هنگامی که پرچم -show-encoding مشخص شده باشد، کدگذاری‌های دستورالعمل در نمای اطلاعات دستورالعمل نمایش داده می‌شوند.

در ادامه مثالی از خروجی -show-encoding برای هسته ضرب داخلی آمده است:

Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)
[7]: Encoding Size
[1]    [2]    [3]    [4]    [5]    [6]    [7]    Encodings:                    Instructions:
 1      2     1.00                         4     c5 f0 59 d0                   vmulps %xmm0, %xmm1, %xmm2
 1      4     1.00                         4     c5 eb 7c da                   vhaddps        %xmm2, %xmm2, %xmm3
 1      4     1.00                         4     c5 e3 7c e3                   vhaddps        %xmm3, %xmm3, %xmm4

ستون Encoding Size اندازه دستورالعمل‌ها را بر حسب بایت نشان می‌دهد. ستون Encodings کدگذاری‌های واقعی دستورالعمل را نمایش می‌دهد (دنباله‌های بایتی در قالب هگزادسیمال).

بخش سوم، نمای Resource pressure view (نمای فشار منابع) است. این نما میانگین تعداد چرخه‌های منبع مصرف‌شده در هر تکرار توسط دستورالعمل‌ها را برای هر واحد منبع پردازنده موجود در معماری مقصد گزارش می‌دهد. اطلاعات در دو جدول ساختاربندی شده‌اند. جدول نخست، تعداد چرخه‌های منبع صرف‌شده به‌طور میانگین در هر تکرار را گزارش می‌کند. جدول دوم، چرخه‌های منبع را به دستورالعمل ماشین در دنباله مرتبط می‌سازد. برای نمونه، هر تکرار از دستورالعمل vmulps همواره بر روی واحد منبع [6] (JFPU1 - خط لوله ممیز شناور شماره ۱) اجرا می‌شود و به‌طور میانگین ۱ چرخه منبع در هر تکرار مصرف می‌کند. توجه داشته باشید که در AMD Jaguar، ضرب ممیز شناور برداری تنها می‌تواند به خط لوله JFPU1 صادر شود، در حالی که جمع‌های ممیز شناور افقی تنها می‌توانند به خط لوله JFPU0 صادر شوند..sp نمای فشار منابع (resource pressure view) به شناسایی گلوگاه‌های ناشی از استفادهٔ زیاد از منابع سخت‌افزاری خاص کمک می‌کند. شرایطی که در آن فشار منابع عمدتاً روی چند منبع معدود متمرکز شده باشد، به‌طور کلی باید اجتناب شود. در حالت ایده‌آل، فشار باید به‌طور یکنواخت بین چندین منبع توزیع شود.

نمای خط زمانی گزارش دقیقی از انتقال وضعیت هر دستورالعمل درون خط‌لولهٔ (pipeline) دستورالعمل تولید می‌کند. این نما با گزینهٔ خط فرمان -timeline فعال می‌شود. هم‌زمان با انتقال دستورالعمل‌ها از مراحل مختلف خط‌لوله، وضعیت آن‌ها در گزارش نما به تصویر کشیده می‌شود. این وضعیت‌ها با نویسه‌های زیر نمایش داده می‌شوند:

  • D : دستورالعمل اعزام شد (Instruction dispatched).
  • e : دستورالعمل در حال اجرا است (Instruction executing).
  • E : دستورالعمل اجرا شد (Instruction executed).
  • R : دستورالعمل بازنشسته شد (Instruction retired).
  • = : دستورالعمل قبلاً اعزام شده و در انتظار اجرا است.
  • - : دستورالعمل اجرا شده و در انتظار بازنشستگی است.

در زیر نمای خط زمانی برای زیرمجموعه‌ای از مثال dot-product واقع در test/tools/llvm-mca/X86/BtVer2/dot-product.s آمده است که توسط llvm-mca با دستور زیر پردازش شده است:

$ llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=btver2 -iterations=3 -timeline dot-product.s
Timeline view:
                    012345
Index     0123456789
[0,0]     DeeER.    .    .   vmulps   %xmm0, %xmm1, %xmm2
[0,1]     D==eeeER  .    .   vhaddps  %xmm2, %xmm2, %xmm3
[0,2]     .D====eeeER    .   vhaddps  %xmm3, %xmm3, %xmm4
[1,0]     .DeeE-----R    .   vmulps   %xmm0, %xmm1, %xmm2
[1,1]     . D=eeeE---R   .   vhaddps  %xmm2, %xmm2, %xmm3
[1,2]     . D====eeeER   .   vhaddps  %xmm3, %xmm3, %xmm4
[2,0]     .  DeeE-----R  .   vmulps   %xmm0, %xmm1, %xmm2
[2,1]     .  D====eeeER  .   vhaddps  %xmm2, %xmm2, %xmm3
[2,2]     .   D======eeeER   vhaddps  %xmm3, %xmm3, %xmm4
Average Wait times (based on the timeline view):
[0]: Executions
[1]: Average time spent waiting in a scheduler's queue
[2]: Average time spent waiting in a scheduler's queue while ready
[3]: Average time elapsed from WB until retire stage
      [0]    [1]    [2]    [3]
0.     3     1.0    1.0    3.3       vmulps   %xmm0, %xmm1, %xmm2
1.     3     3.3    0.7    1.0       vhaddps  %xmm2, %xmm2, %xmm3
2.     3     5.7    0.0    0.0       vhaddps  %xmm3, %xmm3, %xmm4
       3     3.3    0.5    1.4       <total>

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

نمای خط زمانی در دو جدول سازمان‌دهی شده است. جدول اول تغییر وضعیت دستورالعمل‌ها را در طول زمان (اندازه‌گیری‌شده بر حسب چرخه) نشان می‌دهد؛ جدول دوم (با نام Average Wait times) آمارهای زمان‌بندی مفیدی را گزارش می‌کند که به عیب‌یابی گلوگاه‌های کارایی ناشی از وابستگی‌های داده‌ای طولانی و استفادهٔ غیربهینه از منابع سخت‌افزاری کمک می‌کند.

هر دستورالعمل در نمای خط زمانی با یک جفت اندیس مشخص می‌شود، که اندیس اول تکرار (iteration) را مشخص می‌کند و اندیس دوم شناسهٔ دستورالعمل است (یعنی مکانی که در توالی کد ظاهر می‌شود). از آنجا که این مثال با استفاده از ۳ تکرار تولید شده است: -iterations=3، اندیس‌های تکرار شامل بازهٔ ۰ تا ۲ می‌شوند.

به‌استثنای ستون اول و آخر، بقیهٔ ستون‌ها بر حسب چرخه هستند. چرخه‌ها به‌صورت متوالی و با شروع از ۰ شماره‌گذاری شده‌اند.

از خروجی نمونهٔ بالا، موارد زیر را درمی‌یابیم:

  • دستورالعمل [1,0] در چرخهٔ ۱ اعزام شد (dispatched).
  • دستورالعمل [1,0] در چرخهٔ ۲ شروع به اجرا کرد.
  • دستورالعمل [1,0] در چرخهٔ ۴ به مرحلهٔ بازنویسی (write back) رسید.
  • دستورالعمل [1,0] در چرخهٔ ۱۰ بازنشسته (retired) شد.

دستورالعمل [1,0] (یعنی vmulps از تکرار شمارهٔ ۱) نیازی به انتظار در صف زمان‌بند (scheduler) برای در دسترس قرار گرفتن عملوندها ندارد. تا زمانی که vmulps اعزام می‌شود، عملوندها از قبل در دسترس هستند و خط‌لولهٔ JFPU1 آمادهٔ ارائهٔ خدمات به دستورالعمل دیگری است. بنابراین دستورالعمل می‌تواند بلافاصله در خط‌لولهٔ JFPU1 صادر شود (issued). این موضوع با این واقعیت نشان داده می‌شود که دستورالعمل تنها ۱ چرخه (1cy) را در صف زمان‌بند سپری کرده است.

فاصله‌ای ۵ چرخه‌ای بین مرحلهٔ بازنویسی (write-back) و رویداد بازنشستگی (retire) وجود دارد. این به این دلیل است که دستورالعمل‌ها باید به ترتیب برنامه بازنشسته شوند، بنابراین [1,0] باید منتظر بماند تا ابتدا [0,2] بازنشسته شود (یعنی باید تا چرخهٔ ۱۰ صبر کند).

در این مثال، همهٔ دستورالعمل‌ها در یک زنجیرهٔ وابستگی RAW (خواندن پس از نوشتن یا Read After Write) قرار دارند. ثبات %xmm2 که توسط vmulps نوشته می‌شود بلافاصله توسط اولین vhaddps استفاده می‌شود، و ثبات %xmm3 که توسط اولین vhaddps نوشته می‌شود توسط دومین vhaddps استفاده می‌گردد. وابستگی‌های طولانی داده‌ها تأثیر منفی بر ILP (موازی‌سازی در سطح دستورالعمل یا Instruction Level Parallelism) دارند.

در مثال dot-product، پادوابستگی‌هایی (anti-dependencies) وجود دارند که توسط دستورالعمل‌های تکرارهای مختلف ایجاد شده‌اند. با این حال، این وابستگی‌ها را می‌توان در مرحلهٔ تغییر نام ثبات‌ها (register renaming) حذف کرد (به بهای تخصیص نام‌های مستعار ثبات و در نتیجه مصرف ثبات‌های فیزیکی).

جدول Average Wait times به عیب‌یابی مشکلات کارایی ناشی از وجود دستورالعمل‌های با تاخیر بالا (long latency) و وابستگی‌های بالقوه طولانی داده‌ها که ممکن است ILP را محدود کنند، کمک می‌کند. سطر آخر، <total>، میانگین کلی تمام دستورالعمل‌های اندازه‌گیری‌شده را نشان می‌دهد. توجه داشته باشید که llvm-mca به‌طور پیش‌فرض، دست‌کم ۱ چرخه (1cy) را بین رویداد اعزام (dispatch) و رویداد صدور (issue) فرض می‌کند.

هنگامی که کارایی به دلیل وابستگی‌های داده یا دستورالعمل‌های با تاخیر طولانی محدود می‌شود، انتظار می‌رود تعداد چرخه‌های سپری‌شده در وضعیت ready در مقایسه با کل چرخه‌های سپری‌شده در صف زمان‌بند بسیار اندک باشد. تفاوت میان این دو شمارنده معیار مناسبی از میزان اثرگذاری وابستگی‌های داده بر اجرای دستورالعمل‌ها است. زمانی که کارایی عمدتاً به دلیل کمبود منابع سخت‌افزاری محدود شود، اختلاف بین این دو شمارنده ناچیز است. با این حال، تعداد چرخه‌های سپری‌شده در صف معمولاً بیشتر خواهد بود (یعنی بیش از ۱ تا ۳ چرخه - 1-3cy)، به‌ویژه در مقایسه با سایر دستورالعمل‌های با تاخیر اندک..SS تحلیل گلوگاه (Bottleneck Analysis)

گزینه خط فرمان -bottleneck-analysis تحلیل گلوگاه‌های کارایی را فعال می‌کند.

این تحلیل می‌تواند پرهزینه باشد. این فرایند تلاش می‌کند افزایش فشار بر بخش انتهایی (backend pressure، ناشی از فشار بر منابع خط لوله و وابستگی‌های داده) را با توقف‌های اعزام پویا (dynamic dispatch stalls) مرتبط سازد.

در زیر نمونه‌ای از خروجی -bottleneck-analysis آورده شده است که توسط llvm-mca برای ۵۰۰ تکرار از مثال ضرب داخلی (dot-product) بر روی btver2 تولید شده است.

Cycles with backend pressure increase [ 48.07% ]
Throughput Bottlenecks:
  Resource Pressure       [ 47.77% ]
  - JFPA  [ 47.77% ]
  - JFPU0  [ 47.77% ]
  Data Dependencies:      [ 0.30% ]
  - Register Dependencies [ 0.30% ]
  - Memory Dependencies   [ 0.00% ]
Critical sequence based on the simulation:
              Instruction                         Dependency Information
 +----< 2.    vhaddps %xmm3, %xmm3, %xmm4
 |
 |    < loop carried >
 |
 |      0.    vmulps  %xmm0, %xmm1, %xmm2
 +----> 1.    vhaddps %xmm2, %xmm2, %xmm3         ## RESOURCE interference:  JFPA [ probability: 74% ]
 +----> 2.    vhaddps %xmm3, %xmm3, %xmm4         ## REGISTER dependency:  %xmm3
 |
 |    < loop carried >
 |
 +----> 1.    vhaddps %xmm2, %xmm2, %xmm3         ## RESOURCE interference:  JFPA [ probability: 74% ]

بر اساس این تحلیل، توان عملیاتی (throughput) به دلیل فشار منابع محدود شده است، نه به دلیل وابستگی‌های داده. این تحلیل، افزایش فشار بخش انتهایی (backend) را در طول ۴۸٫۰۷٪ از اجرای شبیه‌سازی‌شده مشاهده کرده است. تقریباً تمام این رویدادهای افزایش فشار، به دلیل رقابت (contention) بر سر منابع پردازنده JFPA/JFPU0 رخ داده‌اند.

توالی critical sequence (توالی بحرانی) پرهزینه‌ترین دنباله دستورالعمل‌ها بر اساس شبیه‌سازی است. این دنباله نشانه‌گذاری شده است تا اطلاعات بیشتری درباره وابستگی‌های بحرانی ثبات‌ها و تداخل منابع بین دستورالعمل‌ها ارائه دهد.

انتظار می‌رود دستورالعمل‌های موجود در توالی بحرانی تأثیر چشمگیری بر کارایی داشته باشند. به لحاظ ساختاری، دقت این تحلیل به‌شدت به شبیه‌سازی و (همانند همیشه) کیفیت مدل پردازنده در llvm وابسته است.

تحلیل گلوگاه در حال حاضر برای پردازنده‌های دارای بخش انتهایی ترتیبی (in-order backend) پشتیبانی نمی‌شود.

گزینه خط فرمان -all-stats آمار تکمیلی و شمارنده‌های کارایی را برای منطق اعزام (dispatch logic)، بافر بازآرایی (reorder buffer)، واحد کنترل بازنشستگی (retire control unit)، و پرونده ثبات‌ها (register file) فعال می‌کند.

در زیر نمونه‌ای از خروجی -all-stats تولیدشده توسط llvm-mca برای ۳۰۰ تکرار از مثال ضرب داخلی (dot-product) که در بخش‌های پیشین بررسی شد، آورده شده است.

Dynamic Dispatch Stall Cycles:
RAT     - Register unavailable:                      0
RCU     - Retire tokens unavailable:                 0
SCHEDQ  - Scheduler full:                            272  (44.6%)
LQ      - Load queue full:                           0
SQ      - Store queue full:                          0
GROUP   - Static restrictions on the dispatch group: 0
Dispatch Logic - number of cycles where we saw N micro opcodes dispatched:
[# dispatched], [# cycles]
 0,              24  (3.9%)
 1,              272  (44.6%)
 2,              314  (51.5%)
Schedulers - number of cycles where we saw N micro opcodes issued:
[# issued], [# cycles]
 0,          7  (1.1%)
 1,          306  (50.2%)
 2,          297  (48.7%)
Scheduler's queue usage:
[1] Resource name.
[2] Average number of used buffer entries.
[3] Maximum number of used buffer entries.
[4] Total number of buffer entries.
 [1]            [2]        [3]        [4]
JALU01           0          0          20
JFPU01           17         18         18
JLSAGU           0          0          12
Retire Control Unit - number of cycles where we saw N instructions retired:
[# retired], [# cycles]
 0,           109  (17.9%)
 1,           102  (16.7%)
 2,           399  (65.4%)
Total ROB Entries:                64
Max Used ROB Entries:             35  ( 54.7% )
Average Used ROB Entries per cy:  32  ( 50.0% )
Register File statistics:
Total number of mappings created:    900
Max number of mappings used:         35
*  Register File #1 -- JFpuPRF:
   Number of physical registers:     72
   Total number of mappings created: 900
   Max number of mappings used:      35
*  Register File #2 -- JIntegerPRF:
   Number of physical registers:     64
   Total number of mappings created: 0
   Max number of mappings used:      0

اگر به جدول Dynamic Dispatch Stall Cycles نگاه کنیم، می‌بینیم که شمارنده مربوط به SCHEDQ مقدار ۲۷۲ چرخه را گزارش می‌دهد. این شمارنده هر بار که منطق اعزام به دلیل پر بودن صف زمان‌بند (scheduler) نتواند یک گروه کامل را اعزام کند، افزایش می‌یابد.

با نگاه به جدول Dispatch Logic، مشاهده می‌کنیم که خط لوله تنها در ۵۱٫۵٪ مواقع قادر به اعزام دو ریزکد دستوری (micro opcode) بوده است. گروه اعزام در ۴۴٫۶٪ چرخه‌ها به یک ریزکد دستوری محدود شد که معادل ۲۷۲ چرخه است. آمارهای اعزام با استفاده از گزینه دستوری -all-stats یا -dispatch-stats قابل نمایش هستند.

جدول بعدی، یعنی Schedulers، نموداری هیستوگرام ارائه می‌دهد که تعداد ریزکدهای دستوری صادرشده (issued) در تعداد مشخصی از چرخه‌ها را نشان می‌دهد. در این حالت، از ۶۱۰ چرخه شبیه‌سازی‌شده، ۳۰۶ بار (۵۰٫۲٪) ریزکدهای تکی صادر شده و در ۷ چرخه هیچ ریزکدی صادر نشده است.

جدول Scheduler's queue usage میانگین و حداکثر تعداد مدخل‌های بافر (یعنی مدخل‌های صف زمان‌بند) استفاده‌شده در زمان اجرا را نشان می‌دهد. منبع JFPU01 به حداکثر ظرفیت خود رسیده است (۱۸ از ۱۸ مدخل صف). توجه داشته باشید که AMD Jaguar سه زمان‌بند را پیاده‌سازی می‌کند:

  • JALU01 - زمان‌بندی برای دستورالعمل‌های ALU.
  • JFPU01 - زمان‌بندی برای عملیات ممیز شناور (floating point).
  • JLSAGU - زمان‌بندی برای تولید نشانی (address generation).

هسته ضرب داخلی (dot-product) متشکل از سه دستورالعمل ممیز شناور است (یک ضرب برداری و به دنبال آن دو جمع افقی). این امر مشخص می‌کند چرا به نظر می‌رسد تنها از زمان‌بند ممیز شناور استفاده شده است.

پر شدن صف زمان‌بند، یا ناشی از زنجیره‌های وابستگی داده است یا استفاده غیربهینه از منابع سخت‌افزاری. گاهی اوقات، فشار منابع را می‌توان با بازنویسی هسته پردازشی (kernel) و استفاده از دستورالعمل‌های متفاوت که منابع زمان‌بند متفاوتی را مصرف می‌کنند، کاهش داد. زمان‌بندهای با صف کوچک در برابر گلوگاه‌های ناشی از وجود وابستگی‌های طولانی داده انعطاف‌پذیری کمتری دارند. آمارهای زمان‌بند با استفاده از گزینه دستوری -all-stats یا -scheduler-stats نمایش داده می‌شوند.

جدول بعدی، Retire Control Unit، هیستوگرامی را نشان می‌دهد که تعداد دستورالعمل‌های بازنشسته‌شده (retired) در تعداد مشخصی چرخه را نشان می‌دهد. در این حالت، از ۶۱۰ چرخه شبیه‌سازی‌شده، ۳۹۹ بار (۶۵٫۴٪) دو دستورالعمل در طی یک چرخه بازنشسته شده‌اند و در ۱۰۹ چرخه هیچ دستورالعملی بازنشسته نشده است. آمارهای بازنشستگی با استفاده از گزینه دستوری -all-stats یا -retire-stats نمایش داده می‌شوند.

آخرین جدول ارائه‌شده، Register File statistics است. هر پرونده ثبات فیزیکی (PRF) مورد استفاده توسط خط لوله در این جدول نشان داده شده است. در پردازنده AMD Jaguar دو پرونده ثبات وجود دارد: یکی برای ثبات‌های ممیز شناور (JFpuPRF) و دیگری برای ثبات‌های عدد صحیح (JIntegerPRF). این جدول نشان می‌دهد که از ۹۰۰ دستورالعمل پردازش‌شده، ۹۰۰ نگاشت ایجاد شده است. از آنجا که این نمونه ضرب داخلی تنها از ثبات‌های ممیز شناور استفاده کرده است، JFpuPRF مسئول ایجاد ۹۰۰ نگاشت بوده است. با این حال، مشاهده می‌کنیم که خط لوله در هر زمان مشخص حداکثر از ۳۵ مدخل از ۷۲ مدخل ثبات موجود استفاده کرده است. می‌توان نتیجه گرفت که PRF ممیز شناور تنها پرونده ثبات استفاده‌شده برای این مثال بوده و هرگز دچار کمبود منبع نشده است. آمارهای پرونده ثبات با استفاده از گزینه دستوری -all-stats یا -register-file-stats نمایش داده می‌شوند..sp در این مثال، می‌توان نتیجه گرفت که IPC عمدتاً به‌دلیل وابستگی‌های داده‌ای محدود شده است، نه فشار بر منابع.

این بخش جریان دستورالعمل را در خط لولهٔ پیش‌فرض llvm-mca و همچنین واحدهای عملیاتی درگیر در این فرایند شرح می‌دهد.

خط لولهٔ پیش‌فرض، توالی مراحل زیر را برای پردازش دستورالعمل‌ها پیاده‌سازی می‌کند:

  • اعزام یا Dispatch (دستورالعمل به زمان‌بندها اعزام می‌شود).
  • صدور یا Issue (دستورالعمل به خطوط لولهٔ پردازنده صادر می‌شود).
  • بازنویسی یا Write Back (دستورالعمل اجرا شده و نتایج بازنویسی می‌شوند).
  • بازنشستگی یا Retire (دستورالعمل بازنشسته می‌شود؛ نوشتن‌ها از نظر معماری تثبیت می‌شوند).

خط لولهٔ ترتیبی (in-order) توالی مراحل زیر را پیاده‌سازی می‌کند:

  • صدور ترتیبی یا InOrderIssue (دستورالعمل به خطوط لولهٔ پردازنده صادر می‌شود).
  • بازنشستگی یا Retire (دستورالعمل بازنشسته می‌شود؛ نوشتن‌ها از نظر معماری تثبیت می‌شوند).

ابزار llvm-mca فرض می‌کند که تمام دستورالعمل‌ها پیش از آغاز شبیه‌سازی رمزگشایی شده و درون یک صف قرار گرفته‌اند. بنابراین، مراحل واکشی و رمزگشایی دستورالعمل مدل‌سازی نمی‌شوند. گلوگاه‌های کارایی در بخش پیشین (frontend) شناسایی نمی‌شوند. همچنین، llvm-mca پیش‌بینی پرش (branch prediction) را مدل‌سازی نمی‌کند.

در طول مرحلهٔ اعزام، دستورالعمل‌ها به ترتیب برنامه از صف دستورالعمل‌های از پیش رمزگشایی‌شده برداشته می‌شوند و به‌صورت گروهی به زمان‌بندهای سخت‌افزاری شبیه‌سازی‌شده اعزام می‌گردند.

اندازهٔ یک گروه اعزام به در دسترس بودن منابع سخت‌افزاری شبیه‌سازی‌شده بستگی دارد. پهنای اعزام پردازنده به‌طور پیش‌فرض برابر با مقدار IssueWidth در مدل زمان‌بندی LLVM است.

یک دستورالعمل در صورتی می‌تواند اعزام شود که:

  • اندازهٔ گروه اعزام کوچک‌تر از پهنای اعزام پردازنده باشد.
  • ورودی‌های کافی در بافر بازترتیب (reorder buffer) وجود داشته باشد.
  • ثبات‌های فیزیکی کافی برای انجام تغییر نام ثبات‌ها (register renaming) وجود داشته باشد.
  • زمان‌بندها پر نباشند.

مدل‌های زمان‌بندی می‌توانند به‌صورت اختیاری مشخص کنند چه فایل‌های ثباتی (register files) روی پردازنده موجود است. llvm-mca از این اطلاعات برای مقداردهی اولیهٔ توصیف‌گرهای فایل ثبات استفاده می‌کند. کاربران می‌توانند با استفاده از گزینهٔ خط فرمان -register-file-size تعداد ثبات‌های فیزیکی را که به‌صورت سراسری برای تغییر نام ثبات‌ها موجود هستند، محدود کنند. مقدار صفر برای این گزینه به معنای نامحدود است. با دانستن اینکه چه تعداد ثبات برای تغییر نام در دسترس است، این ابزار می‌تواند توقف‌های اعزام (dispatch stalls) ناشی از کمبود ثبات‌های فیزیکی را پیش‌بینی کند.

تعداد ورودی‌های بافر بازترتیب که توسط یک دستورالعمل مصرف می‌شود، به تعداد ریزکدها (micro-opcodes) مشخص‌شده برای آن دستورالعمل توسط مدل زمان‌بندی مقصد بستگی دارد. بافر بازترتیب مسئول ردیابی پیشرفت دستورالعمل‌های "در حال اجرا" ("in-flight") و بازنشسته کردن آن‌ها به ترتیب برنامه است. تعداد ورودی‌های بافر بازترتیب به‌طور پیش‌فرض برابر با مقدار تعیین‌شده توسط فیلد MicroOpBufferSize در مدل زمان‌بندی مقصد است.

دستورالعمل‌هایی که به زمان‌بندها اعزام می‌شوند، ورودی‌های بافر زمان‌بند را مصرف می‌کنند. llvm-mca مدل زمان‌بندی را پرس‌وجو می‌کند تا مجموعه منابع بافرشدهٔ مصرفی توسط یک دستورالعمل را مشخص نماید. با منابع بافرشده همانند منابع زمان‌بند رفتار می‌شود.

هر زمان‌بند پردازنده یک بافر از دستورالعمل‌ها را پیاده‌سازی می‌کند. یک دستورالعمل باید در بافر زمان‌بند منتظر بماند تا عملوندهای ثبات ورودی آن در دسترس قرار گیرند. تنها در آن نقطه است که دستورالعمل واجد شرایط اجرا می‌شود و ممکن است برای اجرا (احتمالاً خارج از نوبت یا out-of-order) صادر گردد. تاخیرهای دستورالعمل توسط llvm-mca و به کمک مدل زمان‌بندی محاسبه می‌شوند.

زمان‌بند llvm-mca برای شبیه‌سازی چندین زمان‌بند پردازنده طراحی شده است. این زمان‌بند مسئول ردیابی وابستگی‌های داده‌ای و انتخاب پویای منابع پردازنده است که توسط دستورالعمل‌ها مصرف می‌شوند. این بخش مدیریت واحدهای منابع پردازنده و گروه‌های منابع را به یک مدیر منابع واگذار می‌کند. مدیر منابع مسئول انتخاب واحدهای منبعی است که توسط دستورالعمل‌ها مصرف می‌شوند. برای مثال، اگر یک دستورالعمل 1cy از یک گروه منابع را مصرف کند، مدیر منابع یکی از واحدهای دردسترس از آن گروه را انتخاب می‌کند؛ به‌طور پیش‌فرض، مدیر منابع از یک گزینش‌گر نوبت‌گردشی (round-robin) استفاده می‌کند تا تضمین نماید مصرف منابع به‌طور یکنواخت میان تمام واحدهای یک گروه توزیع می‌شود.

زمان‌بند llvm-mca در داخل خود دستورالعمل‌ها را به سه دسته گروه‌بندی می‌کند:

  • WaitSet: مجموعه‌ای از دستورالعمل‌ها که عملوندهای آن‌ها آماده نیستند.
  • ReadySet: مجموعه‌ای از دستورالعمل‌های آماده برای اجرا.
  • IssuedSet: مجموعه‌ای از دستورالعمل‌های در حال اجرا.

بسته به در دسترس بودن عملوندها، دستورالعمل‌هایی که به زمان‌بند اعزام می‌شوند یا در WaitSet یا در ReadySet قرار می‌گیرند.

در هر چرخه، زمان‌بند بررسی می‌کند که آیا دستورالعمل‌ها می‌توانند از WaitSet به ReadySet منتقل شوند، و آیا دستورالعمل‌های موجود در ReadySet می‌توانند به خطوط لولهٔ زیرین صادر شوند یا خیر. این الگوریتم دستورالعمل‌های قدیمی‌تر را نسبت به دستورالعمل‌های جدیدتر در اولویت قرار می‌دهد..SS مرحله بازنویسی و بازنشستگی (Write-Back and Retire Stage)

دستورالعمل‌های صادرشده از ReadySet به IssuedSet منتقل می‌شوند. در آنجا، دستورالعمل‌ها تا زمان رسیدن به مرحله بازنویسی (write-back) منتظر می‌مانند. در آن نقطه، آن‌ها از صف حذف شده و به واحد کنترل بازنشستگی (retire control unit) اطلاع داده می‌شود.

هنگامی که دستورالعمل‌ها اجرا می‌شوند، واحد کنترل بازنشستگی دستورالعمل را با پرچم "ready to retire" (آماده برای بازنشستگی) نشانه‌گذاری می‌کند.

دستورالعمل‌ها به ترتیب برنامه (program order) بازنشسته می‌شوند. بازنشستگی به فایل ثبات (register file) اطلاع داده می‌شود تا بتواند ثبات‌های فیزیکی را که در طول مرحله تغییر نام ثبات برای دستورالعمل تخصیص داده شده بودند، آزاد کند.

برای شبیه‌سازی اجرای خارج از نوبت عملیات‌های حافظه، llvm-mca از یک واحد بارگذاری/ذخیره‌سازی شبیه‌سازی‌شده (LSUnit) برای شبیه‌سازی اجرای حدسی بارگذاری‌ها و ذخیره‌سازی‌ها بهره می‌برد.

هر بارگذاری (یا ذخیره‌سازی) یک مدخل را در صف بارگذاری (یا ذخیره‌سازی) مصرف می‌کند. کاربران می‌توانند پرچم‌های -lqueue و -squeue را به ترتیب برای محدود کردن تعداد مدخل‌ها در صف‌های بارگذاری و ذخیره‌سازی مشخص کنند. صف‌ها به‌طور پیش‌فرض نامحدود هستند.

واحد LSUnit یک مدل سازگاری سست (relaxed consistency) را برای بارگذاری‌ها و ذخیره‌سازی‌های حافظه پیاده‌سازی می‌کند. قوانین عبارتند از:

1.
یک بارگذاری جدیدتر تنها در صورتی مجاز است از یک بارگذاری قدیمی‌تر پیشی بگیرد که هیچ ذخیره‌سازی یا سد (barrier) مداخله‌گری بین آن دو بارگذاری وجود نداشته باشد.
2.
یک بارگذاری جدیدتر مجاز است از یک ذخیره‌سازی قدیمی‌تر پیشی بگیرد، مشروط بر اینکه بارگذاری با آن ذخیره‌سازی تداخل آدرس (alias) نداشته باشد.
3.
یک ذخیره‌سازی جدیدتر مجاز نیست از یک ذخیره‌سازی قدیمی‌تر پیشی بگیرد.
4.
یک ذخیره‌سازی جدیدتر مجاز نیست از یک بارگذاری قدیمی‌تر پیشی بگیرد.

به‌طور پیش‌فرض، LSUnit با خوش‌بینی فرض می‌کند که بارگذاری‌ها با عملیات ذخیره‌سازی هم‌پوشانی آدرس ندارند (-noalias=true). با این فرض، بارگذاری‌های جدیدتر همواره مجازند از ذخیره‌سازی‌های قدیمی‌تر عبور کنند. در اصل، LSUnit هیچ تحلیلی برای تفکیک هم‌پوشانی آدرس‌ها (alias analysis) اجرا نمی‌کند تا پیش‌بینی کند چه زمانی بارگذاری‌ها و ذخیره‌سازی‌ها با یکدیگر تداخل ندارند.

توجه داشته باشید که در مورد حافظه ترکیبی نوشتاری (write-combining)، قانون شماره ۳ می‌تواند برای مجاز دانستن تغییر ترتیب عملیات ذخیره‌سازی غیرهم‌پوشان تعدیل شود. با این وجود، در حال حاضر هیچ راهی برای سست‌تر کردن بیشتر مدل حافظه وجود ندارد (-noalias تنها گزینه است). اساساً، گزینه‌ای برای تعیین نوع حافظه متفاوت (مانند write-back، write-combining، write-through و غیره) و در نتیجه ضعیف‌تر یا قوی‌تر کردن مدل حافظه ارائه نشده است.

سایر محدودیت‌ها عبارتند از:

  • واحد LSUnit نمی‌داند فوروارد ذخیره به بارگذاری (store-to-load forwarding) چه زمانی ممکن است رخ دهد.
  • واحد LSUnit هیچ شناختی از سلسله‌مراتب حافظه نهان (cache) و انواع حافظه ندارد.
  • واحد LSUnit روش شناسایی عملیات‌های ترتیبی‌ساز (serializing) و حائل‌های حافظه (memory fences) را نمی‌داند.

واحد LSUnit تلاشی برای پیش‌بینی اینکه آیا یک بارگذاری یا ذخیره‌سازی در حافظه نهان L1 با برخورد (hit) مواجه می‌شود یا خطا (miss)، انجام نمی‌دهد. این واحد تنها می‌داند که آیا یک دستورالعمل "MayLoad" و/یا "MayStore" هست یا خیر. برای بارگذاری‌ها، مدل زمان‌بندی یک تأخیر "خوش‌بینانه" از بارگذاری تا استفاده (load-to-use) ارائه می‌دهد (که معمولاً با تأخیر load-to-use در صورت برخورد در L1D مطابقت دارد).

ابزار llvm-mca (به‌تنهایی) اطلاعی از عملیات ترتیبی‌ساز یا دستورالعمل‌های مشابه سد حافظه ندارد. واحد LSUnit در گذشته به‌طور محافظه‌کارانه‌ای از پرچم‌های "MayLoad"، "MayStore" و اثرات جانبی مدل‌سازی‌نشده دستورالعمل استفاده می‌کرد تا تعیین کند آیا یک دستورالعمل باید به‌عنوان یک سد حافظه در نظر گرفته شود یا خیر. این رویکرد در کل نادقیق بود و تغییر یافت؛ به‌گونه‌ای که اکنون هر دستورالعمل دارای پرچم‌های IsAStoreBarrier و IsALoadBarrier است. این پرچم‌ها مختص mca بوده و به‌طور پیش‌فرض برای هر دستورالعمل برابر با false هستند. اگر نیاز باشد برای هر دستورالعملی یکی از این پرچم‌ها تنظیم شود، این کار باید در کلاس InstrPostProcess مربوط به معماری مقصد صورت گیرد. برای نمونه، به متد X86InstrPostProcess::postProcessInstruction در فایل llvm/lib/Target/X86/MCA/X86CustomBehaviour.cpp نگاه کنید.

یک سد بارگذاری/ذخیره‌سازی یک مدخل از صف بارگذاری/ذخیره‌سازی را مصرف می‌کند. یک سد بارگذاری/ذخیره‌سازی رعایت ترتیب بارگذاری‌ها/ذخیره‌سازی‌ها را تحمیل می‌کند. یک بارگذاری جدیدتر نمی‌تواند از یک سد بارگذاری عبور کند. همچنین، یک ذخیره‌سازی جدیدتر نمی‌تواند از یک سد ذخیره‌سازی عبور کند. یک بارگذاری جدیدتر باید تا زمان اجرای سد حافظه/بارگذاری منتظر بماند. یک سد بارگذاری/ذخیره‌سازی زمانی "اجرا می‌شود" که به قدیمی‌ترین مدخل در صف(های) بارگذاری/ذخیره‌سازی تبدیل شود. این موضوع همچنین از نظر ساختاری به این معنی است که تمام بارگذاری‌ها/ذخیره‌سازی‌های قدیمی‌تر اجرا شده‌اند.

در نتیجه، مجموعه کامل قوانین سازگاری بارگذاری/ذخیره‌سازی عبارتند از:

1.
یک ذخیره‌سازی نمی‌تواند از یک ذخیره‌سازی قبلی پیشی بگیرد.
2.
یک ذخیره‌سازی نمی‌تواند از یک بارگذاری قبلی پیشی بگیرد (صرف‌نظر از -noalias).
3.
یک ذخیره‌سازی باید منتظر بماند تا یک سد ذخیره‌سازی قدیمی‌تر به‌طور کامل اجرا شود.
4.
یک بارگذاری می‌تواند از یک بارگذاری قبلی پیشی بگیرد.
5.
یک بارگذاری نمی‌تواند از یک ذخیره‌سازی قبلی پیشی بگیرد مگر اینکه -noalias تنظیم شده باشد.
6.
یک بارگذاری باید منتظر بماند تا یک سد بارگذاری قدیمی‌تر به‌طور کامل اجرا شود.

پردازنده‌های ترتیبی به صورت یک مرحله واحد InOrderIssueStage مدل‌سازی می‌شوند. این مرحله واحدهای Dispatch، Scheduler و Load/Store را دور می‌زند. دستورالعمل‌ها به محض در دسترس قرار گرفتن ثبات‌های عملوند خود و برآورده شدن نیازمندی‌های منابع صادر می‌شوند. بسته به مقدار پارامتر IssueWidth در مدل زمان‌بندی LLVM، چندین دستورالعمل می‌توانند در یک چرخه صادر شوند.

پس از صدور، دستورالعمل به مجموعه IssuedInst منتقل می‌شود تا زمانی که آماده بازنشستگی شود. llvm-mca اطمینان حاصل می‌کند که عملیات نوشتن به‌ترتیب ثبت (commit) شوند. با این حال، اگر ویژگی RetireOOO برای حداقل یکی از نوشتن‌های دستورالعمل برقرار (true) باشد، به آن دستورالعمل اجازه داده می‌شود که نوشتن‌ها را ثبت کرده و خارج از نوبت بازنشسته شود.

به دلیل اینکه برخی از دستورالعمل‌ها به طور کامل درون مدل زمان‌بندی خود بیان نمی‌شوند، llvm-mca همیشه قادر به شبیه‌سازی بی‌نقص آن‌ها نیست. با این حال، اصلاح مدل زمان‌بندی همواره گزینه‌ای عملی نیست (شاید به این دلیل که دستورالعمل عمداً نادرست مدل‌سازی شده است یا رفتار آن بسیار پیچیده است). در این موارد می‌توان از کلاس CustomBehaviour برای اعمال مدل‌سازی صحیح دستورالعمل استفاده کرد (اغلب از طریق سفارشی‌سازی وابستگی‌های داده و شناسایی خطرهایی که llvm-mca هیچ راهی برای اطلاع از آن‌ها ندارد).

ابزار llvm-mca همراه با یک کلاس عمومی و چندین کلاس CustomBehaviour ویژه مقصد ارائه می‌شود. در صورتی که از پرچم -disable-cb استفاده شود یا کلاس CustomBehaviour ویژه آن مقصد وجود نداشته باشد، کلاس عمومی استفاده خواهد شد. (کلاس عمومی هیچ کاری انجام نمی‌دهد.) در حال حاضر، کلاس CustomBehaviour تنها بخشی از خط‌لوله ترتیبی است، اما برنامه‌هایی برای افزودن آن به خط‌لوله خارج از نوبت در آینده وجود دارد.

متد اصلی CustomBehaviour متد checkCustomHazard() است که از دستورالعمل فعلی و فهرستی از تمام دستورالعمل‌هایی که هنوز در خط‌لوله در حال اجرا هستند استفاده می‌کند تا مشخص کند آیا دستورالعمل فعلی باید گسیل شود یا خیر. به عنوان خروجی، این متد یک عدد صحیح را برمی‌گرداند که بیانگر تعداد چرخه‌هایی است که دستورالعمل فعلی باید متوقف شود (اگر عدد دقیق را نمی‌دانید، این مقدار می‌تواند یک برآورد کمتر از واقع باشد، و مقدار 0 نشان‌دهنده عدم توقف است).

اگر مایل به افزودن یک کلاس CustomBehaviour برای مقصدی هستید که قبلاً فاقد آن بوده است، برای مشاهده نحوه راه‌اندازی آن به یکی از پیاده‌سازی‌های موجود مراجعه کنید. این کلاس‌ها در پسانه ویژه مقصد (به عنوان مثال /llvm/lib/Target/AMDGPU/MCA/) پیاده‌سازی می‌شوند تا بتوانند به نمادهای پسانه دسترسی داشته باشند.

در معماری‌های خاص، اطلاعات زمان‌بندی برای دستورالعمل‌های معین شامل تمامی اطلاعات لازم برای شناسایی دقیق‌ترین کلاس زمان‌بندی نیست. برای نمونه، داده‌هایی که می‌توانند بر زمان‌بندی تأثیر بگذارند ممکن است در ثبات‌های CSR ذخیره شوند.

یک نمونه از این مورد در RISCV است، جایی که مقادیر موجود در ثبات‌هایی نظیر vtype و vl رفتار زمان‌بندی دستورالعمل‌های برداری را تغییر می‌دهند. از آنجا که MCA مقادیر موجود در ثبات‌ها را دنبال نمی‌کند، می‌توان از کامنت‌های ابزار دقیق (instrument comments) برای مشخص کردن این مقادیر استفاده کرد.

تابع اصلی InstrumentManager تابع getSchedClassID() است که به MCInst و تمامی ابزارهای دقیقی که برای آن MCInst فعال هستند دسترسی دارد. این تابع می‌تواند از ابزارها برای بازنویسی کلاس زمان‌بندی MCInst استفاده کند.

در RISCV، کامنت‌های ابزار دقیق حاوی اطلاعات LMUL توسط getSchedClassID() استفاده می‌شوند تا یک دستورالعمل برداری و LMUL فعال را به کلاس زمان‌بندی شبه‌دستورالعملی که آن دستورالعمل پایه و LMUL فعال را توصیف می‌کند نگاشت کنند.

ابزار llvm-mca همراه با چندین نما (View) نظیر Timeline View و Summary View ارائه می‌شود. این نماها عمومی هستند و می‌توانند با اکثر (اگر نگوییم همه) مقاصد کار کنند. اگر مایلید یک نمای جدید به llvm-mca اضافه کنید که به هیچ قابلیت پسانه‌ای فراتر از آنچه از قبل از طریق کلاس‌های لایه MC (مانند MCSubtargetInfo، MCInstrInfo و غیره) افشا شده نیاز ندارد، لطفاً آن را به دایرکتوری /tools/llvm-mca/View/ اضافه کنید. با این حال، اگر نمای جدید شما ویژه مقصد است و به نمادها یا قابلیت‌های افشانشده پسانه نیاز دارد، می‌توانید آن را در دایرکتوری /lib/Target/<TargetName>/MCA/ تعریف کنید.

برای فعال‌سازی این نمای ویژه مقصد، باید از کلاس CustomBehaviour مربوط به آن مقصد برای بازنویسی متدهای CustomBehaviour::getViews() استفاده کنید. ۳ گونه از این متدها بر اساس جایی که می‌خواهید نمای شما در خروجی ظاهر شود وجود دارد: getStartViews()، getPostInstrInfoViews() و getEndViews(). این متدها یک بردار از نماها برمی‌گردانند، بنابراین باید برداری حاوی تمام نماهای ویژه مقصد را برای مقصد مورد نظر برگردانید.

از آنجا که این نماهای ویژه مقصد (و وابسته به پسانه) به گونه‌های CustomBehaviour::getViews() نیاز دارند، در صورت استفاده از پرچم -disable-cb فعال نخواهند شد.

فعال کردن این نماهای سفارشی تأثیری بر نماهای غیرسفارشی (عمومی) نمی‌گذارد. برای فعال یا غیرفعال کردن آن نماها همچنان از آرگومان‌های معمولی خط فرمان استفاده کنید..SH "نویسندگان (AUTHORS)" توسط تیم LLVM نگهداری می‌شود (https://llvm.org).

2003-2026، پروژه LLVM

2026-07-15 22