Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 254 additions & 0 deletions text/0000-عنوان-رمز-أخذ-عنوان-رمز-الرابط.md
Original file line number Diff line number Diff line change
@@ -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` في التراب.
- **تصريح `رمز خارجيّ` مكتوب**: يرفع تدقيق النوع والوجود فوق نفس الخفض.
- **`عنوان_دالّة`**: صيغة متخصّصة تعيد مؤشّر دالّة قابلًا للاستدعاء (بدل عنوان
خام)، مفيدة لجداول الأكعاب.
Loading