У вас уже есть ответ на языковую часть юриста.Но я хочу ответить на связанный с этим вопрос о том, как понять, почему это возможно в asm на возможной архитектуре ЦП, которая использует LL / SC для атома RMW .
Это не делаетсмысл в C ++ 11 запретить это переупорядочение: в этом случае потребуется барьер загрузки хранилища, когда некоторые архитектуры ЦП могут его избежать.
Это может быть на самом деле возможно с реальными компиляторами на PowerPC, учитывая путьони отображают C ++ 11-порядки памяти в asm-инструкции.
В PowerPC64 функция с обменом acq_rel и загрузкой захвата (с использованием аргументов указателя вместо статических переменных) компилируется следующим образом с gcc6.3 -O3 -mregnames
.Это из версии C11, потому что я хотел посмотреть на вывод clang для MIPS и SPARC, и установка Clang Годболта работает для C11 <atomic.h>
, но не работает для C ++ 11 <atomic>
при использовании -target sparc64
.
(источник + asm на Godbolt для MIPS32R6, SPARC64, ARM 32 и PowerPC64. )
foo:
lwsync # with seq_cst exchange this is full sync, not just lwsync
# gone if we use exchage with mo_acquire or relaxed
# so this barrier is providing release-store ordering
li %r9,1
.L2:
lwarx %r10,0,%r4 # load-linked from 0(%r4)
stwcx. %r9,0,%r4 # store-conditional 0(%r4)
bne %cr0,.L2 # retry if SC failed
isync # missing if we use exchange(1, mo_release) or relaxed
ld %r3,0(%r3) # 64-bit load double-word of *a
cmpw %cr7,%r3,%r3
bne- %cr7,$+4 # skip over the isync if something about the load? PowerPC is weird
isync # make the *a load a load-acquire
blr
isync
не является барьером загрузки магазина;для этого требуется только выполнение предыдущих инструкций локально (удалить из неработающей части ядра).Он не ожидает очистки буфера хранилища, поэтому другие потоки могут видеть более ранние хранилища.
Таким образом, хранилище SC (stwcx.
), которое является частью обмена, может находиться в буфере хранилища.и становятся глобально видимыми после чистой нагрузки, которая следует за ней. На самом деле, еще один вопрос и ответ уже задавали этот вопрос, и ответ таков: мы думаем, что это изменение порядка возможно. Предотвращает ли `isync` переупорядочение Store-Load на CPU PowerPC?
Если чистая загрузка равна seq_cst
, PowerPC64 gcc ставит sync
перед ld
.Принятие exchange
seq_cst
делает не предотвращением переупорядочения.Помните, что C ++ 11 гарантирует только один общий порядок для операций SC, поэтому для обеспечения C ++ 11 для обмена и загрузки необходимо использовать SC.
Так что в PowerPC есть что-то необычноеотображение из C ++ 11 в asm для атомики.Большинство систем ставят более жесткие барьеры для магазинов, что позволяет последующим нагрузкам быть дешевле или иметь барьер только с одной стороны.Я не уверен, требовалось ли это для крайне слабого упорядочения памяти PowerPC, или был возможен другой выбор.
https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html показывает некоторые возможные реализации на различных архитектурах.В нем упоминается несколько альтернатив для ARM.
На AArch64 мы получаем это для исходной версии thread1 на C ++:
thread1():
adrp x0, .LANCHOR0
mov w1, 1
add x0, x0, :lo12:.LANCHOR0
.L2:
ldaxr w2, [x0] @ load-linked with acquire semantics
stlxr w3, w1, [x0] @ store-conditional with sc-release semantics
cbnz w3, .L2 @ retry until exchange succeeds
add x1, x0, 8 @ the compiler noticed the variables were next to each other
ldar w1, [x1] @ load-acquire
str w1, [x0, 12] @ r1 = load result
ret
Переупорядочение не может произойти, потому что выпуск AArch64-магазины последовательный -релиз, не простой выпуск.Это означает, что они не могут переупорядочить с более поздними загрузками.
Но на гипотетической машине, которая также имеет или вместо этого имеет простую атомизацию LL / SC, легко видеть, что acq_rel не останавливается позжезагружает в разные строки кэша, чтобы он становился глобально видимым после LL, но до SC обмена.
Если exchange
реализовано с одной транзакцией, как в x86, то загрузка иХранилище смежно в глобальном порядке операций с памятью, тогда, конечно, никакие более поздние операции не могут быть переупорядочены с помощью обмена acq_rel
, и это в основном эквивалентно seq_cst
.
Но LL / SC не должен бытьнастоящая атомарная транзакция, которая дает атомарность RMW для этого местоположения .
Фактически, одиночная инструкция asm swap
могла бы иметь ослабленную семантику acq_rel.SPARC64 нуждается в membar
инструкциях вокруг его swap
инструкции, поэтому в отличие от x86 xchg
он не является seq-cst сам по себе.(SPARC имеет действительно хорошую / понятную человеку мнемонику инструкций, особенно по сравнению с PowerPC. Ну, в принципе, все более читабельно, чем PowerPC.)
Таким образом, для C ++ 11 не имеет смысла требовать, чтобы это было:это повредило бы реализацию на процессоре, который иначе не нуждался бы в барьере загрузки магазина.