Jump to content

    
jcxz

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

Recommended Posts

Дано:

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
8 часов назад, SII сказал:

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

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

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

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

Нет.

Share this post


Link to post
Share on other sites

Попробовал на 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;

Share this post


Link to post
Share on other sites
2 часа назад, AlexRayne сказал:

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

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
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": их сравнение противоречит вашему высказыванию.

Share this post


Link to post
Share on other sites
8 минут назад, jcxz сказал:

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

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

Share this post


Link to post
Share on other sites
Только что, VladislavS сказал:

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

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

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

Share this post


Link to post
Share on other sites
Цитата

 

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% за такую интерпретацию не ручаюсь. мало ли что там у ИАРа в кишках скрыто.

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

Share this post


Link to post
Share on other sites

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

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]

 

Share this post


Link to post
Share on other sites
21 минуту назад, Kabdim сказал:

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

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

Share this post


Link to post
Share on other sites
1 час назад, Kabdim сказал:

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

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

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

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

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

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

 

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

 

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.