Различия между версиями 5 и 6
Версия 5 от 2019-04-05 16:07:36
Размер: 26852
Редактор: FrBrGeorge
Комментарий:
Версия 6 от 2019-04-06 19:43:30
Размер: 26935
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 322: Строка 322:
 1. <<EJCMC(NoErrors, Без ошибок)>>
Строка 323: Строка 324:
 1. Калькулятор
 1. ???

Исключения и многофайловые проекты

Демонстрация calltrace с помощью macros.inc

Программные исключения

Базовая лекция

Обработка событий в ЭВМ

Что такое «событие»?

  • Происходит в процессе работы (какой-либо программы)
  • Происходит по какой-то причине, явно в программе не заданной

    • дополнительно: программная имитация события

  • Вызывает изменение потока вычислений
    • Вариант реализации: меняется значение счётчика команд
    • Скорее всего, обрабатывается ядром ОС

    Свойства события
  • Асинхронное / активированное синхронно
  • Неожиданное / предполагаемое
  • Требующее возврата к прежнему вычислительному процессу / финальное Терминология плавающая. В архитектуре Intel принято все события называть «прерываниями» В MIPS:
  • прерывания — это события, вызванные внешними причинами (аппаратные)

  • исключения — события, вызванные внутренними причинами (программные)

Тип события

Место в программе известно

Предусмотрено
программой

Источник

Термин

Нештатная ситуация вычислительного процесса (деление на 0, переполнение, защита памяти, нет такой инструкции и т. п.)

да

нет

Внутренний

Исключение (exception)

Имитация нештатной ситуации («система, разбирайся»). В MIPS — исключение TRAP_EXCEPTION

да

да

Внутренний

Ловушка (trap)

Обращение к функции операционной системы

да

да

Внутренний

Системный вызов (system call)

Запрос внешнего устройства

нет

нет

Внешний

Прерывание (interrupt)

Сбой внешнего устройства

иногда

нет

Внешний

Прерывание (interrupt)

Строго говоря, системный вызов — не событие, т. к. всегда явно вызывается программой, но также требует обработки ядром. Общая идея обработки событий.

  • Аппаратура процессора обнаруживает событие и осуществляет передачу управления.
    • на фиксированный адрес (как в MIPS, адрес обработчика 0x80000180)
    • с помощью «вектора прерываний» (он же таблица переходов) — аппаратно поддерживаемая таблица адресов. Предположим, что она находится по адресу 0x80000100

Адрес

Содержимое

Пояснение

0x80000100

0x80000180

адрес обработчика исключения № 0

0x80000104

0x800007ac

адрес обработчика исключения № 1

0x80000108

0x800015b0

адрес обработчика исключения № 2

0x80000120

0x80000e54

адрес обработчика исключения № 8

  • Таблица переходов в MIPS отсутствует:

    • Аппаратная поддержка: двойная косвенность.
    • При получении исключения вычисляется адрес ячейки, из неё берётся адрес обработчика (+операция доступа к памяти).
  • Программная обработка события
    • Аппаратная поддержка: запрет и/или дисциплина обработки повторных событий (событие внутри события); возможно, специальный режим работы процессора

    • Аппаратная поддержка: тип события (выставляется следящей аппаратурой)
  • Возврат к нормальному порядку исполнения инструкций
    • Аппаратная поддержка: адрес возврата запоминается аппаратурой
    • Возможны сложности с конвейером (см. лекцию про конвейер)

Обработка исключний в MIPS

Следит за аппаратурой, устанавливает и хранит дополнительные данные о событиях специальный управляющий сопроцессор MIPS (coprocessor0).

MARS имитирует основные элементы механизма MIPS32 исключений Регистры сопроцессора 0 MARS:

Название

Номер

Назначение

BadVAddr

8

Адрес, при обращении к которому произошло исключение (при записи/чтении по защищённому адресу)

Status

12

Состояние: маска прерываний, биты разрешений, ...

Cause

13

Тип («причина») исключения и биты отложенных прерываний

EPC

14

Адрес инструкции, которая вызвала исключение (или во время выполнения которой произошло прерывание)

Инструкции для работы с регистрами управляющего сопроцессора:

  1. mfc0 Rdest, C0src — загрузить содержимое управляющего регистра C0src в регистр общего назначения Rdest
  2. mtc0 Rsrc, C0dest — загрузить содержимое регистра общего назначения C0src в управляющий регистр Rdest
  3. eret — вернуться из обработчика исключительной ситуации Когда происходит исключение, сопроцессор совершает следующие действия:
  4. Устанавливает бит 1 управляющего регистра $12 (EXception Level, EXL).
  5. Устанавливает биты 2-6 управляющего регистра $13(причина) согласно типу исключения
  6. Устанавливает управляющий регистр $14 (EPC). В регистре сохраняется адрес инструкции, вызвавшей исключение.
  7. Если исключение было вызвано обращением к неправильному адресу памяти, управляющий регистр $8 (BadVaddr) устанавливается на этот неверный адрес.

  8. Поток выполнения переключается с текущей инструкции MIPS на адрес 0x8000180. Этот адрес в сегменте текста ядра (.kext) является стандартным для расположением обработчика исключений MIPS32.

  9. Обработчик исключений может вернуть управление программе, используя команду eret. Это поместит значение из "EPC" (регистр $14) в счетчик команд("PC"). Кроме того, eret очищает бит EXL (см. далее) Замечание: Увеличение регистра $14 на 4 перед возвращением позволит пропустить инструкцию, вызвавшую исключение.

Лень и надувательство (см. спойлер):

Регистр Status:

bits

31-16

15-8

7-5

4

3,2

1

0

target

unused

Int. mask

unused

K/U

unused

Exception level

Int enable

  • Interrupt mask - Mаска прерываний. Соответствующий прерыванию бит равен 1, если данное прерывание разрешено. Если бит равен 0, соответствующее прерывание не обрабатывается (запрещено)
  • K/U - Kernel Mode / User Mode. User mode не эмулируется Mars; всегда 1.
  • Exception level - устанавливается автоматически при исключении; предотвращает повторный вход.
  • Interrupt enable - глобальное разрешение прерываний (0 - отключить). В других версиях MIPS биты 0-5 регистра Status используются по-иному:
  • бит 1 отображает состояние Kernel(=0)/User(=1) mode (то есть работает ли в данный момент процессор в режиме Kernel mode или в режиме User mode)
  • бит 0 отображает резрешение (=1) прерываний
  • когда происходит обработка исключений, биты 3-2 записываются в биты 5-4, биты 1-0 — в биты 3-2, а в биты 1-0 записывается текущее состояние процессора
  • когда происходит выход из обработчика исключений, восстанавливаются предыдущие значения: 3-2 → 1-0, 5-4 → 3-2

Таким образом, в регистре Status формируется трёхуровневый стек для хранения состояний. Если обработка прерывания в прерывании запрещена, достаточно двух уровней, но есть, например, программные ловушки (trap).

Регистр Cause:

31

30-16

15-8

7

6-2

1-0

Br

unused

Pending interrupts

unused

Exception code

unused

  • Br = 1, если исключение произошло на инструкции в слоте задержки перехода (это деталь конвейера, не обращайте внимания пока:)
  • Pending interrupts - ожидающие прерывания. Прерывания асинхронны и вызываются внешними причинами, так что одновременно их вполне может произойти несколько.

  • Exception code - код исключения.

Обработчик исключений

Типы исключений (не обязательно реализованы):

  • ADDRESS_EXCEPTION_LOAD (4)
  • ADDRESS_EXCEPTION_STORE (5)
  • SYSCALL_EXCEPTION (8) (в MARS — исключение внутри системного вызова),

  • BREAKPOINT_EXCEPTION (9) (в MARS — DIVIDE_BY_ZERO_EXCEPTION),

  • RESERVED_INSTRUCTION_EXCEPTION (10),
  • ARITHMETIC_OVERFLOW_EXCEPTION (12),
  • TRAP_EXCEPTION ( 13),
  • DIVIDE_BY_ZERO_EXCEPTION (15),
  • FLOATING_POINT_OVERFLOW (16),
  • FLOATING_POINT_UNDERFLOW (17).

При написании обработчика можно без сохранения использовать только регистры $k0 и $k1, т. к. прерывания могут возникнуть когда угодно, а после возвращения ничего (!) не должно меняться.

⇒ В «больших» системах предусматривается отдельный стек ядра (пользовательский стек может быть испорчен).

Пример тривиального обработчика (пройти под отладчиком Mars):

.text
        nop
        lw    $t0, ($zero)      # Попытка чтения по адресу 0
        li    $v0 10
        syscall

.ktext  0x80000180
        mfc0    $k0 $14         # В регистре EPC — адрес инструкции, где произошло прерывание
        addi    $k0 $k0,4       # Добавим к этому адресу 4
        mtc0    $k0 $14         # Запишем обратно в EPС
        eret                    # Продолжим работу программы

Пример обработчика. Заметим, что коду обработчика разрешается менять только два регистра — $k0 и $k1, все остальные регистры следует сохранять, если они используется в коде.

.text
        lui     $t0 0x7fff
        addi    $t0 $t0 0xffff
        addi    $t0 $t0 0xffff  # Целочисленное переполнение
        sw      $t0 0x400       # Обращение к недоступному адресу памяти
        divu    $t0 $t0 $zero   # Деление на 0
        teq     $zero $zero     # Программная имитация
 (ловушка)
        li      $v0 10
        syscall
.kdata
msg:    .asciiz "Exception "
.ktext  0x80000180
        move    $k0 $v0         # Сохраняем $v0
        move    $k1 $a0         # Сохраняем $a0
        la      $a0 msg         # Выводим сообщение
        li      $v0 4
        syscall
        mfc0    $a0 $13         # Регистр Cause
        srl     $a0 $a0 2       # Выделяем в нём поле «причина»
        andi    $a0 $a0 0x1f
        li      $v0 1           # Выводим как целое
        syscall
        li      $a0 10
        li      $v0 11          # Выводим перевод строки
        syscall

        move    $v0 $k0         # Восстанавливаем $v0
        move    $a0 $k1         # Восстанавливаем $a0

        li      $k0 0
        mtc0    $k0 $13         # Затираем регистр Cause
        mfc0    $k0 $14         # В регистре EPC — адрес инструкции, где произошло прерывание
        addi    $k0 $k0,4       # Добавим к этому адресу 4
        mtc0    $k0 $14         # Запишем обратно в EPС
        eret                    # Продолжим работу программы

Вопрос: какой регистр мы всё-таки испортили в этом обработчике?

Замечание: в Mars исключение №9 — это деление на 0, а точки останова реализованы «аппаратно», т. е. внутри самого MARS

Пример полностью программного обработчика прерываний и исключений в проекте SPIM

Имитация исключений

С одной стороны, на все случаи жизни исключения предусмотреть нельзя.

С другой стороны, это очень эффективный механизм обращения к ОС почти (или совсем) без дополнительных действий, в отличие от syscall. Системный обработчик по адресу 0x80000180 вызывается немедленно одной инструкцией, атомарно с операцией сравнения.

teq $t1,$t2

Trap if equal

Trap if $t1 is equal to $t2

teqi $t1,-100

Trap if equal to immediate

Trap if $t1 is equal to sign-extended 16 bit immediate

tge $t1,$t2

Trap if greater or equal

Trap if $t1 is greater than or equal to $t2

tgei $t1,-100

Trap if greater than or equal to immediate

Trap if $t1 greater than or equal to sign-extended 16 bit immediate

tgeiu $t1,-100

Trap if greater or equal to immediate unsigned

Trap if $t1 greater than or equal to sign-extended 16 bit immediate, unsigned comparison

tgeu $t1,$t2

Trap if greater or equal unsigned

Trap if $t1 is greater than or equal to $t2 using unsigned comparision

tlt $t1,$t2

Trap if less than

Trap if $t1 less than $t2

tlti $t1,-100

Trap if less than immediate

Trap if $t1 less than sign-extended 16-bit immediate

tltiu $t1,-100

Trap if less than immediate unsigned

Trap if $t1 less than sign-extended 16-bit immediate, unsigned comparison

tltu $t1,$t2

Trap if less than unsigned

Trap if $t1 less than $t2, unsigned comparison

tne $t1,$t2

Trap if not equal

Trap if $t1 is not equal to $t2

tnei $t1,-100

Trap if not equal to immediate

Trap if $t1 is not equal to sign-extended 16 bit immediate

Таким образом гарантируется, что за время, прошедшее между сравнением и обращением к системе, никакие данные не изменятся (потому что этого времени нет :) ).

  • Назначение инструкций типа trap:

  • Отладочный останов программы (т. н. assertion). Допустим, в регистре не должно получаться 0, а если ошибочно получится, то лучше пускай система обработает эту ошибку, чем программа будет дальше считать. Например, можно проверить, что изменение счётчика в цикле вообще происходит:
            teqi    $t1 0
            subu    $t0 $t0 $t1
            bgez    $t0 loop
  • Обработка исключительной ситуации, если аппаратного обнаружения её нет. Обычно используется команда R-типа, в которой можно в неиспользуемом аппаратно поле хранить причину исключения. Ср. архитектура RISCore:<<BR>> ||||||||||<style="text-align:center">TEQ - Ловушка по равенству ||

    31

    25

    20

    15

    5

    SPECIAL 000000

    rs

    rt

    code

    TEQ 110100

Содержимое поля code игнорируется системой и может быть использовано для кодированиия информации для системного ПО. Для получения этой информации программа должна загрузить слово команды из памяти (адрес хранится в регистре EPC сопроцессора 0). В ассемблере Mars эта возможность отсутствует, обидно.

Многофайловая сборка

базовая часть лекции

Свойства макроассемблера MARS

Замечания авторов MARS относительно их макроассемблера:

  • Макроопределение должно идти в тексте программы до соответствующей макрокоманды (иначе пришлось бы анализировать текст дважды)

  • Макроопределение локально в пределах одного файла. Это с очевидностью вытекает из самого процесса макроподстановки перед трансляцией. Если нужно, чтобы один и тот же макрос был виден из нескольких файлов, используйте .include

  • Вложенные макросы не поддерживаются, т. е. внутри макроопределения не может встречаться директива .macro

  • Внутри макроопределения, как и в тексте программы, могут встречаться только ранее определённые макрокоманды, искать их определения далее по тексту никто не будет
  • Все метки меняются в процессе макроподстановки, превращаясь в метка_M№

    • (Замечание от меня: нет, на все! если передать метку в качестве параметра, а потом написать что-то вроде %label: , _M№ к ней не припишется. Не знаю, как и зачем это можно использовать…)

  • Несколько макроса с одинаковым именем, но разным количеством параметров, считаются различными, и их можно использовать все
  • Повторное определение макроса с тем же именем и тем же количеством параметров игнорируется, макрокоманда раскрывается в первое определение
  • Параметром макроса (в силу ограниченной реализации) может быть только атомарная лексема языка ассемблера. Например, параметром не может быть "4($t0)", потому что это две лексемы, а не одна

  • Макросредства ассемблера не входят ни в какой стандарт и остаются на усмотрение авторов ассемблера

    В больших многофайловых проектах принято все макросы складывать в отдельный файл и включать их в код программы с помощью директивы .include файл_с_макросами . Подпрограммы при этом складываются в другой файл (возможно. не один), т. н. «библиотеку», и подключаются посредством многофайловой сборки. На предыдущем примере:

    Файл с программой prog.asm:

    .include        "macro.inc"
    .globl  main
    .text
    main:
            input   "First input" $t0
            input   "Second input" $t1
            print   "First result" $t0
            print   "Second result" $t1
            exit

    Файл с подпрограммами lib.asm:

    .globl  _print _input
    
    .text
    _input: # $a0 — message / $v0 — input value
            li      $v0 4
            syscall
            li      $v0 5
            syscall
            jr      $ra
    
    _print: # $a0 — message, $a1 — number
            li      $v0 4
            syscall
            move    $a0 $a1
            li      $v0 1
            syscall
            li      $a0 10
            li      $v0 11
            syscall
            jr      $ra

    На забываем метки всех подпрограмм, которые понадобятся в других файлах, объявлять как .globl

    Файл с макросами macro.inc (имя файла не заканчивается на .asm в знак того, что его не нужно транслировать отдельно):

    .macro  input   %msg %reg
    .data
    msg:    .ascii  %msg
            .asciiz ": "
    .text
            la      $a0 msg
            jal     _input
            move    %reg $v0
    .end_macro
    
    .macro  print   %msg %reg
    .data
    msg:    .ascii  %msg
            .asciiz ": "
    .text
            la      $a0 msg
            move    $a1 %reg
            jal     _print
    .end_macro
    
    .macro  exit
            li      $v0 10
            syscall
    .end_macro

Д/З

TODO

  1. Калькулятор
  2. ???

LecturesCMC/ArchitectureAssembler2019/07_Exceptions (последним исправлял пользователь FrBrGeorge 2019-05-17 15:23:15)