اشتقاقُ التنزيلِ من التنفيذِ لا من الحدس
١ · السؤالُ الذي لم يكن له جواب
مصدرُ الحقيقةِ يعلنُ ١٠٧ قاعدةَ نحو. والخلفيّةُ الأصليّةُ (بلا LLVM) تخفضُ مجموعةً من أوپكودات SIR. وبينهما فجوةٌ تجعلُ سؤالًا بسيطًا غيرَ قابلٍ للقياس: «أتدعمُ الخلفيّةُ الأصليّةُ قاعدةَ لكل؟»
الجوابُ الشائعُ أن يكتبَ مهندسٌ جدولًا: قاعدةٌ ← أوپكودات. وهذا الجدولُ يبلى في أوّلِ رقعةٍ تُغيِّرُ الخفض، ولا شيءَ يكشفُ بلاه: هو نثرٌ يوافقُ نفسَه دائمًا.
٢ · المنهج: يُترجَمُ، ثمّ يُقرَأُ ما خرج
scripts/codegen/gen_grammar_lowers_to.py يشتقُّ الجدولَ بالتشغيل:
- خطُّ الأساس: يُترجَمُ برنامجٌ أدنى (دالّةٌ فارغة) بـ
--أظهر-sir، فتُجمَعُ أوپكوداتُ السقالةِ التي يبثُّها المولِّدُ لأيِّ دالّةٍ كانت. المُودَعُ اليوم:baseline_opcodesأربعةَ عشرَ أوپكودًا. - لكلِّ قاعدة: تُترجَمُ عيّنةٌ من بذورِها في
rules_matrixبالعَلَمِ نفسِه (samples_per_rule: 3)، وتُجمَعُ أوپكوداتُها. lowers_to= اتّحادُ أوپكوداتِ القاعدةِ كاملًا — سقالةً ومضمونًا — لأنّ الحكمَ على الخلفيّةِ يلزمُه كلُّ ما يحتاجُه البرنامجُ فعلًا. وbeyond_baselineهو ما تُضيفُه القاعدةُ فوقَ السقالة.- ما لم يُترجَمْ لا يُدَّعى: القاعدةُ بلا بذرةٍ ناجحةِ الترجمةِ تُوسَمُ
status: not_derivedصراحةً، ولا تُترَكُ خانتُها فارغةً موحيةً بالنجاح.
python scripts/codegen/gen_grammar_lowers_to.py --compiler build/bin/Release/sad-build.exe --recompute٣ · مثالٌ يُرى بالعين: توقف لا أوپكودَ لها
بذرةُ gr.stmt.break/basic/001_in_while.ص كما هي في الشجرة، ثمّ SIR الذي يخرجُ عنها:
sad-build.exe 001_in_while.ص --أظهر-sir -o out.exedefine void @__sad_main() {
entry:
%%ع = alloc
store 0, %%ع
br label %while_cond_0
while_cond_0:
%%2 = lt %%ع, 10
br.cond %%2, label %while_body_1, label %while_exit_2
while_body_1:
%%4 = eq %%ع, 2
br.cond %%4, label %while_exit_2, label %merge_4
while_exit_2:
ret.void
merge_4:
builtin.print %%ع
...
}لا أوپكودَ اسمُه BREAK. توقف صارت br.cond تقفزُ إلى while_exit_2 مباشرةً. جدولٌ مكتوبٌ باليدِ كان سيخترعُ خانةً باسمِ القاعدة؛ والاشتقاقُ بالتنفيذِ يقولُ الحقيقة: القاعدةُ تُنزَّلُ إلى فروعٍ لا إلى تعليمةٍ خاصّةٍ بها.
والمدخلُ المُودَعُ في language-truth/grammar/lowers_to.yaml:
- id: gr.stmt.break
status: derived
lowers_to: [ADD_I64, ALLOC, ARRAY_GET, ARRAY_LEN, ARRAY_NEW, ARRAY_SET,
BR, BR_COND, BUILTIN_PRINT, EQ, LOAD, LT, MOVE, RET_VOID, STORE]
beyond_baseline: [ADD_I64, ARRAY_SET, BUILTIN_PRINT, EQ, LT, MOVE, RET_VOID]
native_ok: [arm64, x86_64]
native_missing_riscv64: [ADD_I64, ARRAY_GET, ARRAY_LEN, ...]
evidence: [001_in_while.ص, 002_in_for.ص, 003_first_iter.ص]٤ · حقلُ evidence هو الفرق
الصفُّ يحملُ أسماءَ البذورِ التي اشتُقَّ منها. فالدعوى قابلةٌ للتكذيب بخطوةٍ واحدة: افتحِ البذرةَ المسمّاة، ترجِمْها بـ--أظهر-sir، وقابِلْ. جدولٌ بلا شواهدَ لا يمكنُ تكذيبُه لأنّه لا يقولُ من أين جاء.
٥ · الأرقام، وكيف تُعاد
python scripts/codegen/check_lowers_to.py① الكتالوج: 780 أوپكودًا (الطزاجة: محدَّث) · مخفوضٌ أصليًّا: 104
② التغطية: 107/107 قاعدةً لها مدخل · مُشتقّة 105 · غير مقيسة 2
③ المفردات: كلُّ أوپكودٍ مذكورٍ في lowers_to معروفٌ في الكتالوج
④ الانجراف: 2 دَينًا مُعلَنًا (يُطبَع ولا يُفشِل)
⚠️ arm64/isel.yaml يذكر أوپكودًا غير موجودٍ في تعداد المترجم: CONST_I64
⚠️ x86_64/isel.yaml يذكر أوپكودًا غير موجودٍ في تعداد المترجم: CONST_I64
▸ دعم الخلفيّة الأصليّة (بلا LLVM) — من المُشتقّ فقط:
arm64 102/105 قاعدة مُشتقّة (102/107 من الكلّ)
riscv64 26/105 قاعدة مُشتقّة (26/107 من الكلّ)
x86_64 102/105 قاعدة مُشتقّة (102/107 من الكلّ)القاعدتانِ غيرُ المشتقّتَين: gr.adv.property_test وgr.adv.ffi_ctype. 🔑 وهما بعينِهما القاعدتانِ الوحيدتانِ بلا مرساةٍ في الشجرة (§ المطابقة). لا مصادفةَ في ذلك: الاشتقاقُ يقرأُ بذورَ القاعدة، فقاعدةٌ بلا بذرةٍ قاعدةٌ بلا تنزيلٍ مشتقّ. عطبٌ واحدٌ يظهرُ في عدّادَينِ مستقلَّين — وهذه علامةُ أنّ العدّادَينِ يقيسانِ الواقعَ لا بعضَهما.
٦ · حدودٌ مقيسة
samples_per_rule: 3. المدخلُ حدٌّ أدنى لمجموعةِ الأوپكودات لا انغلاقُها: صياغةٌ من القاعدةِ لم تُؤخَذْ في العيّنةِ قد تبثُّ أوپكودًا خارجَ القائمة. فـ«مدعومةٌ أصليًّا» تعني «مدعومةٌ على ما قِيس».- الحكمُ على الخلفيّةِ يشملُ السقالة.
lowers_toلا يطرحُ خطَّ الأساس، لأنّ طرحَه يُخفي أنّ السقالةَ نفسَها قد لا تُخفَض. ولهذاgr.stmt.breakتحملُARRAY_NEWوليست في نصِّ القاعدة: عيّنتُها فيها حلقةُلكلعلى مصفوفة. - دَينُ انجرافٍ مُعلَنٌ لا يُفشِل:
CONST_I64مذكورٌ في جدولَي isel لمعماريّتَين وليس في تعدادِ أوپكوداتِ المترجّم. يُطبَعُ في كلِّ تشغيلة، ولا يُوقِفُ الدمجَ — دَينٌ مُقيَّدٌ لا سكوتٌ عنه. - الاشتقاقُ الكاملُ ليس في البوّابة؛ هو يدويٌّ بـ
--recomputeلأنّه يعيدُ ترجمةَ مئاتِ البذور. المُشغَّلُ فيx.py gen --checkهو الفحصُ الرخيص: كتالوجٌ بائت، قاعدةٌ بلا مدخل، ومعماريّةٌ تدّعيloweredبلا خفض.