Нетрадиционные вызовы со встроенным ASM

Я работаю с проприетарным MCU, в котором есть встроенная библиотека в металле (маска ROM). Я использую компилятор clang, который использует встроенный ASM, подобный GCC. Проблема, с которой я сталкиваюсь, - это вызов библиотеки, поскольку у библиотеки нет согласованного соглашения о вызовах. Хотя я нашел решение, я обнаружил, что в некоторых случаях компилятор выполняет оптимизацию, которая регистрируется clobber непосредственно перед вызовом, я думаю, что что-то не так с тем, как я делаю что-то. Вот код, который я использую:

int EchoByte()
{
    register int asmHex __asm__ ("R1") = Hex;
    asm volatile("//Assert Input to R1 for MASKROM_EchoByte"
            :
            :"r"(asmHex)
            :"%R1");
    ((volatile void (*)(void))(MASKROM_EchoByte))(); //MASKROM_EchoByte is a 16-bit integer with the memory location of the function
}

Теперь возникает очевидная проблема: хотя переменная «asmHex» утверждается в регистре R1, фактический вызов не использует ее, и, следовательно, компилятор «не знает», что R1 зарезервирован во время вызова. Я использовал следующий код, чтобы исключить этот случай:

int EchoByte()
{
    register int asmHex __asm__ ("R1") = Hex;
    asm volatile("//Assert Input to R1 for MASKROM_EchoByte"
            :
            :"r"(asmHex)
            :"%R1");
    ((volatile void (*)(void))(MASKROM_EchoByte))();
    asm volatile("//Assert Input to R1 for MASKROM_EchoByte"
            :
            :"r"(asmHex)
            :"%R1");
}

Мне это кажется действительно некрасивым, и должен быть способ получше. Также меня беспокоит, что компилятор может сделать какую-то ерунду между ними, поскольку сам вызов не указывает на то, что ему нужна переменная asmHex. К сожалению, ((volatile void (*) (int)) (MASKROM_EchoByte)) (asmHex) не работает, поскольку он будет следовать соглашению C, которое помещает аргументы в R2 + (R1 зарезервирован для царапин)

Обратите внимание, что изменение библиотеки Mask ROM, к сожалению, невозможно, и существует слишком много часто используемых подпрограмм, чтобы воссоздать их все на C / C ++.

Ура и спасибо.

РЕДАКТИРОВАТЬ: я должен отметить, что, хотя я мог вызвать функцию в блоке ASM, компилятор имеет оптимизацию для функций без вызова, и, вызывая в сборке, похоже, что вызова нет. Я мог бы пойти по этому пути, если есть способ указать, что встроенный ASM содержит вызов функции, но в противном случае адрес возврата, скорее всего, будет затерт. В любом случае я не смог найти способ сделать это.


person Sam Cristall    schedule 03.01.2013    source источник
comment
Думаю, я бы сложил вызов в блок asm, чтобы убедиться, что все настроено правильно. Я не совсем понимаю, почему это не работает для вас.   -  person JasonD    schedule 04.01.2013
comment
Основная проблема связана с отсутствием в MCU Push / Pop. Функции без вызовов оптимизированы так, чтобы не выполнять никаких манипуляций со стеком, и сворачивание вызова в блок asm не похоже на вызов, а это означает, что в некоторых случаях вызываемый абонент будет стирать адрес возврата. Есть ли способ указать, что блок asm содержит вызов?   -  person Sam Cristall    schedule 04.01.2013
comment
не могли бы вы написать в asssembly полную c-вызываемую функцию, которая вызывала бы встроенную библиотеку?   -  person AShelly    schedule 04.01.2013
comment
@ S.C.M. Можете ли вы добавить регистр обратного адреса в список клоббера?   -  person JasonD    schedule 04.01.2013
comment
Я могу, я надеялся, что мне не пришлось этого делать по другим досадным причинам, но похоже, что это, вероятно, лучший способ, спасибо.   -  person Sam Cristall    schedule 04.01.2013
comment
Если вы объявляете C-вызываемую функцию static __inline__ ... в заголовке, то gcc обычно никогда не создает ее (и всегда вставляет код на сайте вызова).   -  person FrankH.    schedule 04.01.2013
comment
Просто: инструкция asm() (на самом деле ничего, но компилятор просто оставляет это в покое) завершается, и затем вы выполняете свой фанк-вызов. Если вы хотите сделать какое-то странное соглашение о вызовах, вы должны сделать это сами внутри asm()   -  person vonbrand    schedule 23.01.2013


Ответы (1)


В комментариях выше:

Наиболее распространенный ответ заключается в том, что вам следует реализовать в сборке функцию-заглушку (в файле .s), которая просто выполняет за вас дурацкий вызов. В ARM это выглядело бы примерно так:

// void EchoByte(int hex);

_EchoByte:
    push {lr}
    mov  r1, r0       // move our first parameter into r1
    bl   _MASKROM_EchoByte
    pop  pc

Реализуйте одну из этих заглушек для каждой процедуры ROM-маски, и все готово.

Что это такое? У вас есть 500 подпрограмм с масками, и вы не хотите вырезать и вставлять так много кода? Затем добавьте уровень косвенности:

// typedef void MASKROM_Routine(int r1, ...);
// void GeneralPurposeStub(MASKROM_Routine *f, int arg, ...);

_GeneralPurposeStub:
    bx   r0

Вызовите эту заглушку, используя синтаксис GeneralPurposeStub(&MASKROM_EchoByte, hex). Это будет работать для любой точки входа в ПЗУ с маской, которая ожидает параметр в r1. Любые действительно странные точки входа по-прежнему будут нуждаться в собственных кодированных вручную заглушках сборки.

Но если вы действительно, действительно, действительно должны делать это через встроенную сборку в функции C, тогда (как указал @JasonD) все, что вам нужно сделать, это добавить регистр ссылок lr в список clobber.

void EchoByte(int hex)
{
    register int r1 asm("r1") = hex;
    asm volatile(
        "bl  _MASKROM_EchoByte"
        :
        : "r"(r1)
        : "r1", "lr"   // Compare the codegen with and without this "lr"!
    );
}
person Quuxplusone    schedule 19.06.2013