From 2eea5754834322af39c0dc5e0029d155c232e15f Mon Sep 17 00:00:00 2001 From: Sad Lang Dev Date: Tue, 21 Jul 2026 03:49:52 +0300 Subject: [PATCH] =?UTF-8?q?RFC:=20=D8=B9=D9=86=D9=88=D8=A7=D9=86=5F=D8=B1?= =?UTF-8?q?=D9=85=D8=B2=20=E2=80=94=20=D8=A3=D8=AE=D8=B0=20=D8=B9=D9=86?= =?UTF-8?q?=D9=88=D8=A7=D9=86=20=D8=B1=D9=85=D8=B2=20=D8=A7=D9=84=D8=B1?= =?UTF-8?q?=D8=A7=D8=A8=D8=B7=20(address-of-symbol)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit مدمج عنوان_رمز("اسم") يعيد عنوان رمز رابط خارجيّ (دالّة/بيانات/رمز .ld) كـرقم، موسَّعًا بالأصفار من عرض مؤشّر الهدف. يُطفئ نمط الجالب *_addr (~25 دالّة C في نواة النحلة) ويصل إلى رموز الرابط البحتة (_kernel_start/_stk) بلا مخزن C. اللبنة 3.13 من موجة «إطفاء C». الخفض يحاكي اقرأ_سجل_تحكم + emitPtrToInt: getOrInsertGlobal(sym) بربط خارجيّ ⇒ ptrtoint ⇒ i64. يقبله المترجم (حرّ+مستضاف FFI)، يرفضه المفسّر. خطأ SEM جديد للوسيط غير الثابت. Co-Authored-By: Claude Opus 4.8 --- ...31\204\330\261\330\247\330\250\330\267.md" | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 "text/0000-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\243\330\256\330\260-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\247\331\204\330\261\330\247\330\250\330\267.md" diff --git "a/text/0000-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\243\330\256\330\260-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\247\331\204\330\261\330\247\330\250\330\267.md" "b/text/0000-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\243\330\256\330\260-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\247\331\204\330\261\330\247\330\250\330\267.md" new file mode 100644 index 0000000..41f0086 --- /dev/null +++ "b/text/0000-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\243\330\256\330\260-\330\271\331\206\331\210\330\247\331\206-\330\261\331\205\330\262-\330\247\331\204\330\261\330\247\330\250\330\267.md" @@ -0,0 +1,254 @@ +- **عنوان المقترح:** `عنوان_رمز` — أخذ عنوان رمز الرابط (address-of-symbol) +- **النطاق:** لغة `text/` +- **تاريخ البدء:** 2026-07-21 +- **رقم الـ RFC:** (يُترك فارغاً حتى الدمج) +- **الحالة:** مقترَح (PR مفتوح) +- **Issue التتبُّع:** (يُترك فارغاً حتى القبول) + +# ملخّص + +مدمج جديد `عنوان_رمز("اسم_الرمز")` يعيد عنوان رمز رابط خارجيّ (دالّة أو بيانات، +عامّ ص أو رمز رابط `.ld`) كقيمة `رقم`. يتيح لبرامج ص — خاصّةً النوى في الوضع +الحرّ — الإشارة إلى عناوين الرموز مباشرةً بدل تغليفها بجالبات C صغيرة على نمط +`unsigned long long bee_idt_addr(void) { return (unsigned int)&bee_idt; }`. +النتيجة المرئيّة: يُطفَأ نمط الجالب `*_addr` كاملًا (~25 دالّة C في نواة النحلة)، +وتصبح عناوين الرموز في متناول ص أصالةً. + +# الدافع (Motivation) + +في هجرة نواة النحلة (nahla) إلى ص الخالصة، بلغ المنطق كلّه لغة ص، وبقيت أرضيّة C +**آليّة لا سياسة**. حلّل تقرير أميليا (2026-07-20) هذه الأرضيّة فوجد أنّ **الرافعة +الأعلى أثرًا لإطفاء C هي غياب أخذ عنوان رمز**: ص لا تملك طريقةً لأخذ عنوان رمز +(رابط أو عامّ)، فيُبقي هذا النقص وحده ~25 جالب `*_addr` وكلّ مخازن `.bss`/`.rodata` +حبيسةً في C. + +النمط المتكرّر في `تراب.c` اليوم: + +```c +/* ⚠️ ABI: ص تعلن الجالب «رقم» (i64) ⇒ unsigned long long لتصفير edx */ +unsigned long long bee_idt_addr(void) { return (unsigned int)bee_idt; } +unsigned long long bee_probe_hits_addr(void){ return (unsigned int)&bee_probe_hits; } +unsigned long long bee_kernel_start_addr(void){ return (unsigned int)_kernel_start; } +``` + +وفي ص تُصرَّح كدوالّ خارجيّة وتُستدعى: + +```sad +دالة خارجية("bee_idt_addr") رقم عنوان_جدول_المقاطعات() +... +متغير جدول = عنوان_جدول_المقاطعات() +``` + +كلّ جالب هو غلاف تافه لتعبير واحد `(unsigned int)&رمز`. وجودها في C ليس لأنّه +منطق مميَّز، بل لأنّ ص لا تعرف كيف تأخذ العنوان. حالات الاستخدام الواقعيّة: + +- **رموز الرابط البحتة** (لا مخزن C): `_kernel_start`/`_kernel_end`/`_stk` من + `ربط.ld`. لا يمكن حتى كتابة جالب C لها إلا بتصريح `extern`؛ عنوان_رمز يصل إليها + مباشرةً. +- **مخازن `.bss`/`.rodata`**: عدّادات المقاطعات، الخريطة البتّيّة لـPMM، خريطة + set-1 للمفاتيح، نسخة mmap، أرشيف SFA1 — كلّها تُقرأ/تُكتب من ص عبر + `اقرأ_ذاكرة32`/`اكتب_ذاكرة32` على عنوان يأتي اليوم من جالب C. +- **جداول ودوالّ أسمبليّة**: `bee_idt`، `bee_isr_stub_table`، `bee_gdt`. + +بلا هذا المدمج تبقى النواة معتمدةً على ~25 دالّة C لا تُضيف شيئًا سوى العنونة. + +# الشرح التوجيهي (Guide-level explanation) + +`عنوان_رمز(اسم)` مدمج يأخذ اسم رمز رابط (سلسلة حرفيّة ثابتة) ويعيد عنوانه وقت +التشغيل كـ`رقم`. الاسم يُطابَق كما يظهر في الربط (لا يخضع لتشكيل ولا ترجمة): + +```sad +# بدل تصريح جالب C وغلافه، خذ العنوان مباشرةً: +متغير جدول = عنوان_رمز("bee_idt") +متغير بداية_النواة = عنوان_رمز("_kernel_start") + +# ثمّ استعمله كأيّ عنوان: +اكتب_ذاكرة32(جدول، 0) +متغير حجم = عنوان_رمز("_kernel_end") - بداية_النواة +``` + +يُغني هذا عن نمط «الجالب الخارجيّ»: + +```sad +# قبل — تصريح + استدعاء: +دالة خارجية("bee_probe_hits_addr") رقم عنوان_عداد_المسبار() +متغير ع = عنوان_عداد_المسبار() + +# بعد — سطر واحد، بلا جالب C: +متغير ع = عنوان_رمز("bee_probe_hits") +``` + +القيمة العائدة `رقم` (i64). على هدف 32-بت (i686 الحرّ) يكون العنوان 32-بت +يُوسَّع بالأصفار إلى 64-بت — مطابقٌ تمامًا لدلالة `(unsigned int)&رمز` المعادة +كـ`unsigned long long` في الجالب C (تصفير النصف الأعلى). + +**التشخيص:** إن كان الوسيط سلسلةً غير ثابتة (متغيّر وقت تشغيل) أو فارغة، يُصدر +المترجم خطأ ترجمة واضحًا (رمز خطأ جديد، انظر أدناه) لأنّ اسم الرمز يجب أن +يُعرَف وقت الترجمة كي يُصدَر كـ relocation. + +``` +خطأ: عنوان_رمز يتطلّب اسم رمز ثابتًا (سلسلة حرفيّة)، لا تعبيرًا وقت تشغيل. +``` + +# الشرح المرجعي (Reference-level explanation) + +## مصدر الحقيقة (`language-truth/`) + +إدخال مدمج جديد في `language-truth/builtins/compiler_os_core.yaml` (المجموعة +`CompilerCpuCtl` نفسها التي تضمّ مدمجات النظام الحرّ، أو مجموعة جديدة +`CompilerSymbol` إن فُضِّل الفصل): + +```yaml +- cpp_id: SYMBOL_ADDR + canonical: عنوان_رمز + category: MODULE_FUNCTION + require_import: true # يلزمه استيراد وحدة النظام + compiler_strategy: INLINE + params: + - { name: اسم, type: نص } # سلسلة حرفيّة ثابتة + returns: رقم +``` + +ثمّ يُعاد توليد `shared/builtins/generated/builtin_registry_generated.h` عبر +`scripts/codegen/gen_builtins_registry.py` (يظهر الاسم كـ`Bn::…::SYMBOL_ADDR`). + +## المحلل المعجمي/النحوي (Lexer/Parser) + +لا رمز ولا قاعدة جديدة: `عنوان_رمز` مدمج عاديّ يُستدعى كدالّة، فيمرّ بمسار +الاستدعاء القائم. وسيطه سلسلة حرفيّة تُحلَّل كأيّ `STRING_LITERAL`. + +## الشجرة المجردة (AST) + +لا عقدة جديدة. الوسيط سلسلة حرفيّة تصل إلى frontend عبر `BuildResult` ثابت +(`isConstant && type == String`)، فيُبنى `SIROperand::ConstantString(القيمة)` +(النصّ يُخزَّن في `op.name`). + +## المفسّر + +في التنفيذ المستضاف (المفسّر) لا يوجد فضاء رموز رابط بالمعنى نفسه. الخيار +المقترح: **يرفض المفسّر `عنوان_رمز` بخطأ «غير مدعوم في المفسّر»** (كما تُعامَل +مدمجات النظام الحرّ الأخرى)، لأنّ دلالته «عنوان رابط» بلا معنًى وقت تفسير. هذا +متّسق مع كون المدمج أداةً للوضع الحرّ/الترجمة. (بديل مطروح في الأسئلة غير +المحسومة: دعم رموز مستضافة معروفة عبر `dlsym`.) + +## المترجم (sadc): SIR / LLVM IR codegen + +1. **SIROpcode جديد** `LOWLEVEL_SYMBOL_ADDR` في `compiler/include/frontend/sir_types.h` + (بجوار `LOWLEVEL_CPU_READ_CR`). +2. **البناء (frontend)** في `builtins_os_core.cpp`، محاكاةً لـ`CPUCTL_4` + (`اقرأ_سجل_تحكم`): تعليمة تعيد `Integer`، وسيطها `argOperands[0]` + (ConstantString لاسم الرمز): + ```cpp + std::string r = b_.newTempRegister(); + SIRInstruction inst(SIROpcode::LOWLEVEL_SYMBOL_ADDR); + inst.result = SIROperand::Register(r, SadTypeKind::Integer); + inst.operands.push_back(argOperands[0]); // ConstantString + b_.currentBlock_->instructions.push_back(inst); + return BuildResult(r, SadTypeKind::Integer); + ``` +3. **التوجيه (backend)** في `instr_lowlevel_ops.cpp`: `case LOWLEVEL_SYMBOL_ADDR`. +4. **الخفض (backend)** `emitLowlevelSymbolAddr` في `lowlevel_ops.cpp`: + - اقرأ اسم الرمز: `const std::string& sym = inst->operands[0].name;` + - تحقّق أنّه ConstantString غير فارغ؛ وإلا أصدِر خطأ SEM (انظر نظام الأخطاء). + - اجلب/أنشئ الرمز الخارجيّ: + ```cpp + llvm::Constant* g = module_->getFunction(sym); // إن كان دالّة مصرّحة + if (!g) g = module_->getOrInsertGlobal(sym, i8Ty); // وإلا رمز بيانات خارجيّ + // (getOrInsertGlobal ينشئ GlobalVariable بربط خارجيّ عند الحاجة) + llvm::Value* addr = builder_->CreatePtrToInt(g, i64Ty, "symaddr"); + bindLowlevelResult(cg_, inst, addr); + ``` + - `CreatePtrToInt` من مؤشّر 32-بت (i686) إلى i64 يوسّع بالأصفار ⇒ مطابقة + دلالة الجالب. على هدف 64-بت يكون المؤشّر 64-بت ⇒ لا توسيع (سلوك صحيح + أيضًا). + - النمط المرجعيّ للـ`ptrtoint` قائم في `arith_extras.cpp::emitPtrToInt`. +5. **التصريحات**: `emitLowlevelSymbolAddr` في `lowlevel_codegen.h` وغلاف + التفويض في `llvm_codegen.h`. + +**الوضعان**: الخفض واحد للمستضاف والحرّ (relocation رمز خارجيّ)، لكن المفسّر +يرفضه (أعلاه)، والمترجم المستضاف يقبله (استعمال FFI مشروع). + +## نظام الأخطاء + +رمز خطأ جديد في `language-truth/errors/semantic.yaml` (مثلًا `SEM_SYMBOL_ADDR_ARG`) +لحالة الوسيط غير الثابت/الفارغ، على نمط `SEM020` المُضاف في اللبنة 3.12: +`عنوان_رمز يتطلّب اسم رمز ثابتًا (سلسلة حرفيّة)`. ويُضاف يدويًّا إلى تعداد +`error_codes.h` (المولِّد يتحقّق لا يضيف). + +## الأدوات + +- **LSP/الإكمال**: يظهر `عنوان_رمز` تلقائيًّا (مولَّد من الكتالوج). +- **المنسّق/sadinfo**: لا أثر خاصّ. + +## التوافق الخلفي + +مدمج جديد باسم غير مستعمَل ⇒ **لا كسر** لبرامج قائمة. لا تغيير في صياغة أو دلالة +قائمة. + +# السلبيات (Drawbacks) + +- **سطح خطر**: أخذ عنوان رمز عشوائيّ بالاسم النصّيّ يتجاوز فحص الأنواع (أخطاء + الاسم تظهر وقت الربط لا الترجمة). لكنّه ليس أخطر من `دالة خارجية("…")` القائمة + التي تثق باسم رمز نصّيّ أيضًا. +- **سلسلة نصّيّة للاسم**: تمرير الاسم كسلسلة (لا كمُعرّف مصرَّح) يفوّت تدقيق + الوجود وقت الترجمة. البديل (تصريح `رمز خارجيّ` مُعرَّف) أنظف نوعيًّا لكنّه + يوسّع الصياغة (انظر البدائل). +- **دلالة مستضافة ملتبسة**: «عنوان رمز» بلا معنًى واضح وقت التفسير ⇒ رفض المفسّر + (تفاوت سلوك مترجم/مفسّر مقصود، لكنّه دَين إدراكيّ). + +# المبرّرات والبدائل (Rationale and alternatives) + +**لماذا مدمج بوسيط سلسلة؟** لأنّه: +1. يحاكي آليّة قائمة ومألوفة (`دالة خارجية("…")` تمرّر اسم رمز نصّيًّا بالضبط). +2. أدنى سطح تغيير: لا صياغة/معجم/AST جديد — فرع مدمج + أوپكود + خفض. +3. يصل إلى **رموز الرابط البحتة** (`_kernel_start`) التي لا مخزن C لها ولا يمكن + تصريحها كمتغيّر ص عاديّ. + +**البدائل المدروسة:** +- **عامل `&اسم`** (على نمط C/Rust): يتطلّب أن يكون الرمز مُعرَّفًا في فضاء أسماء + ص (متغيّرًا)، وهذا يستلزم أوّلًا **تخزينًا ساكنًا مسمّى** (اللبنة 3.14) وتصريح + رموز خارجيّة كبيانات. أوسع نطاقًا؛ يُؤجَّل حتى تملك ص تخزينًا ساكنًا، وقد يُبنى + فوق هذا المقترح لاحقًا. +- **تصريح `رمز خارجيّ("bee_idt") جدول` ثمّ `عنوان_رمز(جدول)`**: أنظف نوعيًّا + (يدقّق المُعرّف)، لكن يضيف شكل تصريح جديدًا. مطروح كتحسين مستقبليّ فوق نفس + الخفض. +- **لا فعل شيء**: تبقى ~25 دالّة C غلافًا للعنونة، ويتعذّر إطفاء مخازن + `.bss`/`.rodata`، فيتوقّف تقدّم «إطفاء C» عند سقفٍ أعلى بكثير من الأرضيّة + الدائمة (~600 سطر). + +**أثر التصفير (ZExt):** اختيار العائد `رقم` (i64) مع توسيع أصفار من 32-بت متعمَّد +لمطابقة عقد الجالبات القائم (`unsigned long long`، تصفير edx)، فلا ينكسر أيّ +مستهلِك حاليّ عند الاستبدال. + +# أعمال سابقة (Prior art) + +- **C**: `&symbol` / اسم المصفوفة يتحلّل لمؤشّر — أخذ العنوان جوهر اللغة. +- **Rust**: `&raw const SYM` / `addr_of!` للرموز الساكنة، و`extern "C" { static S: T; }` + للرموز الخارجيّة ثمّ `&S` ضمن `unsafe`. +- **Go**: `//go:linkname` + `unsafe.Pointer` للوصول لرموز الرابط. +- **Zig**: `@extern(T, .{ .name = "sym" })` — الأقرب دلالةً: يأخذ اسم رمز نصّيًّا + ويعيد مؤشّرًا مكتوبًا. مقترحنا نسخة مبسّطة تعيد عنوانًا خامًا (`رقم`). + +الدرس: كلّها تعامل أخذ عنوان رمز خارجيّ كعمليّة «غير آمنة»/منخفضة المستوى +صريحة. مقترحنا يحصرها في مدمج مسمّى يسهل تدقيقه، متّسقًا مع فلسفة ص «العربيّة +أوّلًا» (اسم عربيّ صريح بدل سيجيل `&`). + +# أسئلة غير محسومة (Unresolved questions) + +- **الاسم**: `عنوان_رمز` (اسم-اسم) مقابل صيغة فعل-أوّلًا (`خذ_عنوان_رمز`) اتّساقًا + مع تسمية مدمجات العتاد فعل-أوّلًا. المدمج مُنتِج قيمة (كـ`حجم`/cast) لا فعل + جانبيّ ⇒ صيغة الاسم مبرَّرة، لكن يُحسم بالنقاش. +- **سلوك المفسّر**: رفض صريح (المقترح) أم دعم رموز مستضافة عبر `dlsym`؟ يُحسم قبل + الدمج. +- **الحصر بالوضع الحرّ**: هل يُقيَّد المدمج بـ`--حرّ` فقط، أم يبقى متاحًا مستضافًا + كأداة FFI؟ (المقترح: متاح في المترجم، مرفوض في المفسّر.) +- **خارج النطاق**: أخذ عنوان **متغيّر/دالّة ص مُعرَّفة محلّيًّا** (يحتاج تخزينًا + ساكنًا مسمّى — اللبنة 3.14)، وتصريح `رمز خارجيّ` المكتوب. + +# إمكانات مستقبلية (Future possibilities) + +- **تخزين ساكن مسمّى (3.14)**: يمكّن `عنوان_رمز` من الإشارة إلى رموز ص مُعرَّفة، + فيُطفَأ ما تبقّى من مخازن `.bss`/`.rodata` في التراب. +- **تصريح `رمز خارجيّ` مكتوب**: يرفع تدقيق النوع والوجود فوق نفس الخفض. +- **`عنوان_دالّة`**: صيغة متخصّصة تعيد مؤشّر دالّة قابلًا للاستدعاء (بدل عنوان + خام)، مفيدة لجداول الأكعاب.