This looks like a bug in GCC, which fails to generate a copy constructor for std::vector<int, std::allocator<int> >
. Note that the error comes from the linker and does not occur during the compilation phase. The copy constructor is used in the copy function that initialises the firstprivate
parameters of the outlined task function. Forcing the compiler to generate it, e.g. changing
std::vector<int> a;
to
std::vector<int> a, b(a);
fixes the problem.
Here is a more elaborate description. GCC transforms the following code
#pragma omp task shared(sum)
{
sum = recursiveSumBody(vec);
}
into something like:
struct omp_data_a data_o;
data_o.vec = vec;
data_o.sum = ∑
GOMP_task(omp_fn_0, &data_o, omp_cpyfn_1, 32, 8, 1, 0, 0, 0);
// --- outlined task body ---
void omp_fn_0(struct omp_data_s & restrict data_i)
{
struct vector & vec = &data_i->vec;
*data_i->sum = recursiveSumBody<int>(vec);
std::vector<int>::~vector(vec);
}
// --- task firstprivate initialisation function ---
void omp_cpyfn_1(struct omp_data_s *data_o, struct omp_data_a *data_i)
{
data_o->sum = data_i->sum;
struct vector &d40788 = data_i->vec;
struct vector *this = &data_o->vec;
std::vector<int>::vector(this, d40788); // <--- invocation of the copy constructor
}
omp_cpyfn_1
gets called by GOMP_task()
in order to initialise the firstprivate arguments. It calls the copy constructor of std::vector<int>
, because (first-)private
treats references to type T as type T itself, but the constructor is not generated, therefore the object code fails to link. This is probably a bug in the gimplifier code as the copy constructor gets created when a non-reference std::vector<T, A>
gets privatised, e.g., with code like this:
...
std::vector<T, A> b;
#pragma omp task shared(sum)
{
sum = recursiveSumBody(b);
}
...
The code compiles with Intel 18.0b. Explicitly specifying vec
as firstprivate
breaks it the same way as with GCC (icpc complains about vec
being of an incomplete type). The following workaround could be used:
template<typename T, typename A>
T recursiveSumBody(std::vector<T, A> &vec) {
T sum = 0;
std::vector<T, A> *ptr = &vec;
#pragma omp task shared(sum)
{
sum = recursiveSumBody(*ptr);
}
return vec[0];
}
In this case ptr
is a pointer. Its firstprivate
version is another pointer that points to the same location, i.e. the vector instance. The semantics differs from the original code as here no private copy of the entire vector gets created, rather the original vector is used.
std::vector
, but I would rather not use pointers directly if possible. – Arbitragelibomp-dev
is the LLVM OpenMP runtime that is unrelated togomp
, which is the OpenMP runtime bundled bygcc
. – Premium