Поддержка ассемблерных вставок

Конструкция вида asm() полезна при необходимости реализации низкоуровневых операций и достижения максимальной эффективности кода. Она присутствует практически во всех языках программирования, например:

asm (

"R2 = 0;"

);

или

asm ("R1 = 2; \

R3 = 4;"

);

Компилятор не анализирует код внутри конструкции asm() и передает его напрямую ассемблеру. Единственное, что делает компилятор – выполняет макроподстановки параметров вместо %0,...,%9, если внутри asm()-конструкции используется шаблон.

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

Поскольку компилятор не анализирует код внутри asm-вставки, то вполне возможно "испортить" те регистры, которые задействованы компилятором для других целей, и он предполагает, что работа с ними – его прерогатива. Поэтому cc21k поддерживает использование шаблонов внутри asm-вставок. При этом для программиста отпадает необходимость прямого (явного) использования регистров в ассемблерных инструкциях: ему достаточно указать процессорные регистры какого типа следует использовать и компилятор сам выберет "свободные" регистры и выполнит их подстановку.

Синтаксис шаблона для asm-конструкции выглядит следующим образом:

asm (

template

[: [constraint (output_operand) [,constraint (output_operand)...]]

[: [constraint (input_operand) [,constraint (input_operand)...]]

[: clobber ]]]

);

где

template – строка в кавычках, содержащая ассемблерные инструкции с символами %число вместо наименований регистров в тех позициях, куда компилятор должен сам подставить регистры. Операнды нумеруются в порядке появления слева направо, от 0 до 9;

constraint – строка специального вида (в кавычках), указывающая компилятору на то, процессорные регистры какого типа следует использовать для каждого из входных/выходных операндов;

output_operand – имя переменной программы С/С++, в которую следует записать результат ассемблерной инструкции;

input_operand – имя переменной программы С/С++, из которой берется значение для выполнения ассемблерной инструкции;

clobber – список регистров (каждый в кавычках, маленькими буквами), которые явно использованы (модифицированы) программистом в данной ассемблерной вставке, чтобы компилятор, при необходимости сгенерировал дополнительный код по их сохранению и восстановлению.

Типы регистров:

Constraint Register type Registers
a DAG2 B registers b8 —b15
b Q2 R registers r4 —r7
c Q3 R registers r8 —r11
d All R registers r0 —r15
e DAG2 L registers l8 —l15
F Floating-point registers F0 —F15
f Accumulator register mrf, mrb
h DAG1 B registers b0 —b7
j DAG1 L registers l0 —l7
k Q1 R registers r0 -r3
l Q4 R registers r12 -r15
r All general registers r0 —r15, i0 —i15, l0 —l15, m0 —m15, b0 —b15, ustat1, ustat2
u User registers ustat1, ustat2 (also ustat3, ustat4 on ADSP-2116x DSPs)
w DAG1 I registers I0 —I7
x DAG1 M registers M0 —M7
y DAG2 I registers I8 —I15
z DAG2 M registers M8 —M15
=& constraint Indicates that the constraint is applied to an output operand that may not overlap an input operand
=constraint Indicates that the constraint is applied to an output operand

Использование других букв, как указано в руководстве по компилятору, приводит к непредсказуемому поведению компилятора при выборе регистров

Примеры:

Исходный код на С Сгенерированный код на ассемблере
{ int result, x, y; asm ( "%0 = %1 + %2;" : "=d" (result) : "d" (x), "d" (y) : ); }   r2=dm(_x); r1=dm(_y); r0 = r2 + r1; dm(_result)=r0;
asm ( "r9 = %1; \ r10 = %2; \ %0 = r9+r10;" : "=d" (result) /* %0 */ : "d" (x), "d" (y) /* %1, %2 */ : "r9", "r10" ); modify(i7,-2); dm(-3,i6)=r9; dm(-2,i6)=r10; r2=dm(_x); r1=dm(_y); r9 = r2; r10 = r1; r0 = r9+r10; dm(_result)=r0; r9=dm(-3,i6); r10=dm(-2,i6);
"0" в операндах!!!!!! (Ix)  

Ограничения при использовании ассемблерных вставок:

- нельзя никаким образом передавать управление внутри asm-вставки (изменять ход выполнения программы);

- переменные C/C++ программы доступны только путем указания их в списке операндов/результатов;

- все изменяемые в явном виде регистры должны быть обязательно внесены в список clobber.

Пропроцессор запускается перед компилятором и не просматривает вставки asm(). Поэтому внутри вставок проблематично использовать названия регистров IOP-процессора, определенных в файле def21x60.h как константы адресов памяти.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: