diff --git "a/text/0000-\330\257\330\247\331\204\330\251-\331\205\331\202\330\247\330\267\330\271\330\251-\331\210\330\250\331\210\330\247\330\250\330\251-x86-intrcc.md" "b/text/0000-\330\257\330\247\331\204\330\251-\331\205\331\202\330\247\330\267\330\271\330\251-\331\210\330\250\331\210\330\247\330\250\330\251-x86-intrcc.md" new file mode 100644 index 0000000..5b7c561 --- /dev/null +++ "b/text/0000-\330\257\330\247\331\204\330\251-\331\205\331\202\330\247\330\267\330\271\330\251-\331\210\330\250\331\210\330\247\330\250\330\251-x86-intrcc.md" @@ -0,0 +1,208 @@ +- **عنوان المقترح:** مُعدِّل `دالة مقاطعة` (خفض `x86_intrcc`) + مدمج `حمل_سجل_المهمة` (ltr) +- **النطاق:** لغة `text/` +- **تاريخ البدء:** 2026-08-24 +- **رقم الـ RFC:** (يُترك فارغاً حتى الدمج) +- **الحالة:** مقترَح +- **Issue التتبُّع:** (يُترك فارغاً حتى القبول) + +# ملخّص + +قدرتان متلازمتان تُطفئان أكعاب دخول المقاطعات في نواة النحلة: + +1. **مُعدِّل `دالة مقاطعة`** — مُعدِّل يلي `دالة` قبل الاسم (نمط `دالة لا_ترجع` + المرسوم في RFC لا_ترجع) يجعل المصرِّف يخفض الدالّة باتفاقية النداء + `x86_intrcc` في LLVM، فيتولّد لها prologue/epilogue بوابةِ مقاطعةٍ كاملان: + حفظ سجلّات الأغراض العامّة المتطايرة، تسويةُ المكدّس لرمز الخطأ، والعودة + بـ`iretq`. لا حاجة لمدمج `ارجع_من_مقاطعة()` — العودة تأتي من الاتفاقية ذاتها. +2. **مدمج `حمل_سجل_المهمة`** (ltr) — نظير `حمل_جدول_مقاطعات` (lidt) + و`حمل_جدول_واصفات` (lgdt) القائمَين، يُحمِّل سجلَّ المهمّة (TR) بواصف TSS — + البوّابة اللازمة لمكدّسات IST التي تعتمد عليها معالجات المقاطعات الجادّة. + +# الدافع (Motivation) + +المتطلَّب المنبعيّ صريح في خارطة نحلة على موضعين: +- بند **م-لغة.3** (`توثيق/خارطة_المراحل.md:101`): «سمة x86_intrcc التي تنقل + أكعابَ دخول المقاطعات إلى لغة ص **+ خيار حفظ SSE (fxsave/fxrstor أو + -mgeneral-regs-only المكافئ)**». +- شرط قبول البند المستهلِك له في `roadmap.json` (feature-14): «سمة + `x86_intrcc` مسدودةٌ منبعيًّا في مستودع اللغة عبر RFC لا ملتفٌّ عليها». + +المرحلة 3.6أ هناك منجزة — IDT يُبنى بلغة ص فعلًا — وأكعابُ الدخول (حفظ +السجلّات + `fxsave` + `iretq`) جزءٌ من `مقاطعات.S` لا يُطفأ إلا بهذا +المُعدِّل. (أرقام الأسطر تُقاس من عدّاد حارس ميزانيّة الأسمبلي في مستودع +نحلة لا تُنثر هنا — الملفّ يحوي أيضًا أغلفةً أخرى خارج نطاق هذا المقترح.) + +وبدون المُعدِّل: معالجُ مقاطعةٍ مكتوبٌ بص يدخل ويعمل، لكنّه **لا يستطيع +الرجوع** — فلا مقاطعة مؤقّت، فلا جدولة، فلا نظام تشغيل؛ برامج خطّية فقط +(المقيس ب١٢٥ في بطارية مجسّات نحلة). + +وبديل «مدمج `ارجع_من_مقاطعة()` يبثّ iretq» مرفوض هندسيًّا: `iretq` داخل جسم +دالّة عاديّة ذات prologue اعتياديّ (`push rbp`) تعود على إطارٍ مكسور وسجلّاتٍ +غير محفوظة — خطأ صامت لا يلتقطه اختبار. بوّابة المقاطعة **خاصيّة دالّةٍ لا +تعليمة**، ومكانها اتفاقية النداء. + +# الشرح التوجيهي (Guide-level explanation) + +```sad +دالة مقاطعة عالج_مقاطعة_المؤقت(رقم عنوان_الإطار) + زد_عداد_النبضات +نهاية + +دالة هيئ_المقاطعات() + سجل_معالج_مقاطعة(32، عنوان_رمز("عالج_مقاطعة_المؤقت")) + حمل_سجل_المهمة(محدد_مقطع_حالة_المهمة) # ltr — بعد بناء TSS/IST بلغة ص +نهاية +``` + +- المعامل الأوّل إلزاميّ: إطار المقاطعة الذي يدفعه العتاد. سطحيًّا يُصرَّح + `رقم` ويُقرأ عنوانًا؛ **بنيويًّا يخفضه المصرِّف مؤشّرًا بسمة `byval`** + (انظر الشرح المرجعي — هذا شرط مُصادِق LLVM لا خيار). +- معامل ثانٍ اختياريّ `رقم`: رمز الخطأ (لاستثناءات العتاد التي تدفعه). +- العائد `فراغ` حصرًا. أيّ توقيع آخر خطأُ تصريفٍ من الكتالوج. +- الدالّة **لا تُنادى** — لا نداءً مباشرًا ولا عبر مرجع دالّة؛ تُؤخذ عنوانًا + حصرًا عبر `عنوان_رمز`. المخالفة خطأ دلاليّ مشترك المحرّكات. +- **جسم المعالج أغراضٌ عامّة فقط**: أيّ عمليّة عشريّة/متّجهيّة داخل + `دالة مقاطعة` خطأ تصريف (المرحلة الأولى — انظر «حالة SSE» أدناه). + +# الشرح المرجعي (Reference-level explanation) + +## الأنبوب — مرآة `لا_ترجع` (سبع محطّات) + +| المحطّة | الموضع (نظير لا_ترجع) | +|---|---| +| كلمة سياقيّة في SoT | `language-truth/keywords.yaml` (نظير `KW-CTX-040` — تنبيه: هذا المعرّف مكرّر اليوم للسطرين 119 و171؛ الكلمة الجديدة تأخذ معرّفًا فريدًا ويُبلَّغ التصادم القائم) | +| التحليل بعد `دالة` قبل الاسم | `shared/parser/src/declarations/parser_declarations.cpp:65-77` | +| الضبط على `FunctionDecl` (٣ مواقع) | `parser_declarations.cpp:152, 437, 466` | +| حقل AST بولياني | `shared/ast/include/declarations.h` (نظير `isNoReturn`) | +| النقل إلى SIR | `compiler/src/frontend/sir_builder_functions.cpp` | +| حقل SIR | `compiler/include/frontend/sir_module.h` | +| الخفض | `compiler/src/backend/llvm/builders/oop/functions2_ops.cpp` | + +## عقد الخفض (شروط مُصادِق LLVM — ليست اختيارات) + +- `fn->setCallingConv(llvm::CallingConv::X86_INTR)` عند `Function::Create` + قبل أيّ منطق ABI (السابقة: `llvm_multiboot.cpp` يضبط `Win64` لدالّة مولَّدة). +- **المعامل الأوّل `ptr` بسمة `byval(بنية_الإطار)` وجوبًا** — المُصادِق يرفض + الوحدة بغيرها («Calling convention parameter requires byval»). بنية الإطار + الدنيا: خمس كلمات 64-بت (RIP، CS، RFLAGS، RSP، SS). التصريح السطحيّ `رقم` + يُجسَّر داخل المقدّمة بتحويل المؤشّر عددًا (`ptrtoint`) فيرى كاتبُ ص + عنوانًا خامًا كما اعتاد في مدمجات العتاد. +- رمز الخطأ (المعامل الثاني إن وُجد) `i64` على x86_64؛ الاتفاقية تتولّى + تسوية المكدّس قبل `iretq`. +- **`noredzone` على الدالّة وجوبًا**: الاتفاقية لا تضبطها، ومعالجٌ يعتمد + المنطقة الحمراء يُفسِده تداخل NMI. (يُقاس أوّلًا: إن كان مسار `--حر` يعطّل + المنطقة الحمراء وحدةً كاملةً فالسمة توثيقٌ تحصينيّ؛ وإلّا فهي السدّ.) +- علمٌ بولياني مطبوع عبر الأنبوب لا سلسلة نصّية — فلا يقع في فخّ «السمة + المجهولة تُقبل بلا أثر» القائم في مسار `[[سمة]]`. + +## حالة SSE (المتطلَّب المنبعيّ الثاني) + +`x86_intrcc` يحفظ سجلّات الأغراض العامّة، **ولا يحفظ حالة FPU/SSE ولا +MXCSR** — ونحلة تملك مسبارًا حيًّا (زرع xmm7 عبر المقاطعة) سيحمرّ لو أُفسدت. +قرار المرحلة الأولى: **مكافئ `-mgeneral-regs-only` تصريفيًّا** — حارسٌ يرفض +أيّ تعليمة عشريّة/متّجهيّة (ولو مولَّدةً ضمنًا) داخل جسم `دالة مقاطعة` +وكلِّ ما يُنادى منه في الوضع الحرّ يبقى على عاتق الكاتب (الجسم لا ينادي — +انظر مصفوفة التراكيب). خيارُ `fxsave/fxrstor` المولَّد (منطقة 512 بايت +بمحاذاة 16) يُرحَّل مُعدِّلَ وسيطٍ لاحقًا (`مقاطعة(احفظ_العائم)`) إن احتاجته +معالجات تلمس SSE. + +## الحرّاس (كلّها أخطاء كتالوج، لا سقوط صامت) + +| # | الحارس | الطبقة | +|---|---|---| +| 1 | التوقيع `(رقم[, رقم]) -> فراغ` | **الدلاليّة المشتركة** (`shared/semantic/`) — فتصل المحرّكات كلّها، لا الخفض وحده (درس توحيد SEM018) | +| 2 | منع النداء (مباشرًا وعبر مرجع دالّة/«أي») — العنوان عبر `عنوان_رمز` حصرًا | الدلاليّة المشتركة | +| 3 | لا عمليّات عشريّة/متّجهيّة في الجسم | الدلاليّة المشتركة | +| 4 | بوّابة المعماريّة: `x86_64` حصرًا في المرحلة الأولى (قرار نحلة «64-بت فقط»؛ i386 يختلف عقده — رمز خطأ i32 وعودة `iret` — فيُرجأ حتى يطلبه عميل) | الخفض (ثالوث الهدف) | +| 5 | بوّابة الوضع الحرّ: المُعدِّل يُرفض مستضافًا | الخفض | + +## مصفوفة تراكيب المُعدِّلات + +| التركيب | الحكم | +|---|---| +| `دالة لا_ترجع مقاطعة` | **تنافر تصريفيّ** — المقاطعة تعود بـ`iretq` وNoReturn يبثّ `unreachable` بعد النداء: دلالتان متعارضتان | +| `دالة خارجية` + مقاطعة | تنافر — المعالج يلزمه جسم | +| `متزامنة` / مولِّدة + مقاطعة | تنافر — لا حالة مُعلَّقة داخل بوّابة عتاد | + +## مدمج `حمل_سجل_المهمة` (ltr) + +على نمط `LOWLEVEL_GDT_LOAD` القائم (تسجيل في `builtins/compiler_os_core.yaml` +⇒ توليد ⇒ بناء SIR في `builtins_os_core.cpp` ⇒ خفض في `lowlevel_ops.cpp`)، +**لكن بقالبٍ مستقلّ لا بإعادة استعمال `emitFreestandingTableLoad`**: القيد +الموثَّق في `dialects/assembly_mnemonics/i686.yaml:167` — `ltr` تأخذ `r/m16` +فيرفضها المُجمِّع بعرض سجلٍّ كامل؛ يلزم القيد `${0:w}` ⇒ `ltr %ax` (الآليّة +قائمة ومقيسة في `statement_asm.cpp` واختبار `test_task_register_i686.py`). + +**تسوية معجميّة لازمة**: لفظ اللهجة التجميعيّة `حمّل_سجل_المهمة` قائم في +`i686.yaml:167` وملاحظتُه تنصّ «لا مدمج قانونيّ يقابله» — والمصرِّف يجرّد +الشدّة فيتماهى الاسمان كتابيًّا. ضمن التنفيذ: تُحدَّث الملاحظة لتقول إنّ +المدمج القانونيّ صار قائمًا (الفضاءان معجميّان منفصلان — تعبيرُ لغةٍ مقابل +لفظِ تجميع — والتجانس مقصود لا تصادم). + +## المفسّر والمحرّكات الأخرى + +حرّاس التوقيع والنداء والجسم في الطبقة الدلاليّة المشتركة فيفرضها المفسّر +والمصرِّف سواء (لا ازدواج دلاليّ). أمّا **الأثر التنفيذيّ** (الاتفاقية) فلا +معنى له في المفسّر — قبول ضمنيّ بلا أثر، نمط `@تمثيل_سي` المرسوم. الخلفيّة +الأصليّة بلا LLVM: رفضُ بناءٍ صريح مبدئيًّا (دَين مُعلَن يُسدّ حين يبلغها +مسار الوضع الحرّ). + +# السلبيات (Drawbacks) + +- المُعدِّل أوّل بابٍ معماريّ الطابع في سطح اللغة (تخفّفه بوّابة المعماريّة + الصريحة، ولمعماريّات أخرى نظائر مستقبليّة تحت المُعدِّل نفسه). +- `X86_INTR` يحفظ كلّ سجلّات الأغراض العامّة المستعملة — أثقل من كعبٍ يدويٍّ + محسوبِ السجلّات؛ ثمنٌ مقبول مقابل الصحّة البنيويّة. +- حارس «لا عشريّ في الجسم» أضيق من الضرورة (يرفض معالجًا يريد fxsave يدويًّا) + — يُوسَّع لاحقًا بمُعدِّل الوسيط. + +# أعمال سابقة (Prior art) + +- **Rust**: سمة `x86-interrupt` غير المستقرّة (`#[naked]` بديلًا) — التجربة + هناك أثبتت أنّ البوّابة **خاصيّةُ دالّةٍ** لا تعليمةٌ في جسمها، وأنّ إلزام + المعامل الأوّل مؤشّرَ إطارٍ يمنع أكثر الأخطاء شيوعًا (إطارٌ مقروءٌ من مكدّسٍ + مزاح). +- **C/clang و GCC**: `__attribute__((interrupt))` على x86 — العقد نفسه + (`byval` للإطار، معاملٌ ثانٍ لرمز الخطأ، عودةٌ بـ`iretq`)، ومعه القيدُ + المعروف أنّ الاتفاقيّة **لا تحفظ حالة SSE/MXCSR** — وهو ما يبرّر حارسَ + «لا عشريّ في الجسم» في المرحلة الأولى بدل `fxsave` مولَّد. +- **داخل ص**: مُعدِّل `لا_ترجع` هو السابقة البنيويّة الحيّة — أنبوبُه السباعيّ + (كلمة سياقيّة في مصدر الحقيقة ⇒ تحليل ⇒ حقل AST ⇒ SIR ⇒ خفض) هو القالبُ + الذي يسير عليه هذا المُعدِّل حرفًا بحرف؛ و`حمل_جدول_مقاطعات`/`حمل_جدول_واصفات` + سابقةُ المدمج المرافق. + +# البدائل (Alternatives) + +- **مدمج `ارجع_من_مقاطعة()`**: مرفوض (إطار مكسور — انظر الدافع). +- **سمة `[[معالج_مقاطعة]]`**: أنبوبها أقصر (ملفّان)، لكنّها نصٌّ خام يقبل + الخطأ الإملائيّ بصمت، والمتطلَّب المنبعيّ صيغ مُعدِّلًا («دالة مقاطعة»). +- **fxsave المولَّد من المرحلة الأولى**: يؤجَّل — يُدخل 512 بايت لكلّ إطار + معالجٍ وأغلبُ المعالجات لا تلمس العائم؛ الحارس التصريفيّ يحفظ الصحّة + بثمن صفر. +- **إبقاء الأكعاب في `مقاطعات.S`**: يخالف شرط «مسدودة منبعيًّا لا ملتفّ + عليها» المُعلن في شرط قبول feature-14. + +# أسئلة غير محسومة (Unresolved questions) + +- **الاسم**: `دالة مقاطعة` (طلب نحلة الحرفيّ؛ تُقرأ إضافةً «دالّةُ مقاطعةٍ») + أم `دالة معالجة_مقاطعة` (صفة مؤنّثة تطابق قاعدة «الموصوف ثمّ الصفة»)؟ + التوصية الأوليّة: `مقاطعة` — أقصر، وطلبها العميل نصًّا، وقراءتها الإضافيّة + سليمة نحوًا. +- تمثيل بنية الإطار في سطح اللغة لاحقًا: هل تُعرَّف بنية `@تمثيل_سي` قياسيّة + (`إطار_مقاطعة`) بدل `رقم` الخام؟ (الخفض `byval` قائم في الحالين؛ الفرق + سطحيّ.) +- قياس تعطيل المنطقة الحمراء في مسار `--حر` (يحسم هل `noredzone` سدّ أم + توثيق — انظر عقد الخفض). + +# أعمال مستقبلية (Future possibilities) + +- **مُعدِّل وسيط `مقاطعة(احفظ_العائم)`**: يولّد `fxsave/fxrstor` على منطقة + 512 بايت بمحاذاة 16 لمعالجاتٍ تلمس العائم — يرفعُ حارسَ «لا عشريّ» عن + الدوالّ التي تطلبه صراحةً بثمنٍ معلَن. +- **بنية `إطار_مقاطعة` قياسيّة** بـ`@تمثيل_سي` بدل `رقم` الخام، فيقرأ الكاتبُ + `RIP`/`RFLAGS` بأسمائها لا بإزاحاتٍ محسوبةٍ يدويًّا. +- **نظائر معماريّة**: `aarch64` (بوّابةُ استثناءٍ عبر `VBAR_EL1`) و`riscv` + (`mtvec`) تحت المُعدِّل نفسِه — البوّابةُ الصريحةُ في هذا المقترح تجعل + التوسيعَ إضافةَ فرعٍ لا تغييرَ سطحٍ. +- **`دالة مقاطعة` في الخلفيّة الأصليّة بلا LLVM**: توليدُ الكعبِ يدويًّا + (حفظُ السجلّاتِ ثمّ `iretq`) حين يبلغُها مسارُ الوضعِ الحرّ.