Константа, появляющаяся inline в .text вместо загрузки из памяти - PullRequest
0 голосов
/ 18 мая 2018

Мы в основном работаем в архитектуре sparc и используем gcc для компиляции нашего кода.

Мы используем некоторые константы в нашем коде, но компилятор вместо выделения памяти для некоторых из этих констант вместо этого оптимизирует и делает его частью кода

, например

cHello : CONSTANT INTEGER_16 := 16#FFFE#;
a = cHello [ cHello is a constant ];

Сборка выглядит следующим образом.

set 16#FFFE#, %g1
sthw %g1, [%g2]

компилятор помещает значение cHello inline в код (.text) вместо загрузки из памяти.

Как сделать так, чтобы компилятор загружался постоянно из памяти, а не помещать их в строку

Редактировать: Язык Ада

Почему меня это волнует:

Вотпроблема в том, что у нас есть встроенная система, где наш код фактически выполняется из ОЗУ, и нам бы хотелось иметь постоянную изменяемость .Мы не хотим делать это .data, так как тогда у нас будет две его копии;при включении они копируются в область Variable RAM.Так что константа лучше в этом случае.Кроме того, ОЗУ, из которого мы выполняем, заблокировано от записи.Таким образом, мы разблокируем его и затем запишем в RAM кода.

Ответы [ 4 ]

0 голосов
/ 19 мая 2018

Я думаю, вы должны определить значение константы в отдельном файле asm или C .Это гарантированный способ остановить компиляцию значения в любом месте, даже с оптимизацией во время компоновки, не используя ничего столь же неэффективного, как volatile.то есть компилятор Ada никогда не видит значение константы вообще в любом исходном файле.

Я не знаю Ada, но эквивалент C будет extern const int my_const;, а затем вотдельный constant.S файл использует .section .rodata / my_const: .long 0x12345.Или используйте пользовательский раздел и скрипт компоновщика, чтобы ваши изменяемые константы были размещены в определенном месте вашего двоичного файла.

Подробнее о взаимодействии Ada с C или asm см. https://gcc.gnu.org/onlinedocs/gcc-7.3.0/gnat_ugn/Interfacing-to-C.html. В ней есть примеры импортаи экспорт определений символов в / из C. (А сопоставление между C и asm очень простое, поэтому вы можете просто сказать Ada, что это C, даже если вы создаете объектные файлы с использованием asm.)

My_Num : Integer;
pragma Import (C, My_Num, "Ada_my_num");

В идеале вы можете объявить My_Num так, чтобы компилятор Ada знал, что это константа. Это объявляет его как обычный глобальный (с внешним определением, использующим имя символа Ada_my_num C).

Это очень похоже на то, что предлагает ответ @ Jose, за исключением использования стандартногоодин asm, чтобы скрыть значение от компилятора.


Вы хотите, чтобы компилятор мог максимально оптимизировать .то есть предположить, что значение этой «переменной» не изменяется в течение времени жизни программы, поэтому она может загрузить ее в регистр при запуске функции и предположить, что вызовы не встроенных функций не могут ее изменить,Или чтобы избежать повторных вычислений с этим ( CSE ), поэтому, если ваш исходный код имеет
a * my_const как до, так и после вызова функции, он может сохранить результат в регистре вместо перезагрузки константыиз памяти и повторного умножения после вызова функции.

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

Но если вы использовали обычную глобальную переменную и присваивали ей значение везде, где может видеть компилятор Ada, тогда вся программаОптимизация времени соединения может распространить это значение в других местах или даже превратить его в другие константы.(например, если вы когда-либо сделаете a += 2 * my_constant, вы могли бы жестко кодировать 2*my_constant где-нибудь в своем выводе asm).

(например, если вы скомпилируете + ссылку с -flto напусть компилятор оптимизирует между модулями компиляции IDK, если GNAT может сделать это так же, как это делает интерфейс C, но, надеюсь, может.)


Почему компилятор делает это: потому что этоконечно, более эффективный!

Загрузка значения из статических данных в памяти обычно требует нескольких инструкций для генерации 32-битного адреса (на ISA с фиксированной шириной инструкции, такой как SPARC);с таким же количеством инструкций вы могли бы создать произвольную 32-битную константу непосредственно в регистре.Инструкции ALU обычно дешевле загрузок и не могут отсутствовать в кэше.

Небольшие константы еще более эффективны и могут использоваться в качестве единственного непосредственного операнда для add, or, andили что-то еще.

Постоянное свертывание и постоянное распространение после встраивания - это основной способ, с помощью которого ассемблерная версия программы может выполнять меньше работы, чем исходная.Например, 5 * my_const может быть сделано во время компиляции, если компилятору известно значение my_const.(Таким образом, он может генерировать , что в регистре напрямую, при необходимости, вместо загрузки my_const и использования shift / add.)

Некоторая универсальная функция может проверять if(x>0), ноКомпилятор может доказать, что это всегда так в одном месте, где встроена функция, если он знает что-то о значениях констант.(Это оптимизация диапазона значений).

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


Пример вывода компилятора .(Я предполагаю, что вы можете написать эквивалентный Ada, который серверная часть SPARC для GNAT / gcc будет оптимизировать аналогично тому, что делает clang / LLVM -target sparc).Суть этого в том, чтобы проиллюстрировать разницу между константой неизвестного значения и известной константой:

( From clang6.0 -O3 -fomit-frame-pointer -target sparc в проводнике компилятора Godbolt )

const int my_const1, my_const2;
static const int my_static = 123;  // a small constant that works as an immediate

int foo(int x) {
    return x + my_static;

}

    retl
    add %o0, 123, %o0         # branch-delay slot


int one_constant(int x) {
    return x + my_const1;
}

    sethi %hi(my_const1), %o1
    ld [%o1+%lo(my_const1)], %o1
    retl
    add %o1, %o0, %o0

Последний, очевидно, менее эффективен.Кажется, что clang не знает, совпадают ли / когда %hi(my_const1) и %hi(my_const2), поэтому он использует другой sethi для каждого статического местоположения.В идеале компилятор мог бы использовать одну и ту же контрольную точку для множественного доступа внутри большой функции, но это не относится к clang / LLVM.У Godbolt нет SPARC GCC, поэтому я не мог попробовать это легко.

0 голосов
/ 18 мая 2018

Синтаксис этого ответа предполагает недавний (GNAT, Ada2012) компилятор, но я не сомневаюсь, что вы могли бы сделать то же самое с прагмами.

Из наблюдения, GNAT сделает константу внепосредственный литерал, если он его видит.

GNAT не позволит вам сделать переменную постоянной и изменчивой.

Единственный способ, которым я нашел, - заставить константу быть извлеченной изхранить вообще значит обмануть компилятор, заставив его импортировать переменную:

with Interfaces;
package Prakhar_Constants is
private
   Chello : constant Interfaces.Integer_16 := 16#7FFE#
   with
     Export,
     External_Name => "constant_chello";
end Prakhar_Constants;

, а затем

with Interfaces;
with Prakhar_Constants;  -- so the binder will know to include it
procedure Prakhar is
   Chello : constant Interfaces.Integer_16
   with
     Import,
     Convention => Ada,
     External_Name => "constant_chello";
   A : Interfaces.Integer_16;
begin
   A := CHello;
end Prakhar;

Я не думаю, что вам нужно беспокоиться о volatile (если высобираемся поменять "постоянную" середину исполнения).

0 голосов
/ 18 мая 2018

Вы можете попробовать aliased constant:

cHello : aliased constant Interfaces.Integer_16 := 16#FFFE#;
0 голосов
/ 18 мая 2018

Работает как положено.Постоянные переменные размещаются в .text (или любом другом разделе const), однако вы хотите использовать RAM как флэш-память, так как RAM является ценным преимуществом.В любом случае, если вы хотите использовать оперативную память для выделения постоянных значений, вы можете создать новый раздел (.myownconst) в оперативной памяти через компоновщик и объявить переменную const как __attribute__((section(".myownconst")))

...