Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Добавление i к адресу, на который указывает edx (j ).



Строка 24

Перемещение нового значения i в еах.

Строка 25

Инкрементирование i.

Строка 26

Обратный переход в проверку цикла for.

Строка 27

Выравнивание, как описано в комментарии к строке 14.

Строка 28

Метка.L4.

Строка 29

Установка кода возврата в еах.

Строка 30

Освобождение локальной области в памяти.

Строка 31

Извлечение переменной из стека, извлечение адреса возврата, и обратный переход в вызывающий код.

Пример ассемблера PowerPC

Следующий результирующий РРС-ассемблерный код с С-программы. Если вы знакомы с языком ассемблера (и его акронимами), вам станут понятны большинство функций РРС. Тем не менее существует несколько различных форм базовых инструкций, обсуж­даемых далее.

stwu, RS, D (RA) (Store Word with Update, сохранение слова с обновлением). Эта инструкция берет значение в регистре (GPR) RS и сохраняет его по эффективному адресу, формируемому как RA+D. После этого регистр RA (GPR) обновляется новым эффектив­ным адресом.

li RT, RS, SI (Load Immediate, непосредственная загрузка). Эта расширенная мнемоника для загрузки инструкций с фиксированной точкой. Эквивалентна добавлению


Пример языка ассемблера



RT, RS, S1, где сумма RS (GPR) и S1, являющаяся двоичным 16-битовым дополнением, сохраняется в RT. Если RS - это RO (GPR), значение SI сохраняется в RT. Обратите вни­мание, что используется только 16-битовое значение при том, что opcode, регистры и значения должны кодироваться в 32-битовые инструкции.

lwz RT, D(RA) (Load Word and Zero, загрузка слова и нуля). Эта инструкция формирует эффективный адрес наподобие s twu и загружает слово данных из памяти в RT (GPR); «and Zero» означает, что верхние 32 бита вычисляемого эффективного адреса ус­танавливаются в 0, если 64-битовая реализация запускается в 32-битовом режиме. (Более подробно ее реализация описана в PowerPC Architecture Book L)

blr (Branch to Link Register, переход к регистру ссылки). Эта инструкция безус­ловного перехода по 32-битовому адресу в регистре ссылки. При вызове этой функции вы­зывающий код помещает адрес возврата в регистр ссылки. Подобно х86-инструкции ret, blr является простым методом возврата из функции.

Следующий код сгенерирован в результате ввода в командную строку дсс -S count. с:

countppc. s

1.file " count, с"

2.section H. text"

3.align 2

4.globl main

5.type main, ©function

6 main:

#Создание 32-битовой области в памяти из пространства стека #и инициализация i и j.

7 stwu 1, -32(1) #Сохранение 32 байт stack ptr (rl) в стек

8 stw 31, 28(1) #Сохранение слова г31 в нижнюю часть области памяти

9 mr 31, 1 Перемещение содержимого rl в г31

10 li 0, 0 # Загрузка 0 в г0

11 stw 0, 12(31) # Сохранение слова г0 в эффективный адрес 12(r31), var j

12 li 0, 0 # Загрузка 0 в r0

13 stw 0, 8(31) # Сохранение слова г0 в эффективный адрес 8(r31), var i

14.L2: #Проверка цикла for

15 lwz 0, 8(31) # Загрузка 0 в г0

16 cmpwi 0, 0, 7 #Сравнение слова, следующего за г0 с целым значением 7

17 Ые 0, .L5 #Переход на метку L5, если меньше или равно

18 b.L3 #Безусловный переход на метку.L3

19.L5:

#Тело цикла for

20 lwz 9, 12(31) #3агрузка j в г9

21 lwz 0, 8(31) # Загрузка i в г0

22 add 0, 9, 0 #Добавление г0 к г9 и помещение результата в г0



Глава 2 • Исследовательский инструментарий


23 stw 0, 12(31) #Сохранение гО в j

24 lwz 9, 8(31) # Загрузка i в г9

25 addi 0, 9, 1 # Добавление 1 к г9 и помещение результата в гО

26 stw 0, 8(31) # Сохранение гО в i

27 Ь.L2

28.1, 3:

29 li 0, 0 # Загрузка 0 в гО

30 mr 3, 0 #перемещение гО в гЗ

31 lwz 11, 0(1) # Загрузка rl в rll

32 lwz 31, -4(11) #Восстановление г31

33 mr 1, 11 # Восстановление rl

34 Ыг #Безусловный переход по содержимому регистра ссылки

Строка 7

Сохранение 32 байтов stackptr (rl) в стек.

Строка 8

Сохранение слова г31 в нижнюю часть области памяти.

Строка 9

Перемещение содержимого rl в г31.

Строка 10

Загрузка 0 в гО.

Строка 11

Сохранение слова гО в эффективный адрес 12 (r31), var j.

Строка 12

Загрузка 0 вгО.

Строка 13

Сохранение слова гО в эффективный адрес 8 (гЗ 1), var i.

Строка 14

Метка.L2:.

Строка 15

Загрузка i в гО.

Строка 16

Сравнение слова, следующего за гО с целым значением 7.

Строка 17

Переход на метку.L5, если меньше или равно.

Строка 18

Безусловный переход на метку.L3.


Пример языка ассемблера



Строка 19

Метка.L5:.

Строка 20

Загрузка j вг9.

Строка 21

Загрузка i в гО.

Строка 22

Добавление г0 к г9 и помещение результата в гО.

Строка 23

Сохранение г 0 в j.

Строка 24

Загрузка i вг9.

Строка 25

Добавление 1 к г9 и помещение результата в гО.

Строка 26

Сохранение г 0 в i.

Строка 27

Безусловный переход на метку.L2.

Строка 28

Метка.L3:.

Строка 29

Загрузка 0 в гО.

Строка 30

Перемещение г0 в гЗ.

Строка 31

Загрузка rl в г 11.

Строка 32

Восстановление г31.

Строка 33

Восстановление rl.

Строка 34

Безусловный переход в место, указанное содержимым регистра ссылки.



Глава 2 • Исследовательский инструментарий


Сравнивая эти два ассемблерных файла, можно увидеть, что они состоят практически из одинакового количества строк. При более подробном изучении вы увидите, что PRISC (РРС)-процессоры характеризуются использованием множества инструкций загрузки и сохранения, тогда как COSC (х86) чаще всего используют инструкции mov.

Ассемблерные вставки

Еще одной формой кодирования, разрешенной gcc-компилятором, являются ассемб­лерные вставки. Как следует из их имени, ассемблерные вставки не требуют вызова от­дельно скомпилированных ассемблерных программ. Используя определенные инструк­ции, мы можем указать компилятору, что данный блок кода нужно не компилировать, а ассемблировать. Несмотря на то что в результате получается архитектурно-зависимый файл, читаемость и эффективность С-функции сразу увеличивается. Вот конструкция ассемблерной вставки:

1 asm ассемблерная инструкция(ции)

2: операнды вывода (опционально)

3: операнды ввода (опционально)

4: затираемые регистры (опционально)

5 );

Например, такая простая форма: asm (*mov %eax, %ebx" ); может быть переписана в виде asm (umovl %еах, %ebx" : : : );

Здесь мы «обманываем» компилятор, потому что мы затираем регистр ebx. (Читайте об этом дальше.)

Ассемблерные вставки являются настолько гибкими благодаря способности брать С-выражения, модифицировать их и возвращать их в программу с полной уверенностью, что компилятор их воспримет. Далее мы рассмотрим передачу параметров.

Операнды вывода

В строке 2 за двоеточием операнд вывода перечисляет С-выражения в круглых скобках начиная с условий. Для операндов вывода условия обычно имеют модификатор =, оз­начающий, что они доступны только для чтения. Модификатор & показывает, что это операнд «ранней очистки», это значит, что операнд освобождается до того, как инструк­ция заканчивает его использовать. Каждый операнд отделяется запятой.


Ассемблерные вставки



Операнд ввода

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

Очищаемые регистры (или список очистки)

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

Нумерация параметров

Каждый параметр получает порядковый номер начиная с 0. Например, если есть пара­метр вывода и два входных параметра, %0 указывает на параметр вывода, а%1и%2-на параметры ввода.

Ограничения

Ограничения обозначают то, как могут использоваться операнды. Документация GNU перечисляет полный список простых ограничений и машинных ограничений. В табл. 2.4 перечислены наиболее используемые ограничения для х86.

Таблица 2.4. Простые и машинные ограничения длях8б Ограничение Функция

А Регистр еах

B Регистр ebx

С Регистр есх

D Регистр edx

S Регистр esi

D Регистр edi

I Значение ограничения (0... 31)

Q Динамически выделяемый среди еах, ebx, ecx, edx регистр

г То же, что и q + esi, edi

M Позиция в памяти

а То же, что и а + Ь; еах и ebx выделяются вместе в виде 64-битового регистра



Глава 2 • Исследовательский инструментарий


2.4.6 asm

На практике (особенно в ядре Linux) ключевое слово asm может привести к ошибке при
компиляции из-за наличия других инструкций с тем же именем. Зачастую можно увидеть
выражения наподобие__ asm__, означающие то же самое.

2.4.7 _volatile_

Еще одним часто используемым модификатором является__ volatile _. Этот моди­
фикатор важен для ассемблерного кода. Он указывает компилятору не оптимизировать
содержимое ассемблерной вставки. Зачастую в программах аппаратного уровня компи­
лятор может подумать, что мы пишем слишком много ненужного кода, и попытается оп­
тимизировать наш код, насколько это возможно. При разработке прикладных программ
это полезно, но на аппаратном уровне результат может оказаться полностью противопо­
ложным.

Например, представим, что мы пишем в спроецированный в память регистр, пред­
ставленный переменной reg. Затем мы выполняем действие, требующее опросить reg.
Компилятор видит, что мы выполняем бессмысленное чтение той же самой области памя­
ти, и удаляет бесполезную команду. При использовании_ volatile_____ компилятор бу­
дет знать, что оптимизировать доступ к этой переменной не нужно. Аналогично, когда вы
видите asm volatile (...) в блоке ассемблерного кода, компилятор не будет оптими­
зировать код этого блока.

Теперь, когда мы знаем основы ассемблера и встроенного ассемблера дсс, мы мо­жем обратить свое внимание на сам встраиваемый ассемблерный код. Используя то, что мы только что изучили, мы сначала рассмотрим простой пример, а затем немного более сложный блок кода.

Вот первый пример кода, в котором мы передаем переменную внутрь встроенного участка кода:

6 int foo(void)

7 {

8 int ее = 0x4000, се = 0x8000, reg;

9 __ asm____ volatile_ (" movl %1, %%eax" ;

10 " movl %2, %%ebx";

11 " call setbits-

12 -movl %%eax, %0"

13: " =r" (reg) // reg [параметр %0] - вывод

14: " r" (ce), Hr- (ее) // се [параметр %1], ее [параметр %2] - ввод

15: -%еах-, -%ebx" // %еах и % ebx очищаются

16 )

17 printf (Hreg=%x-, reg);

18 }


Ассемблерные вставки



Строка 6

В этой строке находится обычное С-начало функции.

Строка 8

ее, се и req - локальные переменные, передаваемые в ассемблерную вставку в качестве параметров.

Строка 9

В этой строке находится обычное ассемблерное выражение. Помещение сев еах.

Строка 10

Помещение ее в ebx.

Строка 11

Вызов функции из ассемблера.

Строка 12

Возвращение значения в еах и его копирование в reg.

Строка 13

Эта строка содержит перечень выходных параметров. Параметр reg доступен толь­ко для чтения.

Строка 14

Эта строка содержит перечень входных параметров. Параметры сей ее - перемен­ные регистров.

Строка 15

В этой строке находится перечень затираемых регистров. Далее изменяются реги­стры еах и ebx. Компилятор знает, что после этого выражения использовать дан­ные значения нельзя.

Строка 16

Эта строка обозначает конец ассемблерной вставки.

Второй пример использует функцию switch_to() из include/asm-i386/ system, h. Эта функция - сердце переключения контекстов Linux. В этой главе мы рас­смотрим только механизм ассемблерных вставок. Гл. 9, «Построение ядра Linux», описы­вает использование switch_to ().

include/asm-i386/system.h

012 extern struct task_struct * FASTCALL(_ switch_to(struct

task_struct *prev, struct task_struct *next));

015 #define switch__to (prev, next, last) do {



Глава 2 • Исследовательский инструментарий


16 unsigned long esi, edi;

17 asm volatile (" pushf l\n\t"

18 " pushl %%ebp\n\t"

19 " movl %%esp, %0\n\t" /* сохранение ESP */

20 " movl %5, %%esp\n\t" /* восстановление ESP */

21 " movl $lf, %l\n\t" /* сохранение EIP */

22 " pushl %6\n\t" /* восстановление EIP */

23 " jmp __ switch_to\n"

 

23 " l: \t"

24 " popl %%ebp\n\t"

25 " popfl"

26: " =m" (prev-> thread.esp), " =m" (prev-> thread.eip),

27 " =a" (last), " =S" (esi), n=D" (edi)

28: " m" (next-> thread.esp), " mH (next-> thread.eip),

29 " 2" (prev), " d" (next));

030 } while (0)

Строка 12

FASTCALL указывает компилятору передавать параметры в registers, asmlinkage указывает компилятору передавать параметры в stack.

Строка 15

Метод do {statements...} while (0) позволяет макросу представляться компи­лятору в более похожем на функцию виде. В этом случае он позволяет использовать локальные переменные.

Строка 16

Не смущайтесь: это просто имя локальной переменной.

Строка 17

Это ассемблерная вставка, не требующая оптимизации.

Строка 23

Параметр 1 используется как адрес возврата.

Строки 17-24

\n\t выполняется через интерфейс компилятора/ассемблера. Каждая ассемблерная инструкция записывается на своей строчке.

Строка 26

prev-> thread. esp и prev-> thread. eip - выходные параметры: [%0] = (prev-> thread, esp), только для чтения [%1] = (prev-> thread, eip), только для чтения


Ассемблерные вставки



Строка 27

%2 ] = (las t) - только для чтения в регистр еах:

[ %3 ] = (esi), только для чтения в регистр esi

[%1] = (prev-> thread, eip), только для чтения в регистр edi

Строка 28

Вот параметры ввода:

[%5] = (next-> thread.esp), в памяти

[%б]= (next-> thread.eip), в памяти

Строка 29

[%7] = (prev) - повторное использование параметра «2» (регистр еах) как вход­ного:

[ %8 ] = (next), назначен как ввод для регистра esx. Обратите внимание, что здесь нет списка очистки.

Встроенный ассемблер для PowerPC практически идентичен по конструкции встроенному ассемблеру для х86. Простые ограничения, такие, как «т» и «п>, исполь­зуются вместе с набором машинных ограничений PowerPC. Вот простой пример обмена 32-битового указателя. Обратите внимание, насколько этот встроенный ассемблер похож на ассемблер для х86:

include/asm-ppc/system.h

103 static _ inline_ unsigned long

104 xchg_u32(volatile void *p, unsigned long val)

105 {

106 unsigned long prev;
107

108 ___ asm____ volatile_ (M\n\

109 1: lwarx %0, 0, %2 \n"
110

111 " stwcx. %3/0/%2 \n\

112 bne- lb"

113: " =& r" (prev), " =m" (*(volatile unsigned long *)p)

114: HrH (p), " r" (val), " m" (*(volatile unsigned long *)p)

115: " cc", " memory" ); 116

117 return prev;

118 )

Строка 103

Эта подпрограмма выполняется на месте; она не вызывается.



Глава 2 • Исследовательский инструментарий


Строка 104

Подпрограмма получает параметры р и val.

Строка 106

Локальная переменная prev.

Строка 108

Встроенный ассемблер. Не требует оптимизации.

Строки 109-111

lwarx вместе с stwcx формирует «атомарный обмен»; lwarx загружает слово из памяти и «резервирует» адрес для последующего сохранения из s twcx.

Строка 112

Переход, если не равно, на метку 1 (b = backward - в обратную сторону).

Строка 113

Вот операнды вывода:

[ %0 ] = (prev), только для чтения, ранняя очистка

[%1]= (* (volatile unsigned long *)p), операнд в памяти только для чтения.

Строка 114

Вот операнды ввода:

[ %2 ] = (р), операнд регистра

[ %3 ] = (val), операнд регистра

[%4]= (* (volatile unsigned long *)р), операнд памяти

Строка 115

Вот операнды очистки:

[ %5 ] = изменение кода регистра состояния

[ % б ] = очистка памяти

На этом мы завершаем обсуждение языка ассемблера и его использования в ядре Linux 2.6. Мы увидели, чем архитектуры РРС и х86 различаются и что у них общее. Ас­семблерные технологии программирования используются в зависимости от платформы. Теперь обратим наше внимание на язык программирования С, на котором написана боль­шая часть ядра Linux, и рассмотрим основные проблемы программирования на языке С.


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-03-25; Просмотров: 777; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.101 с.)
Главная | Случайная страница | Обратная связь