- •5. Программная конвейерная обработка и поддержка циклов
- •5.1. Краткий обзор
- •5.2. Терминология циклов и основная поддержка циклов
- •5.3. Оптимизация циклов
- •5.3.1. Развертывание цикла
- •5.3.2. Программная конвейерная обработка
- •5.4. Особенности поддержки циклов в архитектуре IntelItanium
- •5.4.1. Ротация регистров
- •5.4.2. Замечание об инициализации ротации предикатов
- •5.4.3. Переходы циклов с программной конвейерной обработкой
- •5.4.3.1. Переходы счетных циклов
- •5.4.3.2. Пример счетного цикла
- •5.4.3.3. Переходы while-циклов
- •5.4.4. Краткий обзор терминологии
- •5.5. Оптимизация циклов в архитектуре IntelItanium
- •5.5.1. While-циклы
- •5.5.2. Циклы с инструкциями, имеющими предикаты
- •5.5.3. Циклы с множеством выходов
- •5.5.3.1. Преобразование циклов с несколькими выходами в циклы с одним выходом.
- •5.5.3.2. Конвейерная обработка с явными многократными выходами
- •5.5.4. Соображения о программной конвейерной обработке
- •5.5.5. Программная конвейерная обработка и предварительные загрузки
- •5.5.5.1. Ограничения емкости
- •5.5.5.2. Конфликты в alat
- •5.5.6. Предварительное разворачивание циклов для программной конвейерной обработки
- •5.5.7. Осуществление сокращений
- •5.5.8. Явный пролог и эпилог
- •5.5.9. Устранение избыточной загрузки в циклах
- •5.6. Итоги
5.5.2. Циклы с инструкциями, имеющими предикаты
Инструкциям, которые уже имеют предикаты в исходном цикле, не назначаются предикаты стадий. Они продолжают управляться инструкциями сравнения в теле цикла. Например, следующий цикл содержит предикатные инструкции:
L1: ldfs f4 = [r5],4
ldfs f9 = [r8],4;;
fcmp.ge.unc p1,p2 = f4,f9;;
(p1) stfs [r9] = f4, 4
(p2) stfs [r9] = f9, 4
br.cloop L1 ;;
Ниже представлен возможный конвейер, при допущении, что загрузка с плавающей точкой выполняется за 9 тактов:
стадия 1: (p16) ldfs f4 = [r5],4
(p16) ldfs f9 = [r8],4;;
--- // пустой такт
стадии 2-4: --- // пустые стадии
стадия 5: --- // пустой такт
(p20) fcmp.ge.unc p1,p2 = f4,f9;;
стадия 6: --- // пустой такт
(p1) stfs [r9] = f4, 4
(p2) stfs [r9] = f9, 4
Далее приводится код для конвейерной реализации:
mov lc = 199 // LC = счетчик циклов - 1
mov ec = 6 // EC = стадий эпилогов + 1
mov pr.rot=1<<16;; // PR16 = 1, rest = 0
L1:
(p16) ldfs f32 = [r5],4
(p16) ldfs f38 = [r8],4;;
(p32) stfs [r9] = f37, 4
(p20) fcmp.ge.unc p31,p32 = f36,f42
(p33) stfs [r9] = f43, 4
L2: br.ctop.sptk L1;;
5.5.3. Циклы с множеством выходов
Все обсуждаемые до сих пор примеры циклов, имели единственный выход внизу цикла. Цикл ниже, имеет несколько выходов – выход внизу, связанный с переходом, закрывающим цикл и преждевременный выход в середине:
L1: ld4 r4 = [r5],4;;
ld4 r9 = [r4];;
cmp.eq.unc p1,p0 = r9,r7
(p1) br.cond exit // преждевременный выход
add r8 = -1,r8;;
cmp.ge.unc p3,p0 = r8,r0
(p3) br.cond L1;;
Циклы с несколькими выходами требуют специальных мер предосторожности, которые гарантировали бы, что в случае принятия преждевременного выхода, конвейер будет корректно опустошен. Имеется два способа для генерирования конвейерной версии этого цикла: (1) преобразовать его в цикл с одним выходом, либо (2) создать конвейер с несколькими явными выходами.
5.5.3.1. Преобразование циклов с несколькими выходами в циклы с одним выходом.
Сначала преобразуем цикл с несколькими выходами в цикл с одним выходом. В исходном цикле первый переход отгораживает сложение, второе сравнение и второй переход. Цикл может быть преобразован в цикл с одним выходом путем отгораживания выполнения этих инструкций и перемещения из тела цикла, перехода преждевременного выхода, как это показано ниже:
L1: ld4 r4 = [r5],4;;
ld4 r9 = [r4];;
cmp.eq.unc p1,p2 = r9,r7
add r8 = -1,r8;;
(p2) cmp.ge.unc p3,p0 = r8,r0
(p3) br.condL1;;
(p1) br.cond exit // преждевременный выход, если p1=1
Вычисление р3 определяет, был ли принят какой-нибудь выход из исходного цикла. Если р3=0, то выходим из цикла, аp1определяет, какой именно выход из цикла был принят. Сложение выполняется спекулятивно (оно не отгорожено с помощьюp2), для предохранения зависимости междуcmp.eqи сложением от ограничения ИИ. Это предполагает что, либоr8не нужен при преждевременном выходе, либо в область адресата преждевременного выхода будет добавлен компенсирующий код. Ниже показан конвейер для этого цикла с назначениями предикатов стадий, но без других распределений ротации регистров. Сравнению и переходу в конце стадии 4 не назначены предикаты стадии, поскольку они уже имеют квалифицирующие предикаты в исходном цикле:
стадия 1: ld4.s r4 = [r5],4;; // ИИ = 2
--- // пустой такт
стадия 2: --- // пустой такт
ld4.s r9 = [r4];;
стадия 3: --- // пустой такт
стадия4: (p19)add r8 = -1,r8
(p19)cmp.eq.unc p1,p2 = r9,r7;;
(p2) cmp.ge.unc p3,p0 = r8,r0
(p3) br.cond L1;;
Код, реализующий этот конвейер, показан ниже и дополнен инструкцией chk.
mov ec = 3
mov pr.rot = 1 << 16;; // PR16 = 1, остальные = 0
L1: ld4.s r32 = [r5],4 // Такт 0
(p19) chk.s r36, recovery // Такт 0
(p19) add r8 = -1,r8 // Такт 0
(p19) cmp.eq.unc p31,p32 = r36,r7;;// Такт 0
ld4.s r34 = [r33] // Такт 1
(p32) cmp.ge p18,p0 = r8,r0 // Такт 1
L2:
(p18) br.wtop.sptk L1;; // Такт 1
(p32) br.cond exit // преждевременный выход, если p32=1
Примечание. При выходе из цикла происходит одна финальная ротация, значениер31ротируется вр32. Поэтомур32используется как предикат перехода для перехода преждевременного выхода.