Перейти к содержанию
    

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

Дано:

typedef __packed u64 u64p32;
struct FocQ {         
  union {             
    struct {          
      u32 amplitude;  
      u32 angle;      
    };                
    u64p32 ampl_angle;
  };                  
  s32 reduI;          
} focQ;

Присваивание (вариант 1):

u32 j, j1;
...
*(u64 *)&focQ.ampl_angle = j1 | (u64)j << 32;

Результат компиляции (обрезал всё кроме самой записи):

...
0x65A0             STR      R0,[R4, #+88]
0x65E1             STR      R1,[R4, #+92]

Хммм.... Почему не STRD?

 

Присваивание (вариант 2):

u32 j, j1;
...
*(u64 *)&focQ.amplitude = j1 | (u64)j << 32;

Результат компиляции (обрезал всё кроме самой записи):

0xE9C4 0x0116      STRD     R0,R1,[R4, #+88]

Теперь как и ожидалось - STRD.

 

Но почему эти 2 вроде идентичных выражения приводят к разному результату?? Почему в 1-м случае не STRD, а 2 STR?   :umnik2:

PS: Если ampl_angle объявить как u64 и присваивать просто (focQ.ampl_angle = j1 | (u64)j << 32), то получаю STRD естественно. Но тогда у самой focQ появляется выравнивание на 64 бита, что не комильфо.

 

Кстати:

#define __align4 __attribute__ ((aligned (4)))
struct FocQ {
  union {
    struct {
      u32 amplitude;
      u32 angle;
    };
    u64 __align4 ampl_angle;
  };
  s32 reduI;
} focQ;
...
u32 j, j1;
...
focQ.ampl_angle = j1 | (u64)j << 32;

Опять даёт 2 STR вместо STRD. Хотя вроде ничего не мешает применить тут STRD.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Возможно, компилятор (точней, его разработчики) считает, что, поскольку LDRD/STRD работают с двумя словами, то они работают с одним двойным словом -- а значит, нужно выравнивание на границу двойного слова (что не так, им достаточно выравнивания на границу слова); если выравнивания нет, то компилятор использует пары команд LDR и STR.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

потомушта обращение по невыравненому адресу требует специальных команд доступа, а обычные ЛД/СТ вызовут икслючение шины. может поэтому?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 часов назад, SII сказал:

...если выравнивания нет, то компилятор использует пары команд LDR и STR.

После приведения к *(u64 *), вроде как, компилятор теряет информацию о выравнивании и вправе грузить кусок памяти u64 как ему угодно - STR/STRD/STM, что, собственно, мы и видим.
 

54 минуты назад, AlexRayne сказал:

может поэтому?

Нет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Попробовал на Keil-е с ARM Compiler 6.
На -O0 дает пару STR. На > -O0 всегда STRD.

P.S. Keil не дает сделать

typedef __attribute__((packed)) u64 u64p32;

пишет "warning: 'packed' attribute ignored [-Wignored-attributes]".

Если же поместить атрибут перед объявлением самого элемента

struct FocQ {         
  union {             
    struct {          
      u32 amplitude;  
      u32 angle;      
    };                
    __attribute__((packed)) u64 ampl_angle;
  };                  
  s32 reduI;          
} focQ;

то ассемблерный выхлоп не пострадает (по прежнему STRD), но Keil справедливо выдаст предупреждение "warning: taking address of packed member 'ampl_angle' of class or structure 'FocQ::(anonymous)' may result in an unaligned pointer value [-Waddress-of-packed-member]" на строке

*(u64 *)&focQ.ampl_angle = j1 | (u64)j << 32;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, AlexRayne сказал:

потомушта обращение по невыравненому адресу требует специальных команд доступа, а обычные ЛД/СТ вызовут икслючение шины. может поэтому?

Во-первых: "обращения по невыравненому адресу" - там нет, адрес выровнен.

Во-вторых: LDR/STR в Cortex-M как раз вполне нормально работают с невыровненным доступом (с настройками "по умолчанию").

1 час назад, VladislavS сказал:

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

В последнем куске кода (ниже "Кстати") нету __packed. Специально его привёл, чтобы показать что от __packed не зависит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Когда программист начинает мудрить с упаковками, выравниваниями и приведениями типов указателей, то компилятор правильно делает, что перестраховывается. Ну не смог он на 100% гарантировать, что SRD в этом месте будет по слову выровнен. Пусть уж лучше два str будет. У меня был на компиляторе ARM v6 случай, когда я схлопотал LDM с невыровненым адресом из-за приведения типов указателей.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

41 минуту назад, Arlleex сказал:

Попробовал на Keil-е с ARM Compiler 6.
На -O0 дает пару STR. На > -O0 всегда STRD.

Я все примеры компилировал на "medium" оптимизации. Но сейчас проверил на "High, balanced" - результат аналогичный.

10 минут назад, VladislavS сказал:

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

Вообще-то (если Вы не обратили внимания) то я явно приводил тип указателя к u64 * при присваивании.

Заметьте - не __packed u64 *, не u64 __align4 *, а просто u64 *. Что должно говорить компилятору обращаться со значением по этому указателю как с u64.

Таким приведением типа указателя я явно говорю компилятору что u64 - выровнено.

 

И кроме того - сравните внимательнее "вариант 1" и "вариант 2": их сравнение противоречит вашему высказыванию.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 минут назад, jcxz сказал:

Таким приведением типа указателя я явно говорю компилятору что u64 - выровнено.

Что именно в приведении к (u64 *) говорит о том что до приведения там всё было выровнено?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только что, VladislavS сказал:

Что именно в приведении к (u64 *) говорит о том что до приведения там всё было выровнено?

Само приведение.

PS: Ещё раз - Сравните "вариант 1" и "вариант 2".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Цитата

 

Other instructions cannot be pipelined after STR with register offset. STR can only be pipelined when it follows an LDR, but nothing can be pipelined after the store. Even a stalled STRnormally only takes two cycles, because of the write buffer.

LDRD and STRD cannot be pipelined with preceding or following instructions. However, the two words are pipelined together. So, this operation requires three cycles when not stalled

 

из шпаргалки CORTEX-M4 INSTRUCTION TIMING

Возможно STR может параллельно выполнитья с другими командами. Версия с D ставит конвеер колом. => они равноценны для компилятора с точки зрения времени исполнения и размера. Если он видит что возможен приход на конвер инструкции до str и первой str то он использует 32 битную версию, если видит что нет - ставит STRD. На 100% за такую интерпретацию не ручаюсь. мало ли что там у ИАРа в кишках скрыто.

А вообще совет - бросайте считать такты и команды, это уровень компиляторописателей.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

 Вы слишком много хотите от компилятора. Меня, например, больше напрягает, когда он (по моей указке) молча вот так делает

constexpr uint8_t *x = (uint8_t *)1;
volatile uint64_t y = *(uint64_t *)x;
        MOVS     R0,#+1         
        LDRD     R0,R1,[R0, #+0]
        STRD     R0,R1,[SP, #+0]

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

21 минуту назад, Kabdim сказал:

А вообще совет - бросайте считать такты и команды, это уровень компиляторописателей.

Это же повышение своего профессионального уровня. Да и нельзя доверять всё чужим дядькам-комплиляторостроителям. Я, вот, с большим интересом слежу за этой темой! Хотя компиляторы не строю. Кстати, за доку спасибо!!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Kabdim сказал:

Возможно STR может параллельно выполнитья с другими командами. Версия с D ставит конвеер колом. => они равноценны для компилятора с точки зрения времени исполнения и размера. Если он видит что возможен приход на конвер инструкции до str и первой str то он использует 32 битную версию, если видит что нет - ставит STRD. На 100% за такую интерпретацию не ручаюсь. мало ли что там у ИАРа в кишках скрыто.

А вообще совет - бросайте считать такты и команды, это уровень компиляторописателей.

С чего Вы вообще взяли что я "считаю такты"???  :wacko2:

Вообще-то цель моя была: обеспечить атомарную запись в 2 32-битных слова.

И нет - они не равноценны и не могут быть быть равноценными, потому что одна STRD - атомарна, 2 STR - не атомарны.

(т.е. - команда STRD не может быть прервана посередине прерыванием).

 

Вообще совет - бросайте думать за других, у вас это плохо получается.  :unknw:

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...