2016-02-05 5 views
2

Я смотрел на compiler output of rmw atomics from gcc и заметил что-то странное - на Aarch64 операции rmw, такие как fetch_add, могут быть частично переупорядочены с расслабленными нагрузками.Частичное переупорядочение C++ 11 atomics на Aarch64

На Aarch64, следующий код может быть сгенерирован для value.fetch_add(1, seq_cst)

.L1: 
    ldaxr x1, [x0] 
    add x1, x1, 1 
    stlxr w2, x1, [x0] 
    cbnz L1 

Однако, это возможно для нагрузок и магазинов, которые происходят до ldaxr быть заказано мимо нагрузки и нагрузки/магазинов, которые происходят после stlxr (см. here). GCC не добавляет заборы, чтобы предотвратить это - Вот небольшой фрагмент кода демонстрирует это:

void partial_reorder(std::atomic<uint64_t> loader, std::atomic<uint64_t> adder) { 
    loader.load(std::memory_order_relaxed); // can be reordered past the ldaxr 
    adder.fetch_add(1, std::memory_order_seq_cst); 
    loader.load(std::memory_order_relaxed); // can be reordered past the stlxr 
} 

генерации

partial_reorder(std::atomic<int>, std::atomic<int>): 
    ldr  w2, [x0] @ reordered down 
.L2: 
    ldaxr w2, [x1] 
    add  w2, w2, 1 
    stlxr w3, w2, [x1] 
    cbnz w3, .L2 
    ldr  w0, [x0] @ reordered up 
    ret 

В действительности нагрузки могут быть частично заказана с операцией RMW - они происходят в середине его.

Итак, что общего? Что я спрашиваю?

  1. Кажется странным, что атомная операция делится как таковая. Я не мог найти ничего в стандарте, предотвращающего это, но я считал, что существует комбинация правил, которые подразумевают, что операции неделимы.

  2. Кажется, что это не относится к приобретению заказа. Если я выполняю нагрузку сразу после этой операции, я могу видеть переупорядочение хранилища или загрузки хранилища между fetch_add и более поздней операцией, что означает, что более поздний доступ к памяти, по меньшей мере, частично переупорядочен за операцией получения. Опять же, я не мог найти ничего в стандартах, явно заявляя, что это запрещено, а приобретать - упорядочение загрузки, но я понимаю, что операция получения применяется ко всей операции, а не только ее части. Аналогичный сценарий может применяться к выпуску, когда что-то переупорядочивается за ldaxr.

  3. Это может быть несколько более растягивающее определения порядка, но кажется недействительным, что две операции до и после операции seq_cst могут быть переупорядочены друг за другом. Это может произойти (?), Если граничные операции переупорядочиваются в середине операции, а затем проходят один за другим.

+0

Возможно, связано: http://bigflake.com/seq_cst.cpp показывает выход компилятора для x64/arm32/aarch64 для базовой атомной загрузки/хранения. 'store (val, std :: memory_order_seq_cst)', кажется, отсутствует барьер. Это с gcc 4.9 на Android. – fadden

ответ

0

Я не вижу здесь ничего плохого. Второй пример почти такой же, как и первая часть с добавлением нагрузки и добавлением нагрузки. Таким образом, это точно соответствует тому, что вы написали в коде C. Я не вижу никакого переупорядочения здесь.

+0

Переупорядочение неявно разрешено инструкциями. – Voo

4

Похоже, вы правы. По крайней мере, очень похожий bug for gcc был принят и исправлен.

Они обеспечивают этот код:

.L2: 
    ldaxr w1, [x0]  ; load-acquire (__sync_fetch_and_add) 
    add w1, w1, 1 
    stlxr w2, w1, [x0] ; store-release (__sync_fetch_and_add) 
    cbnz w2, .L2 

Так предыдущие операции могут быть заказана с ldaxr и futher операции могут быть заказаны с stlxr, который разрушает C++ 11 confirmance. Documentation для барьеров на aarch64 ясно объясняет, что такое переупорядочение возможно.

+0

В то время как я не могу придумать сценарий, когда имеет место переупорядочение первой нагрузки, может потребоваться вторая переупорядоченность, приобретенная в магазине, - например, эпоха или указатель на опасность, как техника, часто требуют переупорядочения, купленного в магазине, между маркировкой потока как активной и загружаемой указатель. В этом случае, если второй груз был указателем, он мог загрузить указатель перед тем, как сделать хранилище «активным». Это действительный, если приобретение и выпуск в acq_rel применяются только к частям операции rmw. Это похоже на неприметную деталь стандарта, поскольку это означает, что атомистика явно неатомная, в некотором смысле. –

+0

На других слабоупорядоченных архитектурах заказ на освобождение освобождается за счет явных заборов (dmb вокруг ldrex и strex для руки), что предотвращает такое внутреннее переупорядочение. Кроме того, я до сих пор не вижу, насколько допустимы операции по переупорядочению вокруг операции seq_cst, которая сильнее, чем acq_rel. –

+0

'он может загрузить указатель перед тем, как совершить хранение в качестве« active ». - Опять же,« commiting »выполняется в вызове' stlxr', который не может быть переупорядочен с более новыми нагрузками. 'cbnz' просто проверяет, что коммитирование было успешным, его поведение не наблюдается другими потоками. Что касается упорядочения 'seq_cst', я считаю, что инструкция' stlxr' также обеспечивает глобальную гарантию видимости для данной архитектуры. Это может быть еще один вопрос для Stack Overflow. – Tsyvarev

Смежные вопросы