Подтвердить что ты не робот

Понимание кода плиты котла входа/выхода SBCL

ПРЕДПОСЫЛКИ

При использовании 64-битного стального банка 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.

ВОПРОСЫ

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

Ответ 1

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

Форма safepoint определена в compiler/x86-64/macros.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 по умолчанию используют safepoints, но, очевидно, они не используют инструкции x86).

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

Ответ 2

Я ничего не знаю о 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 (clear carry), потому что он возвращает одно значение.

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 (set carry), потому что он возвращает два значения.