flang компилятор доказывает, что программа не зациклится 0.7.22 GitHub English

Драйвер MSI на flang

docs/examples/driver/msi/msi.flang — контроллер MSI моста PCIe JH7110 (плата VisionFive 2, мост PLDA XpressRICH), переписанный на flang с драйвера из ядра NetBSD. Источник — файлы jh7110_pcie_msi.c (сам контроллер) и jh7110_pcievar.h (типы и адреса регистров) в каталоге sys/arch/riscv/starfive нашей ветки дерева NetBSD; в апстриме этого драйвера нет. Проверялось одно утверждение: перенос драйвера на язык с доказательствами упирается в объём, а не в возможность.

Границы. В ядро это не собирается, на плате не пробовалось, рабочий драйвер не заменяет. Дерево NetBSD при этой работе только читалось.

Приём: драйвер ничего не пишет, он отвечает, что записать

Каждая функция чистая: «состояние контроллера и событие → новое состояние, список записей в регистры и список разрядов, которые надо раздать». Тот же приём, что у драйвера UART docs/examples/driver/uart.flang и у распределителя памяти.

тотальная функция «Занять вектор»
  принимает контроллер: «Состояние MSI», вектор: неотрицательное, безопасен: признак
  возвращает «Отклик MSI»

Хозяина на C в этом каталоге нет. Его работа — цикл: прочитать слово состояния MSI, позвать «Раздать прерывание», для каждого разряда отклика выполнить запись «сброс» и, если поле «звать» истинно, позвать обработчик вектора.

Порядок «сначала сброс бита записью единицы, потом обработчик» — тот, который в исходнике на C держит комментарий, — здесь держится строением данных: сброс есть поле «сброс» объекта «Разряд», а не отдельный шаг, и переставить его после вызова нечем.

Что языку пришлось заменить:

Что лежит в файле

Файл один, 426 строк .

Типы: «Запись в регистр» (адрес, значение), «Разряд» (вектор, сброс, звать), «Вектор MSI» (занят, обработчик, безопасен), «Состояние MSI» (звонок, адрес маски, маска, векторы, включён), «Отклик MSI» (состояние, записи, разряды, отказ).

Функции по группам:

группафункции
константы регистров и их различие«Регистр местной маски», «Регистр местного состояния», «Регистр звонка», «Регистр состояния MSI», «Бит MSI в местной маске», «Число векторов», «Адреса регистров различны»
граница и биты«Вектор в пределах», «Вес вектора», «Бит взведён», «Младший бит взведён», «Маска со взведённым», «Маска со сброшенным», «Предел маски»
годность состояния«Годное состояние»
таблица векторов«Номера векторов», «Свежие векторы», «Шаг правки», «Проход правки», «Правка вектора»
установка и снятие обработчика«Занять вектор», «Освободить вектор»
раздача прерывания«Шаг раздачи», «Начало раздачи», «Раздать прерывание»
запуск«Запустить контроллер»

Взято из драйвера: разбор слова состояния MSI и раздача по векторам (jh7110_pcie_msi_dispatch), установка обработчика с проверкой границы и взведением бита маски (jh7110_pcie_msi_intr_establish), снятие обработчика со сбросом того же бита (ветвь MSI в jh7110_pcie_intr_disestablish), запуск — чтение звонка, перевзвод захвата MSI, обнуление таблицы, снятие маски (jh7110_pcie_msi_init), все адреса регистров и константы.

Оставлено: программирование capability MSI устройства (jh7110_pcie_msi_program — смещения находит pci_get_capability во время работы), распределение выровненных отрезков векторов (jh7110_pcie_msi_find_run, jh7110_pcie_msi_alloc_common), переговоры MSI/MSI-X/INTx (jh7110_pcie_intr_alloc), функции MSI-X (в этом мосте его нет), взаимное исключение, выделение памяти и отладочная печать — это работа хозяина.

Как запустить

bootstrap/flang check docs/examples/driver/msi/msi.flang --proof
bootstrap/flang test  docs/examples/driver/msi/msi.flang

Отчёт --proof печатает вердикт по каждому постусловию и последней строкой — сколько утверждений доказано, сколько на сетке, сколько объявлено без доказательства. Этих чисел на странице нет: они меняются вместе с ядром. Замер на определённую дату, до и после правки свёртки, стоит в разделе о неравенствах на странице какие обещания ядро берёт.

Что доказано и что нет

Все функции тотальны. У двух — «Вес вектора» и «Бит взведён» — завершение доказано постоянным шагом, и для них в напечатанный код ставится проверка убывания: спуск идёт по типу число (разность неотрицательное минус 1 в неотрицательное не втекает), а на IEEE-754 постоянный шаг не всегда меняет величину. У остальных — композицией, без проверок в напечатанном коде.

Утверждений вида «объявлено, не доказано» в отчёте нет.

Доказано обо всех входах:

На сетке (проверено примерами функции, не доказано):

постусловиефункцияпочему
«это тот же нулевой разряд, что читает Бит взведён»«Младший бит взведён»справа рекурсивная функция; ядро сверяет равенство на конечном наборе значений
«ШАГ СОХРАНЯЕТ ГОДНОСТЬ ЦЕЛИКОМ: из годного состояния выходит годное»«Занять вектор»годность содержит неравенство «маска меньше предела», а арифметики неравенств у ядра нет
«ШАГ СОХРАНЯЕТ ГОДНОСТЬ ЦЕЛИКОМ: из годного состояния выходит годное»«Освободить вектор»то же

Что от годности всё-таки доказано у занятия и освобождения: длина таблицы не меняется, бит вектора в маске взводится и сбрасывается, звонок и адрес маски не меняются, на чужом векторе состояние не трогают.

Одно доказанное обещание пусто как запись. «НЕ ТЕРЯЕТ ВЕКТОРОВ (ИНВАРИАНТ ЦИКЛА)» у «Шаг раздачи» — «если до шага разрядов было столько же, сколько взведённых бит, то и после» — выполняется при любом теле, которое прибавляет к счёту и к списку разрядов одно и то же. Содержание несут четыре обещания рядом: они пришпиливают прирост к конкретному числу. Инвариант оставлен ради формулировки, а не ради доказательной силы.

Чего не гарантирует

Рядом