std::atomic trivially copyable structs - c++

C++ reference says: http://en.cppreference.com/w/cpp/atomic/atomic
std::atomic may be instantiated with any TriviallyCopyable type T
However following example does not work under g++ 6.2.0
#include <atomic>
#include <functional>
struct Test11 {
int x;
};
struct Test12 {
char x;
};
struct Test13 {
long x;
};
struct Test2 {
char x;
int y;
};
struct Test3 {
int y;
long x;
};
template<typename T, typename... ARGS>
void test(ARGS&& ... args) {
static_assert(std::is_trivially_copyable<T>::value);
std::atomic<T> a;
a.store(T{std::forward<ARGS>(args)...});
}
int main() {
test<Test11>(1);
test<Test12>('\1');
test<Test13>(1L);
test<Test2>('\1',2);
test<Test3>(1,2L);
return 0;
}
Compile: g++-6 -std=c++14 -latomic test.cpp
/tmp/cchademz.o: In function std::atomic<Test3>::store(Test3, std::memory_order):
test.cpp:(.text._ZNSt6atomicI5Test3E5storeES0_St12memory_order[_ZNSt6atomicI5Test3E5storeES0_St12memory_order]+0x3e): undefined reference to __atomic_store_16
collect2: error: ld returned 1 exit status
g++-6 --version
g++ (Ubuntu 6.2.0-7ubuntu11) 6.2.0 20161018
Especially I do not understand why Test2 works but Test3 does not.
Any ideas?
EDIT: added -latomic flag and g++ version

As mentioned by #TartanLlama in its now-deleted answer, you need to link against libatomic:
g++-6 -std=c++14 test.cpp -latomic
You need to add -latomic at the end of the compilation line. Some compilers (linkers) may work correctly if you put -latomic before test.cpp (e.g. g++ on Coliru), but some won't (see Why does the order in which libraries are linked sometimes cause errors in GCC?).
Disclaimer: I am not an expert in linkage, so I cannot provide a detailed explanation as to why it works with -latomic before on some platform and not on other (I am guessing the linkers are different, but... ).
As to why your code compiles if you remove Test3, this is compiler and architecture dependent. If you look at the generated ASM with -O2 and g++6.2 on godbolt:
sub rsp, 24
movabs rax, 8589934593
mov ecx, 5
mov DWORD PTR [rsp], 1
mov rdi, rsp
mov esi, 1
mov edx, 2
mfence
mov BYTE PTR [rsp], 1
mfence
mov QWORD PTR [rsp], 1
mfence
mov QWORD PTR [rsp], rax
mfence
call __atomic_store_16
You see that for structure that takes less than 8 bytes (Test1X, Test2), the compiler can use the mov QWORD instruction (a qword is usually 8 bytes long on nowadays architectures), but it cannot generate a single instruction to handle the cases where the size is strictly greater than 8 (sizeof(Test3) will usually be 16).
Basically, there is probably a specialization of std::atomic<T> (or of some operations of std::atomic<T>) in g++1 when T is "small", and the definition of "small" is probably architecture dependent.
Disclaimer: Again, I am not an expert regarding <atomic> so this is mainly from experiments regarding generated ASM on godbolt and behaviour of g++ and clang on Coliru.
1 clang has a __atomic_store_8 procedure and a __atomic_store procedure, and without -latomic it will not compile for Test2 and Test3. However it manages to compile Test13 even if sizeof(Test13) is 8 so it does not use __atomic_store_8 for some structures. icc has a completely different behavior and does not generate any call (cannot test on Coliru, but you can look it up on godbolt).

Related

Why can't Clang get __m128's data by index in constexpr function

#include <cstddef>
#include <immintrin.h>
constexpr float get_data(__m128 a, std::size_t pos) {
return a[pos];
}
It works on GCC. I wonder is there any workaround to make it possible
Regardless of constexpr, a[pos] is only valid as a GNU C extension, not portable to MSVC. Storing to an array, or C++20 std::bit_cast to a struct might work. bit_cast is constexpr-compatible, unlike other type-punning methods. Although I'd be worried about how efficiently that would compile across compilers for runtime-variable pos
bit_cast does compile ok with clang, and works in a constexpr function. But compiles inefficiently for GCC.
Correction: clang compiles this, but rejects it if called in a context that requires it to be constant-evaluated. note: constexpr bit_cast involving type '__attribute__((__vector_size__(4 * sizeof(float)))) float const' (vector of 4 'float' values) is not yet supported.
Other failed attempts with current clang in a constexpr context:
_mm_store_ps - not supported. Nor is *(__m128*)f = a; because it's a reinterpret_cast.
f[0] = vec[0] etc. initializers: no, even literal constant indexing of a GNU C native vector isn't supported in clang in constexpr.
union type punning: reading an inactive member not allowed in a constexpr context
_mm_cvtss_f32(vec) - non-constexpr function unusable, so no chance of using if constexpr for separate shuffles and returns.
Not-working answer, may work at some point in the future but not with clang trunk pre 15.0
#include <cstddef>
#include <immintrin.h>
#include <bit>
// portable, but inefficient with GCC
constexpr float get_data(__m128 a, std::size_t pos) {
struct foo { float f[4]; } s;
s = std::bit_cast<foo>(a);
return s.f[pos];
}
float test_idx2(__m128 a){
return get_data(a, 2);
}
float test_idxvar(__m128 a, size_t pos){
return get_data(a, pos);
}
These compile to decent asm on Godbolt, the same you'd get from clang with a[pos]. I used -O3 -march=haswell -std=gnu++20
# clang 14 -O3 -march=haswell -std=gnu++20
# get_data has no asm output; constexpr is like inline in that respect
test_idx2(float __vector(4)):
vpermilpd xmm0, xmm0, 1 # xmm0 = xmm0[1,0]
ret
test_idxvar(float __vector(4), unsigned long):
vmovups xmmword ptr [rsp - 16], xmm0
vmovss xmm0, dword ptr [rsp + 4*rdi - 16] # xmm0 = mem[0],zero,zero,zero
ret
Store/reload is a sensible strategy for a runtime-variable index, although vmovd / vpermilps would be an option since AVX introduced a variable-control shuffle that uses dword indices. An out-of-range index is UB so the compiler doesn't have any requirement to return any specific data in that case.
Using vpermilpd for the constant index 2 is a waste of code-size vs. vmovhlps xmm0, xmm0, xmm0 or vunpckhpd. It costs a longer VEX prefix and an immediate, so 2 bytes of machine-code size, but otherwise same performance on most CPUs.
Unfortunately GCC doesn't do such a good job
We get a store/reload even for the fixed index of 2, and even worse, reload by bouncing through a GP-integer register. This is a missed optimization, but IDK how quickly it would get fixed if reported. So if you're going to do this, perhaps #ifdef __clang__ or #ifdef __llvm__ for bit_cast, and #ifdef __GNUC__ for a[pos]. (Clang defines __GNUC__ so check for that after special-casing clang.)
# gcc12 -O3 -march=haswell -std=gnu++20
test_idx2(float __vector(4)):
vmovaps XMMWORD PTR [rsp-24], xmm0
mov rax, QWORD PTR [rsp-16]
vmovd xmm0, eax # slow: should have loaded directly from mem
ret
test_idxvar(float __vector(4), unsigned long):
vmovdqa XMMWORD PTR [rsp-24], xmm0
vmovss xmm0, DWORD PTR [rsp-24+rdi*4] # this is fine, same as clang
ret
Interestingly the runtime-variable version didn't have the same anti-optimization for GCC.

std::mutex::lock() produces weird (and unnecessary) asm code

I was checking generated asm for some of my code and my eye caught some interesting stuff:
#include <mutex>
std::mutex m;
void foo()
{
m.lock();
}
generated asm code (x86-64 gcc 9.2, -std=c++11 -O2):
foo():
mov eax, OFFSET FLAT:_ZL28__gthrw___pthread_key_createPjPFvPvE
test rax, rax
je .L10 // (1) we can simply bypass lock() call?
sub rsp, 8
mov edi, OFFSET FLAT:m
call __gthrw_pthread_mutex_lock(pthread_mutex_t*)
test eax, eax
jne .L14 // (2) waste of space that will never be executed
add rsp, 8
ret
.L10:
ret
.L14:
mov edi, eax
call std::__throw_system_error(int)
m:
.zero 40
Questions:
part (1) -- gcc specific:
what it is doing? (allocating TLS entry?)
how failing that operation allows us to silently bypass lock() call?
part (2) -- looks like each compiler is affected:
std::mutex::lock() can throw according to standard
... but it never does in correct code (as discussed in related SO posts), for all intents and purposes std::mutex::lock() is always noexcept in correct code
is it possible to let compiler know so that it stops emitting unnecessary tests and instruction blocks (like .L14 above)?
Note: I can't see how throwing from std::mutex::lock() is better than simply abort()ing. In both cases your program is screwed (no one expects it to fail), but at least in latter case you end up with considerably smaller asm code ("pay only for something you use", remember?).
It seems that you are misinterpreting the asm output. What you see is not the code of foo but the inlined code of mutex::lock.
From https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/mutex:
void lock() // in class mutex
{
int __e = __gthread_recursive_mutex_lock(&_M_mutex);
// EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
if (__e)
__throw_system_error(__e);
}
From https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.0/gthr-default_8h-source.html:
static inline int __gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *mutex)
{
return __gthread_mutex_lock (mutex);
}
static inline int __gthread_mutex_lock (__gthread_mutex_t *mutex)
{
if (__gthread_active_p ())
return pthread_mutex_lock (mutex);
else
return 0;
}
The names do not exactly match your asm code, so I probably looked at a different libstdc++ source, but to me it looks like the compiler inlined mutex::lock into your function foo and it also inlined the functions that mutex::lock is calling.

Referencing memory operands in .intel_syntax GNU C inline assembly

I'm catching a link error when compiling and linking a source file with inline assembly.
Here are the test files:
via:$ cat test.cxx
extern int libtest();
int main(int argc, char* argv[])
{
return libtest();
}
$ cat lib.cxx
#include <stdint.h>
int libtest()
{
uint32_t rnds_00_15;
__asm__ __volatile__
(
".intel_syntax noprefix ;\n\t"
"mov DWORD PTR [rnds_00_15], 1 ;\n\t"
"cmp DWORD PTR [rnds_00_15], 1 ;\n\t"
"je done ;\n\t"
"done: ;\n\t"
".att_syntax noprefix ;\n\t"
:
: [rnds_00_15] "m" (rnds_00_15)
: "memory", "cc"
);
return 0;
}
Compiling and linking the program results in:
via:$ g++ -fPIC test.cxx lib.cxx -c
via:$ g++ -fPIC lib.o test.o -o test.exe
lib.o: In function `libtest()':
lib.cxx:(.text+0x1d): undefined reference to `rnds_00_15'
lib.cxx:(.text+0x27): undefined reference to `rnds_00_15'
collect2: error: ld returned 1 exit status
The real program is more complex. The routine is out of registers so the flag rnds_00_15 must be a memory operand. Use of rnds_00_15 is local to the asm block. It is declared in the C code to ensure the memory is allocated on the stack and nothing more. We don't read from it or write to it as far as the C code is concerned. We list it as a memory input so GCC knows we use it and wire up the "C variable name" in the extended ASM.
Why am I receiving a link error, and how do I fix it?
Compile with gcc -masm=intel and don't try to switch modes inside the asm template string. AFAIK there's no equivalent before clang14 (Note: MacOS installs clang as gcc / g++ by default.)
Also, of course you need to use valid GNU C inline asm, using operands to tell the compiler which C objects you want to read and write.
Can I use Intel syntax of x86 assembly with GCC? clang14 supports -masm=intel like GCC
How to set gcc to use intel syntax permanently? clang13 and earlier didn't.
I don't believe Intel syntax uses the percent sign. Perhaps I am missing something?
You're getting mixed up between %operand substitutions into the Extended-Asm template (which use a single %), vs. the final asm that the assembler sees.
You need %% to use a literal % in the final asm. You wouldn't use "mov %%eax, 1" in Intel-syntax inline asm, but you do still use "mov %0, 1" or %[named_operand].
See https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html. In Basic asm (no operands), there is no substitution and % isn't special in the template, so you'd write mov $1, %eax in Basic asm vs. mov $1, %%eax in Extended, if for some reason you weren't using an operand like mov $1, %[tmp] or mov $1, %0.
uint32_t rnds_00_15; is a local with automatic storage. Of course it there's no asm symbol with that name.
Use %[rnds_00_15] and compile with -masm=intel (And remove the .att_syntax at the end; that would break the compiler-generate asm that comes after.)
You also need to remove the DWORD PTR, because the operand-expansion already includes that, e.g. DWORD PTR [rsp - 4], and clang errors on DWORD PTR DWORD PTR [rsp - 4]. (GAS accepts it just fine, but the 2nd one takes precendence so it's pointless and potentially misleading.)
And you'll want a "=m" output operand if you want the compiler to reserve you some scratch space on the stack. You must not modify input-only operands, even if it's unused in the C. Maybe the compiler decides it can overlap something else because it's not written and not initialized (i.e. UB). (I'm not sure if your "memory" clobber makes it safe, but there's no reason not to use an early-clobber output operand here.)
And you'll want to avoid label name conflicts by using %= to get a unique number.
Working example (GCC and ICC, but not clang unfortunately), on the Godbolt compiler explorer (which uses -masm=intel depending on options in the dropdown). You can use "binary mode" (the 11010 button) to prove that it actually assembles after compiling to asm without warnings.
int libtest_intel()
{
uint32_t rnds_00_15;
// Intel syntax operand-size can only be overridden with operand modifiers
// because the expansion includes an explicit DWORD PTR
__asm__ __volatile__
( // ".intel_syntax noprefix \n\t"
"mov %[rnds_00_15], 1 \n\t"
"cmp %[rnds_00_15], 1 \n\t"
"je .Ldone%= \n\t"
".Ldone%=: \n\t"
: [rnds_00_15] "=&m" (rnds_00_15)
:
: // no clobbers
);
return 0;
}
Compiles (with gcc -O3 -masm=intel) to this asm. Also works with gcc -m32 -masm=intel of course:
libtest_intel:
mov DWORD PTR [rsp-4], 1
cmp DWORD PTR [rsp-4], 1
je .Ldone8
.Ldone8:
xor eax, eax
ret
I couldn't get this to work with clang: It choked on .intel_syntax noprefix when I left that in explicitly.
Operand-size overrides:
You have to use %b[tmp] to get the compiler to substitute in BYTE PTR [rsp-4] to only access the low byte of a dword input operand. I'd recommend AT&T syntax if you want to do much of this.
Using %[rnds_00_15] results in Error: junk '(%ebp)' after expression.
That's because you switched to Intel syntax without telling the compiler. If you want it to use Intel addressing modes, compile with -masm=intel so the compiler can substitute into the template with the correct syntax.
This is why I avoid that crappy GCC inline assembly at nearly all costs. Man I despise this crappy tool.
You're just using it wrong. It's a bit cumbersome, but makes sense and mostly works well if you understand how it's designed.
Repeat after me: The compiler doesn't parse the asm string at all, except to do text substitutions of %operand. This is why it doesn't notice your .intel_syntax noprefex and keeps substituting AT&T syntax.
It does work better and more easily with AT&T syntax though, e.g. for overriding the operand-size of a memory operand, or adding an offset. (e.g. 4 + %[mem] works in AT&T syntax).
Dialect alternatives:
If you want to write inline asm that doesn't depend on -masm=intel or not, use Dialect alternatives (which makes your code super-ugly; not recommended for anything other than wrapping one or two instructions):
Also demonstrates operand-size overrides
#include <stdint.h>
int libtest_override_operand_size()
{
uint32_t rnds_00_15;
// Intel syntax operand-size can only be overriden with operand modifiers
// because the expansion includes an explicit DWORD PTR
__asm__ __volatile__
(
"{movl $1, %[rnds_00_15] | mov %[rnds_00_15], 1} \n\t"
"{cmpl $1, %[rnds_00_15] | cmp %k[rnds_00_15], 1} \n\t"
"{cmpw $1, %[rnds_00_15] | cmp %w[rnds_00_15], 1} \n\t"
"{cmpb $1, %[rnds_00_15] | cmp %b[rnds_00_15], 1} \n\t"
"je .Ldone%= \n\t"
".Ldone%=: \n\t"
: [rnds_00_15] "=&m" (rnds_00_15)
);
return 0;
}
With Intel syntax, gcc compiles it to:
mov DWORD PTR [rsp-4], 1
cmp DWORD PTR [rsp-4], 1
cmp WORD PTR [rsp-4], 1
cmp BYTE PTR [rsp-4], 1
je .Ldone38
.Ldone38:
xor eax, eax
ret
With AT&T syntax, compiles to:
movl $1, -4(%rsp)
cmpl $1, -4(%rsp)
cmpw $1, -4(%rsp)
cmpb $1, -4(%rsp)
je .Ldone38
.Ldone38:
xorl %eax, %eax
ret

GCC inline assembly error: asm-specifier for variable '%al' conflicts with asm clobber list

Sorry for so many questions, but I've encountered yet another cryptic error trying to compile the following inline assembly (with -fasm-blocks) which works in MSVC, but apparently not in GCC and wasn't able to deal with it:
unsigned char testData = 128;
__asm
{
// ...
mov al, testData
mov ah, al // error: asm-specifier for variable '%al' conflicts with asm clobber list
shl eax, 16
// ...
};
What is this clobber list and what is wrong with it?
I also tried to change optimization level, but it had no effect.
This has to be some bug in gcc (maybe __asm blocks have implicit clobbering). Anyway there are many workarounds:
__asm
{
// ...
mov ah, testData
mov al, ah
shl eax, 16
// ...
};
or
__asm
{
// ...
mov al, testData
mov ah, testData
shl eax, 16
// ...
};
or
__asm
{
// ...
movzx eax, testData
imul eax, 0x0101
shl eax, 16
// ...
};
the clobber-list is explained here: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html, but not in the context of your __asm syntax, with which I'm not familiar. Trying to compile your snippet I get
jcomeau#intrepid:/tmp$ make test
cc test.c -o test
test.c:4: error: expected ‘(’ before ‘{’ token

Inline assembly troubles

I tried to compile with GCC inline assembly code which compiled fine with MSVC, but got the following errors for basic operations:
// var is a template variable in a C++ function
__asm__
{
mov edx, var //error: Register name not specified for %edx
push ebx //error: Register name not specified for %ebx
sub esp, 8 //error: Register name not specified for %esp
}
After looking through documentation covering the topic, I found out that I should probably convert (even if I am only interested in x86) Intel style assembly code to AT&T style. However, after trying to use AT&T style I got even more weird errors:
mov var, %edx //error: Expected primary-expression before % token
mov $var, edx //error: label 'LASM$$s' used but not defined
I should also note that I tried to use LLVM-GCC, but it failed miserably with internal errors after encountering inline assembly.
What should I do?
For Apple's gcc you want -fasm-blocks which allows you to omit gcc's quoting requirement for inline asm and also lets you use Intel syntax.
// test_asm.c
int main(void)
{
int var;
__asm__
{
mov edx,var
push ebx
sub esp,8
}
return 0;
}
Compile this with:
$ gcc -Wall -m32 -fasm-blocks test_asm.c -o test_asm
Tested with gcc 4.2.1 on OS X 10.6.
g++ inline assembler is much more flexible than MSVC, and much more complicated. It treats an asm directive as a pseudo-instruction, which has to be described in the language of the code generator. Here is a working sample from my own code (for MinGW, not Mac):
// int BNASM_Add (DWORD* result, DWORD* a, int len)
//
// result += a
int BNASM_Add (DWORD* result, DWORD* a, int len)
{
int carry ;
asm volatile (
".intel_syntax\n"
" clc\n"
" cld\n"
"loop03:\n"
" lodsd\n"
" adc [edx],eax\n"
" lea edx,[edx+4]\n" // add edx,4 without disturbing the carry flag
" loop loop03\n"
" adc ecx,0\n" // Return the carry flag (ecx known to be zero)
".att_syntax\n"
: "=c"(carry) // Output: carry in ecx
: "d"(result), "S"(a), "c"(len) // Input: result in edx, a in esi, len in ecx
) ;
return carry ;
}
You can find documentation at http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Extended-Asm.