Настроить EMACS
По материалам "Sanely Bootstrappable Common Lisp".
Создаёт кросс-компилятор (xc) - приложение на CL внутри компилятора, а также приложение sb!fasl:genesis. Также порождаются Си файлы для начального образа на целевой системе - src/runtime/genesis.h
Создаёт исполняемый файл src/runtime/sbcl (без образа, только часть на Си и ассемблере). Выполняет grovel, собирающий из Си с помощью экспериментов программу, описывающую конфигурацию target-а (output/stuff-groveled-from-headers.lisp)
sb!xc:compile-file компилирует исходник SBCL снова и создаёт *.lisp-obj - то ли объектные, то ли fasl-файлы.
В том же образе SBCL имитирует загрузку *.lisp-obj и сохранение образа в память.
Запуск образа, составленного из результатов target-1 и genesis-2
В новом образе переименовываем пакеты, убирая восклицательные знаки, загружается PCL. Затем сохраняется output/sbcl.core и на этом процесс завершён.
Порядок выполнения задаётся в make.sh, порядок обработки лисповых файлов при сборке кросс-компилятора и при кросс-компиляции - в build-order.lisp-expr, а определения пакетов для целевого образа - в package-data-list.lisp-expr . В файле порядка сборки есть ключи, говорящие о том, что некоторые файлы на некоторых этапах нужно пропустить, например, :not-host, :not-target - см. .исбцл *expected-stem-flags*
Пакеты sb!xx - для кросс-компилятора, в конечном образе они имеют вид sb-xx.
Пакет sb!xc содержит части common-lisp, к-рые должны для кросс-компилятора отличаться от обычных. Существует только в кросс-компиляторе и не попадате в конечный образ.
#!+
делает условную компиляцию по $2, заданных для цели, а не для 'хоста' кросс-компиляции.
фича :sb-xc-host, :sb-xc в хостовом лиспе говорят о том, какая фаза сборки сейчас идёт (описаны в base-target-features.lisp-expr).
def!type - версия deftype, к-рая выполняется при построении кросс-компилятора и определяет тип в хосте, а также обезпечивает создание такого типа и в целевом лиспе. То же - def!struct и def!type
См. также Теги: SBCL-INFOЕсть политика компиляции check-constant-modification .
Visual C++2013 redistributable
Проанализировать вообще применения %instance-ref. В Make-load-form выглядит уместным, а вот, к примеру, в circle-subst - не очень.
sb-kernel::%instance-cas - это дыра в нашем механизме - нужно, видимо, проверять мутабельность перед использованием.
Что есть fop? В них мы ставим %set-instance-ref
Сделать для раскладки окон 'пересчитать текущую раскладку окон так, чтобы они ложились поверх текущего положения консоли'
backend-type-predicates может быть интересным.
Автоматическую раскладку окон сделать на полный размер экрана, возвращаемый оконным менеджером, тогда не нужно будет создавать ненадёжное тестовое окно.
А также: Изучить недокументированную декларацию '.исбцлт explicit-check'.
А также:
(defoptimizer (array-element-type derive-type) ((array))
кодирует данные типами, и получается (cons integer), к-рый у нас срезается до cons. Поэтому падает тест coerce derive-type. Вероятно, мы сможем это исправить не раньше, чем добавим иммутабельность.
А также: не работает pgup, pgdn на цифровой клавиатуре в линуксе.
Документировать explicit-check, см. .исбцл { explicit-check}
typexpand, typexpand-all - документировать.
Нужно учесть, что (and immutable integer) == integer - возможно, нужно написать соотв. методы. Также это должно поддерживаться в supertype-such-that-...
Принцип лесов: когда правим компилятор, критично, что есть интерпретатор, позволяющий исправить сломанные функции. Иначе каждая ошибка при правке компилятора требовала бы отката и пересборки. Это - некий общий принцип.
Есть несколько функций, к-рые могут вызываться клиентами, и которые сами могут друг друга вызывать. Хотим узнать, как они вызываются клиентами, но не хотим знать, как они друг друга вызывают. Один из вариантов приблизитьва - трассировать все эти функции, но только при условии, что на стеке нет трассируемой функции. Делается так:
(defun trace-depth () (if (boundp 'sb-debug::*traced-entries*)
(length sb-debug::*traced-entries*)
0))
(trace sb-kernel::csubtypep :condition (= (trace-depth) 0))
; и такая же трассировка для sb-kernel::types-equal-or-intersect,
; для sb-kernel::type-intersection, sb-kernel::type-intersection2 и, возможно, др.
sb-c::ir1-phases ==
(trace sb-c::eliminate-dead-code :condition (progn (sb-c::describe-component (sb-debug:arg 0) *standard-output*)))
sb-c::my-ir1-optimize ==
(defun my-ir1-optimize (fn cast) (let ((c cast)) (multiple-value-prog1 (funcall fn cast) (sb-c::describe-object cast *trace-output*))))
(decorate-function:decorate-function 'sb-c::ir1-optimize-cast 'my-ir1-optimize)
SB-C:DEFTRANSFORM ==
(defun my-ir1-transform (fn node transform) ; ir1-transform
(let ((result (funcall fn node transform)))
(print (if (eq result t) "Transform failed" `("Node after transform: " ,node)) *trace-output*)
result))
(decorate-function:def-function-decoration ir1-transform 'my-ir1-transform)
Из старых находок:
Выяснили, что полезно трассировать (trace sb-c::ir1-phases :break t)
, чтобы получить вид flow графа до начала его обработки, и при этом sb-c::describe-component помогает более-менее внятно посмотреть на его содержимое.
Для проверки constraint propagation нужно ещё что-то протрассировать и посмотреть. Возможно, надо править describe, чтобы увидет больше сведений о типах.
В инспекторе происходит ошибка печати, т.к. (я предполагаю) инспектор запускается в отдельном потоке, а нужна переменная SB-C::*COMPILER-IR-OBJ-MAP*
, связанная только в потоке компилятора. Вроде эту ошибку поправил.
Полезны следующие концепции:
(compile-file ... :trace-file t)
См. также Идея для синонимов - печатать имена символов так, чтобы в них был некий запретный character. Тогда при попытке intern синтетического имени возникнет ошибка. Но этот character должен выбрасываться при любой печати, если только не установлена спец. переменная. Ну или вообще чтобы с такими печатанными именами можно было оперировать только в особых условиях, исключающих возможность превращения в символы
Design of CMU CL, 17.1 - VMR Control representation, а также glossary
SB-C::**POLICY-PRIMARY-QUALITIES**
or a declaration, which disallows implicit upcasts like (the integer (the number x))
SB-C::TYPE-NEEDS-CONSERVATION-P - уже похоже на то, что надо, но не используется должным образом.
typep-never-changes-p для типа означает, что если некий объект в данный момент времени является членом этого типа, то он является членом этого типа на протяжении всей своей жизни. Например, таков тип cons. Но типы (cons integer) и (satisfies ufo) не таковы. Причин для нестабильности членства две:
С мутабельностью всё понятно, а с переопределением труднее:
Ф-я sb-c::supertype-such-that-typep-never-changes (смысл следует из названия) для (cons integer) вернёт cons. При передаче информации о типе места в процессе вывода типов, должен быть передан не сам тип, а только возврат этой функции.
ОГО! sb-c::lvar-conservative-type похоже на то, что мы хотим!!!
Теги: CONCEPT TYPE-SYSTEM-EXTENSIONНам нужны дополнительные атрибуты типов для выражения иммутабельности, неизменности типа во времени (и, возможно, иные). Как этого добиться?
(sb-ext:ty (cons integer) :immutable t)
"c:/yar/lp/budden-tools/sbcl--find-definition-sources-by-name--patch.lisp", sb-introspect::sb-introspect--find-definition-sources-by-name-sb--decorated
Теги: SBCL-INFO(sb-kernel::specifier-type 'integer) ; туда
(sb-kernel::type-specifier *) ; и обратно
Проверка типов проверяет на непустоту персечения, т.к. у нас вся библиотека нетипизированная!
(sb-kernel::types-equal-or-intersect (sb-kernel::specifier-type 'integer) (sb-kernel::specifier-type 'string))
;; и есть такая же для values, может пригодиться для сигнатур методов:
(sb-kernel::values-subtypep (sb-kernel::values-specifier-type '(values integer integer &optional)) (sb-kernel::values-specifier-type '(values rational integer &optional)))
;; допустим, мы создали тип, расширяющийся в существующий defstruct str
(deftype fake-type (argument)
(declare (ignore argument))
'str)
;; как узнать, что он есть структура? А вот как:
(sb-kernel::specifier-type '(fake-type 1))
;; ==> #<SB-KERNEL:STRUCTURE-CLASSOID STR>
Наследование структур устроено так:
тег-самого-старшего-предка
тег-более-молодого-предка
...
тег-этого-типа
Хотя возможно, что теги предков содержатся в вынесенном векторе. Похоже, что typep на тип структуры работает так: для каждого типа известна глубина его вложенности. Сначала тег типа сверяется eq с тегом этой стр-ры. Если нет, то ищется в предках (в известном месте вектора предков, т.к. уровень вложенности предка известен).
sb-kernel::layout-of
- Return the layout for an object. This is the basic operation for
finding out the "type" of an object, and is used for generic
function dispatch
sb-kernel::ctype = compiler type - ссылается на type-class.
sb-kernel::type-class - разновидность типа, например, named, values, number, hairy. Экземпляры структуры type-class хранятся в sb-kernel::*type-classes*
. Экземпляр type-class содержит операции над типами такого класса, например, равенство и пересечение типов.
sb-kernel::!define-type-class - определяет класс типов def-type-translator - подобен deftype
sb-kernel::named-type, sb-kernel::member-type - структуры - наследники ctype, ссылаются на экземпляры type-class с именем named, member и т.п.
sb-vm::!define-primitive-object - определяются массивы, в т.ч. с fill-pointer -ом, и некоторые другие объекты.
Теги: SBCL-INFOслужат для упрощения типов. define-type-method: если не определён, то соотв. операция не меняет тип метод для subtype может возврщать два значения, как и сам subtype simple методы служат для случая, когда оба операнда имеют одинаковый type-class complex методы служат для случая, когда один из аргументов (обычно - первый) имеет иной type-class
Похоже, что для возможности горячего переопределения define-type-method нужно две вещи:
Методы :simple-intersection :complex-intersection2 описаны возле
sb-kernel::%type-intersection2
Теги: SBCL-INFO TYPE-INFERENCEЕсть, как минимум,
Забавно то, что они должны работать вместе, но работают по отдельности, см. примечание у SB-C::CONSTRAINT-PROPAGATE-IN-BLOCK, а также FIXME в том же файле. Видимо, на данном этапе мы радуемся тому, что они не взаимодействуют и занимаемся только системой вывода типов.
Проговорим всё от начала до конца.
(defun foo (x y)
(declare (type foo x))
(the (cons integer) x) ; теперь мы 'знаем', что x имеет тип
(bar)
(the baz))
Это без вывода типов означает:
(defun foo (x y)
(the foo x)
(the (and foo (cons integer)) x)
(bar)
(the (and foo baz) x))
А с выводом типов это означает:
(defun foo (x y)
(the (and foo cons) x)
(the (and foo (cons integer)) x)
(bar)
(the (and foo cons) x))
Т.о., очевидно, что у ноды есть два типа - декларированный и выведенный. Декларированный распространяется точно и в обязательном порядке, даже если переменная подвергается присваиванию.
От выведенного распространяется только conservative-type
Теги: SBCL-INFO TYPE-INFERENCEИ отдельный sb-c::mv-combination для mv
sb-c::node-derived-type ; the bottom-up derived type for this node.
В lvar и ctran есть поля "use(s)", что на самом деле означает 'источник' - откуда lvar получает значение и, соотв., откуда передаётся управление.
;; cached type of this lvar's value. If NIL, then this must be
;; recomputed: see sb-c::LVAR-DERIVED-TYPE
sb-c::lver-%derived-type
;; Cached type which is checked by DEST. If NIL, then this must be
;; recomputed: see sb-c::LVAR-EXTERNALLY-CHECKABLE-TYPE
sb-c::%externally-checkable-type
;; the type which values of this leaf must have
sb-c::leaf-type
;; the type which values of this leaf have last been defined to have
;; (but maybe won't have in future, in case of redefinition)
sb-c::leaf-defined-type
derived-type по умолчанию берётся из leaf-type
Теперь нужно для каждого найти все ссылки на тип и (возможно) будет ясно как расщепить.
В 64 разрядах fixnum - это всё, у чего младший бит равен нулю. Поинтер - всё, у чего два младших бита равны единице. Всё остальное - это 10. Остальные два бита поинтера заняты тегом типа поинтера. Конс - это просто два слова, полностью занятые двумя объектами. В него больше ничего не впихнуть.
Указатель на конс - два бита, что это поинтер, два бита, что это конс, остальное - адрес. Как сделать немутабельным? (А) Уменьшить адресуемое пр-во, тогда в указатель (но не в сам конс) можно будет вписать признак мутабельности. (Б) Сделать отдельный внешний массив, в к-ром хранятся биты мутабельности:
502 CL-USER>(defparameter kb 1024)
KB
503 CL-USER>(defparameter mb (* kb kb))
MB
504 CL-USER>(defparameter gb (* mb kb))
GB
505 CL-USER>(/ gb 16) ; столько байт занимает 1 объект
67108864
506 CL-USER>(/ (dynamic-space-size) 16) ; объект занимает 16 байт, значит у нас всего столько объектов
67108864
509 CL-USER>(defparameter *mutability-table* (make-array 67108864 :element-type '(unsigned-byte 2)))
> (room)
16,777,328 bytes for 3 simple-array-unsigned-byte-2 objects.
Т.е. они экономично упакованы. Что тут придётся сделать - так это двигать данные в этом массиве при сборке мусора. Но не будем на это отвлекаться и сделаем пока слабую хеш-таблицу. А в будущем мы могли бы вынести тег типа в этот массив и тем самым получить бинарную совместимость с Си.
Теги: SBCL-INFOЧто нужно учесть: функция freeze на начальном этапе НЕ должна сообщать о том, что аргумент и результат совпадают, поскольку это приведёт к ложному выводу о том, что аргумент был иммутабелен и до момента вызова freeze. Далее неплохо бы сделать так, чтобы инфа о типе распространялась только в будущее, а не в прошлое.
Что возьмём за основу? а) !def-type-translator б) !define-type-class
Уже есть (!define-type-class constant :inherits values). Похоже, это про ситуацию, когда значение параметра известно во время компиляции, см., напр, sb-c::constant-function-call-value . Но можно попробовать взять за основу и менять по аналогии.
Теги: SBCL-INFOЭтим занимается slam.sh, но он не является build-файлом. Перекомпилирует только изменённые файлы, игнорируя зависимости. Также нужна фича after-xc-core
Теги: SBCL-INFOSBCL: a Sanely-Bootstrappable Common Lisp
Контрибы, включая тесты: sh make-target-contrib.sh sb-aclrepl
- один контриб, или без параметров - все контрибы.
Идея состоит в том, чтобы снизить сложность. Но не получается:
(disassemble (compile (defun foo (x) (typecase x ((integer 0 100) (+ 1 x)) (t 538)))))
Эта функция перестаёт оптимизировать +, т.е. видимо, идея отключать constraint-ы - плохая.
Теги: SBCL-INFOНедоделано в степпере (как минимум)
(flet ((f (&key (arg default)) arg))
Не входим в default - это нужно исправлять, видимо, в sb-c::ir1-convert-hairy-args, добавляя default-vals-super-forms . Для глобальных функций нам здесь ещё не хватает возможности вставать за пределы компилируемой формы, т.к. вычисление происходит в вызываемой, а не в вызывающей форме. Хотя... можно ведь сделать это и в вызываемой, а значит - делаем.
FIXME: При вызове неопределённой функции какой-то бред выводится степпером.
FIXME:
(defun f (&key (a 8)) a)
(funcall f)
Если мы не встаём на 8, то похоже, что надо исправлять FIXME STEPPER в sb-c::convert-more-entry
Нас не устраивает, что так мало шагов в пошаговой отладке. Хотим найти, как это делается, и добавить ещё.
sb-c::ir2-convert-block
sb-c::ir2-convert-template
sb-vm::step-instrument-before-vop
SB-VM::EMIT-SINGLE-STEP-TEST
Код такой:
(when (emit-step-p call)
(vop sb!vm::step-instrument-before-vop call block))
sb-c:multiple-call и т.п. - делаются с помощью macrolet define-full-call, к-рого нет в образе
sb-vm::emit-single-step-test
Код такой:
(when step-instrumenting
(emit-single-step-test)
(inst jmp :eq DONE)
(inst break single-step-around-trap))
DONE
Теги: SBCL-INFO sb-c::*source-paths*
- хранит eq отображение форма->путь во время компиляции. Проблема - для формы 42 нет смысла хранить путь, т.к. может быть более одного вхождения 42 в исходном тексте.
Пусть описан у sb-di::form-number-translations, но вообще есть расширенное понятие пути, см. ф-ю sb-c::source-path-tlf-number
и рядом с ней.
sb-c::note-source-path
- запомнить один путь
SB-C::FIND-SOURCE-PATHS
- вызывается читателем для определения номеров подформ. Номера будут записаны в отладочную инфу. С ней синхронизируется sb-di:form-number-translations
, к-рая вызывается отладчиком при поиске места в исходнкие, где мы стоим . Запускать: (let ((sb-c::$2 (make-hash-table))) (sb-c::find-source-paths '(defun f (x) 8) 1) sb-c::$2)
sb-c::get-source-path
- взять из таблицы
sb-c::source-path-original-source
- подтверждает, что второй эл-т надо пропустить.
sb-c::source-path-forms
- говорит о том, что путь содержит не только original-source, но и ещё что-то перед ним.
swank/sbcl::stream-source-position
возвращает смещение в форме по "пути".
Порядок отладки проблем с ненахождением точки:
```
Каков номер формы? вызвать (sb-di::form-number-translations Определение 43 1) он напечатает номера.
Раскомментариваем печать в swank/sbcl::stream-source-position и шагаем к этому месту. Здесь номер формы, взятый из отладочной инфы. Если его нет, то его не записали туда.
sb-di::safe-readtable - ближайшее похожее. Возможный план состоит в том, чтобы наложить ограничения на readtable, чтобы любой нестандартный читатель возвращал осмысленный объект (или даже какой-то syntax object), не имея побочных эффектов, или чтобы он мог запускаться в безопасном режиме без побочных эффектов. Для символов у нас уже есть заглушка
Пример, работающий под Linux и под Windows. Но неплохо бы убедиться, что брекпойнты используются для trace, тогда будет ясно, действительно ли они работоспособны.
>(compile (defun zz (x) (print x)))
>(sb-di::activate-breakpoint (sb-di::make-breakpoint #'(lambda (&rest ignore) (break "Wouch!")) (sb-di::fun-debug-fun #'zz) :kind :fun-end))
>(compile (defun khoj () (zz 3)))
>(khoj)
Теги: SBCL-INFO sb-kernel::step-condition
- base of all step conditions
sb-kernel::step-form-condition
- его (но не только его) сигналит код, скомпилированный с высокой отладкой.
с ним всегда связаны 3 рестарта: step-into, step-next, step-continue
sb-kernel::step-result-condition
- наследует от step-condition
sb-kernel::step-values-condition
- его сигналит код, скомпилированынй с
высокой отладкой, после выполнения формы. Рестартов нет.
sb-kernel::step-finished-condition
- возврат из step
swank/backend:activate-stepping - просто вызывает (sb-impl::enable-stepping)
sb-vm::step-instrument-before-vop - вставляет before-trap
А sb-c:multiple-call-named и аналоги вставляет around-trap - это происходит, если в combination node (вызове функции) есть step-info, а step-info там есть, если sb-c::step-form-p вернул истину.
Как связываются между собой сигнализаторы
(inst break single-step-around-trap)
(inst break single-step-before-trap)
и обработчики? Видимо, через sb-di:handle-single-step-before-trap, sb-di:handle-single-step-around-trap . Они обе вызываются sb-impl::step-form, к-рая сигналит 'step-form-condition. при этом, around-trap ВМЕСТО собственно вызова функции делает так, чтобы вызвался wrapper от функции.
Кривые они в пошаговом режиме тут:
(defun ff (x)
"Outer function"
(let ((y (+ x 1))
(z (- x 1)))
(sb-impl::enable-stepping)
(list
;; здесь y и z видна
(gg y)
;; а здесь - нет.
(+ z y)
)
))
(defun gg (y) y)
Там всё сложно, у нас нет ресурсов разбираться.
Вводим флаг sb-c::$2 , чтобы динамически включать-выключать наш способ инструментирования.
Чтобы переменные показывались, мы выкидываем before-trap, а вместо него делаем вызов ненужной пустой функции sb-c::stupid-step-fn-itself. В ней мы делаем step, и при этом обманываем отладчик, скрывая факт существования на стеке этой вот sb-c::stupid-step-fn-itself.
Далее нам нужно починить step/next/out, к-рый может от этого сломаться.
Теги: SBCL-INFO