Каков правильный способ создания постоянного пула для встроенной сборки?

Проблема в том, что внутри функции C у меня встроенная assembly. Что-то вроде

ldr r7, =0xdeadbeef svc 0 

Если литеральный пул не был создан явно (это так), ассемблер создает его в конце единицы перевода. Обычно это нормально, но если блок перевода оказывается действительно огромным, это не работает, потому что литеральный пул слишком далек от инструкции ldr.

Итак, я задаюсь вопросом, как лучше всего справиться с этой проблемой. Наиболее очевидным способом является создание литерального пула вручную внутри встроенной сборки:

  ldr r7, =0xdeadbeef svc 0 b 1f .ltorg 1: 

Или же

  ldr r7, 1f svc 0 b 2f 1: .word 0xdeadbeef 2: 

К сожалению, это приводит к субоптимальному коду из-за избыточной инструкции ветвления. Я не ожидаю, что ассемблер будет достаточно умным, чтобы найти подходящее место для постоянного пула внутри функции. То, что я хотел бы сделать, – создать постоянный пул в конце функции. Есть ли способ сообщить компилятору (gcc) создать литеральный пул в конце функции?

PS Я закончил использование movw/movt вместо постоянных пулов. Хотя, во-первых, решение movw / movt немного менее переносимо, чем литеральные пулы, и, во-вторых, мне просто интересно, можно ли использовать постоянные пулы в встроенной сборке как надежно, так и эффективно.


Обновление: Итак, каков наилучший способ решения проблемы?

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

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

 register int var asm("r7") = 0xdeadbeef; asm volatile("svc 0\n" :: "r" (var)); 

Вы можете использовать -ffunction-sections и в соответствии с запросом в -ffunction-section , используйте ld --gc-sections для удаления неиспользуемого кода.

Существует очевидное разделение файла.

Решение, которое должно работать, – использовать naked функцию с unused аннотацией, поскольку она никогда не вызывается. Поместите здесь один .ltorg а также установите обе функции в специальном разделе; .text.ltorg_kludge например. Сценарий компоновщика должен использовать .text* а функции в одинаковых подразделах помещаются вместе. В некотором роде это похоже на разделение файла, поскольку компилятор попытается встроить static функции.

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


Кроме того: movw / movt более эффективен из-за эффектов кеша. Он также работает с ARMv6 и над Thumb2 кодом. Я не вижу переносимости в качестве большой сделки (поскольку встроенный ассемблер не переносится, и вы, вероятно, предпочитаете производительность по сравнению с переносимостью ), но вопрос имеет отношение к пользователям ARMv4 / 5.


Я исследовал использование ограничения R из ограничений машины gcc ,

р
Элемент в постоянном пуле

Тем не менее, образец с gcc-4.8 дает ошибку невозможное ограничение . Использование альтернативных букв типа C также дает то же сообщение об ошибке. Проверка источника contraints.md, по- видимому, указывает на то, что ограничение R является только функцией документа. К несчастью, как кажется, цель создана для решения этой проблемы.

Возможно, что компилятор загружает значение, но это может быть неоптимальным в зависимости от inline ассемблера. Например,

  asm(" add %0, %0, %1\n" : "+r" (0xdeadbeef) : "r" (0xbaddeed0));