Понимание шаблонного кода сборки входа/выхода SBCL

ЗАДНИЙ ПЛАН

При использовании 64-битного Steel Bank Common Lisp в Windows для тривиальной функции идентификации:

(defun a (x)
   (declare (fixnum x)) 
   (declare (optimize (speed 3) (safety 0))) 
  (the fixnum x))

Я нахожу, что разборка дается как:

* (disassemble 'a)

; disassembly for A
; Size: 13 bytes
; 02D7DFA6:       84042500000F20   TEST AL, [#x200F0000]      ; safepoint
                                                              ; no-arg-parsing entry point
;       AD:       488BE5           MOV RSP, RBP
;       B0:       F8               CLC
;       B1:       5D               POP RBP
;       B2:       C3               RET

Я так понимаю строки:

mov rsp, rbp
pop rbp
ret  

выполнить стандартный возврат из операций функции, но я не понимаю, почему есть строки:

TEST AL, [#x200F0000]  // My understanding is that this sets flags based on bitwise and of AL and contents of memory 0x200F0000

а также

CLC // My understanding is that this clears the carry flag.

ВОПРОСЫ

  1. Почему SBCL генерирует тестовую инструкцию, но никогда не использует флаги?
  2. Почему SBCL очищает флаг переноса перед возвратом из функции?

person Peter de Rivaz    schedule 18.02.2014    source источник
comment
Может соглашение о вызовах? Это с включенными оптимизациями?   -  person Niklas B.    schedule 18.02.2014
comment
Насколько я понимаю, мой спецификатор объявления оптимизации говорит ему компилировать для скорости, а не для безопасности. (Без них код намного длиннее)   -  person Peter de Rivaz    schedule 18.02.2014
comment
Какая версия SBCL? Я не получаю TEST AL ни в SBCL, ни в Allegro Lisp.   -  person asm    schedule 18.02.2014
comment
@AndrewMyers 64-битная версия для Windows   -  person Peter de Rivaz    schedule 18.02.2014
comment
@PeterdeRivaz А, я использую 64-битный Linux, возможно, это важно.   -  person asm    schedule 18.02.2014
comment
@AndrewMyers: Подтверждено. 64-битный Linux, инструкции по тестированию нет. Вероятно, что-то связанное с Windows ABI.   -  person Linuxios    schedule 18.02.2014
comment
По крайней мере, на одной из платформ SBCL использует флаги состояния, чтобы сообщить, является ли это возвратом одного значения или нет.   -  person Philipp Matthias Schäfer    schedule 19.02.2014
comment
Поскольку я могу редактировать комментарии только в течение пяти минут, вот ссылка на соответствующий код (с комментариями): код компилятора   -  person Philipp Matthias Schäfer    schedule 19.02.2014
comment
@PhilippMatthiasSchäfer Большое спасибо, я думаю, что многому научусь, пытаясь понять этот код! Я был приятно удивлен, увидев, что компилятор SBCL Lisp написан на Lisp :)   -  person Peter de Rivaz    schedule 19.02.2014


Ответы (2)


Как намекает дизассемблер, инструкция TEST является точкой безопасности. Он используется для синхронизации потоков для сборщика мусора. Точки безопасности вставляются в местах, где компилятор знает, что поток находится в безопасном состоянии для сборки мусора.

Форма точки сохранения определяется в compiler/x86-64/. макросы.lisp:

#!+sb-safepoint
(defun emit-safepoint ()
  (inst test al-tn (make-ea :byte :disp sb!vm::gc-safepoint-page-addr)))

Вы, конечно, правы насчет того, что результат операции не используется. В этом случае SBCL интересует побочный эффект операции. В частности, если страница, содержащая адрес, оказывается защищенной, инструкция генерирует ошибку страницы. Если страница доступна, инструкция просто тратит очень небольшое количество времени. Я должен отметить, что это, вероятно, намного, намного быстрее, чем простая проверка глобальной переменной.

В Windows функции C map_gc_page и unmap_gc_page находятся в runtime/win32-os. c используются для сопоставления и удаления страницы:

void map_gc_page()
{
    DWORD oldProt;
    AVER(VirtualProtect((void*) GC_SAFEPOINT_PAGE_ADDR, sizeof(lispobj),
                        PAGE_READWRITE, &oldProt));
}

void unmap_gc_page()
{
    DWORD oldProt;
    AVER(VirtualProtect((void*) GC_SAFEPOINT_PAGE_ADDR, sizeof(lispobj),
                        PAGE_NOACCESS, &oldProt));
}

К сожалению, мне не удалось отследить обработчик ошибки страницы, но общая идея, похоже, заключается в том, что когда требуется коллекция, будет вызываться unmap_gc_page. Каждый поток будет продолжать работать до тех пор, пока не достигнет одной из этих точек безопасности, после чего произойдет ошибка страницы. Предположительно, обработчик ошибок страниц затем приостановит этот поток, а затем, когда все потоки будут приостановлены, запустится сборка мусора, а затем снова будет вызван map_gc_page, и потокам будет разрешено возобновить работу.

Файл с титрами посвящен Антону Коваленко за введение этого механизма.

В Linux и Mac OS X по умолчанию используется другой механизм синхронизации, поэтому инструкция не создается в сборках по умолчанию для этих платформ. (Я не уверен, что порты PowerPC используют точки сохранения по умолчанию, но очевидно, что они не используют инструкции x86).

С другой стороны, я понятия не имею об инструкции CLC.

person Samuel Edwin Ward    schedule 13.05.2014
comment
Ух ты! Это увлекательно! С процессорами, работающими не по порядку, интересно, есть ли риск того, что функция может начать выполнять некоторые небезопасные операции, но добавление инструкции CLC заставляет завершить проверку точки безопасности? - person Peter de Rivaz; 14.05.2014
comment
@PeterdeRivaz, инструкция CLC отображается в сборках без точек сохранения, поэтому я подозреваю, что она не связана с точками сохранения. - person Samuel Edwin Ward; 15.05.2014

Я ничего не знаю о TEST AL, [#x200F0000], но я считаю, что CLC предназначен для функций, которые возвращают одно значение. Руководство по внутренним функциям SBCL, "Возвраты неизвестных значений", предлагает функциям устанавливать флаг переноса, если они возвращают несколько значений, или очищать флаг переноса, если они возвращают одно значение.

Я использую SBCL 1.1.14 с OpenBSD и x86-64. Я могу увидеть CLC и SEC, если дизассемблирую функцию, возвращающую одно значение, и функцию, возвращающую несколько значений:

CL-USER> (disassemble (lambda () 100))
; disassembly for (LAMBDA ())
; Size: 16 bytes
; 04B36F64:       BAC8000000       MOV EDX, 200               ; no-arg-parsing entry point
;       69:       488BE5           MOV RSP, RBP
;       6C:       F8               CLC
;       6D:       5D               POP RBP
;       6E:       C3               RET
;       6F:       CC0A             BREAK 10                   ; error trap
;       71:       02               BYTE #X02
;       72:       19               BYTE #X19                  ; INVALID-ARG-COUNT-ERROR
;       73:       9A               BYTE #X9A                  ; RCX
NIL

У этого есть CLC (очистить перенос), потому что он возвращает одно значение.

CL-USER> (disassemble (lambda () (values 100 200)))
; disassembly for (LAMBDA ())
; Size: 35 bytes
; 04B82BD4:       BAC8000000       MOV EDX, 200               ; no-arg-parsing entry point
;       D9:       BF90010000       MOV EDI, 400
;       DE:       488D5D10         LEA RBX, [RBP+16]
;       E2:       B904000000       MOV ECX, 4
;       E7:       BE17001020       MOV ESI, 537919511
;       EC:       F9               STC
;       ED:       488BE5           MOV RSP, RBP
;       F0:       5D               POP RBP
;       F1:       C3               RET
;       F2:       CC0A             BREAK 10                   ; error trap
;       F4:       02               BYTE #X02
;       F5:       19               BYTE #X19                  ; INVALID-ARG-COUNT-ERROR
;       F6:       9A               BYTE #X9A                  ; RCX
NIL

У этого есть STC (установить перенос), потому что он возвращает два значения.

person George Koehler    schedule 11.06.2014