flang the compiler proves your program cannot hang 0.7.22 GitHub Русский

Memory allocator

docs/examples/allocator/allocator.flang is a memory allocator written as a pure function. The heap is data: a list of free segments and the total size. A request is a value: «Взять» (take) so many, or «Вернуть» (return) an address and a length. The allocator is the function «Шаг кучи», which returns the new heap, an address and the flag «удалось» (succeeded):

тотальная функция «Шаг кучи»
  принимает куча: «Куча», запрос: «Запрос»
  возвращает «Отклик кучи»

The function does not touch memory — it answers what is now to be considered taken. The approach is the same as in the UART driver docs/examples/driver/uart.flang and in the MSI driver.

The program answers the question "can an allocator be expressed in flang, and what about it is provable". It is not a replacement for malloc in the runtime printed to C: the runtime needs memory before the first «Куча» value exists.

What is in the file

1 file, 416 lines .

Types: «Отрезок» (segment: start, length), «Куча» (heap: free segments, total), «Запрос» (request: the variants «Взять» and «Вернуть»), «Отклик кучи» (heap reply: heap, address, succeeded). The heap size is the constant of the function «Размер кучи», 4096.

What the functions do:

How to run

bootstrap/flang check docs/examples/allocator/allocator.flang --proof
bootstrap/flang test  docs/examples/allocator/allocator.flang

The --proof report prints a verdict for every postcondition and, on its last line, how many assertions are proved, how many are on the grid and how many are declared without a proof. Those numbers are not on this page: they change together with the kernel. A measurement on a given date, before and after the fold rewrite, is in the section on inequalities of which promises the kernel takes.

What is proved and what is not

Every function in the file is total, and each termination is proved by composition. The report has no assertions "declared, not proved".

Proved for all inputs — everything written as an equality to a term of the same branch or as a condition on a flag: honest refusal («ОТКАЗ ЧЕСТЕН: места нет — куча не изменилась», «ОТКАЗ ЧЕСТЕН: нулевой возврат отвергается, куча цела», «ОТКАЗ ЧЕСТЕН: возврат за границу кучи отвергается, куча цела»), issue only from a segment («ВЫДАЁТ ТОЛЬКО ИЗ ОТРЕЗКА: адрес выдачи есть начало отрезка»), the remainder takes the place of the taken segment («НИЧЕГО НЕ ПОТЕРЯНО: вместо взятого отрезка встаёт его остаток»), the returned segment enters the list, the insertion order, the whole closed trace of three steps — including heap validity after every step and "the free space shrank by exactly what was issued".

Proved by induction over the fold: «просят столько же, сколько запросили» and «свободных отрезков остаётся столько же» of «Пройти свободные», «возвращаемый отрезок по дороге не меняется» of «Пройти вставку». For this the fold must run over the list argument itself: «Пройти свободные» takes the list of segments rather than the heap, because with свёртка куча.«свободные» the kernel does not read the induction principle.

On the grid (checked on the function's examples, not proved) remain the postconditions written with an inequality or a subtraction:

postconditionfunction
«конец не левее начала»«Конец отрезка»
«НИЧЕГО НЕ ПОТЕРЯНО: остаток плюс выданное — прежняя длина»«Остаток отрезка»
«ВНУТРИ КУЧИ: адрес не левее начала отрезка»«Выдать из отрезка»
«ВНУТРИ КУЧИ: конец выданного не правее конца отрезка»«Выдать из отрезка»
«НЕ ПЕРЕСЕКАЮТСЯ: конец выданного не правее начала остатка»«Выдать из отрезка»
«НЕ ПЕРЕСЕКАЮТСЯ: выданное и остаток — по проверке»«Выдать из отрезка»
«НИЧЕГО НЕ ПОТЕРЯНО: остаток плюс выданное — прежняя длина отрезка»«Выдать из отрезка»
«ГРАНИЦА НЕ ПЯТИТСЯ: прежняя не правее новой»«Шаг годности»

The reason is one: the kernel has no arithmetic of inequalities — no reflexivity of "not greater", no monotonicity of addition, no law "(a minus b) plus b equals a". So "the issued segment lies inside the heap" and "nothing is lost", written as inequalities, stay on the grid, while the same thought written as an equality ("the remainder starts exactly where the issued segment ends") is proved. The rule for the writer: write segment bounds as equalities.

Separately, about types: the fields «начало» and «длина» have the type число rather than неотрицательное, because the sum of two неотрицательное in the language has the type число. The promise «конец не левее начала» is therefore not merely unproved — with a negative length it is false, and the kernel's refusal here is on the merits.

What is not here

Nearby