Đây là lỗi trong cách GCC của LLVM được hỗ trợ (llvm-gcc
) chuyển đổi vùng OpenMP và xử lý cuộc gọi đến bên trong. Vấn đề có thể được chẩn đoán bằng cách kiểm tra các bãi cây trung gian (có thể đạt được bằng cách chuyển đối số -fdump-tree-all
đến gcc
). Nếu không có OpenMP kích hoạt các mã đại diện chính thức sau đây được tạo (từ test.c.016t.fap
):
main (argc, argv)
{
D.6544 = __builtin_object_size (temp, 0);
D.6545 = __builtin_object_size (temp, 0);
D.6547 = __builtin___memcpy_chk (temp, D.6546, 10, D.6545);
D.6550 = __builtin_ia32_shufpd (v_a, v_a, 1);
}
Đây là một đại diện C-như thế nào trình biên dịch thấy mã nội bộ sau khi tất cả biến đổi. Đây là những gì sau đó được chuyển thành hướng dẫn lắp ráp. (chỉ những dòng tham khảo các built-in được hiển thị ở đây)
Với OpenMP kích hoạt các khu vực song song được chiết xuất vào chức năng riêng, main.omp_fn.0
:
main.omp_fn.0 (.omp_data_i)
{
void * (*<T4f6>) (void *, const <unnamed type> *, long unsigned int, long unsigned int) __builtin___memcpy_chk.21;
long unsigned int (*<T4f5>) (const <unnamed type> *, int) __builtin_object_size.20;
vector double (*<T6b5>) (vector double, vector double, int) __builtin_ia32_shufpd.23;
long unsigned int (*<T4f5>) (const <unnamed type> *, int) __builtin_object_size.19;
__builtin_object_size.19 = __builtin_object_size;
D.6587 = __builtin_object_size.19 (D.6603, 0);
__builtin_ia32_shufpd.23 = __builtin_ia32_shufpd;
D.6593 = __builtin_ia32_shufpd.23 (v_a, v_a, 1);
__builtin_object_size.20 = __builtin_object_size;
D.6588 = __builtin_object_size.20 (D.6605, 0);
__builtin___memcpy_chk.21 = __builtin___memcpy_chk;
D.6590 = __builtin___memcpy_chk.21 (D.6609, D.6589, 10, D.6588);
}
Một lần nữa tôi đã chỉ còn lại các mã mà đề cập đến nội trang. Điều rõ ràng (nhưng lý do cho điều đó là không rõ ràng ngay với tôi) là mã trasnformer mã OpenMP thực sự khẳng định khi gọi tất cả các thông tin được tích hợp thông qua các con trỏ hàm. Các ký hiệu con trỏ này:
__builtin_object_size.19 = __builtin_object_size;
__builtin_ia32_shufpd.23 = __builtin_ia32_shufpd;
__builtin_object_size.20 = __builtin_object_size;
__builtin___memcpy_chk.21 = __builtin___memcpy_chk;
tạo các tham chiếu bên ngoài cho các ký hiệu không phải là ký hiệu thực sự mà là các tên được trình biên dịch đặc biệt xử lý. Sau đó, trình liên kết cố gắng giải quyết chúng nhưng không thể tìm thấy bất kỳ tên __builtin_*
nào trong bất kỳ tệp đối tượng nào mà mã được liên kết.Đây cũng là quan sát được trong các mã lắp ráp mà người ta có thể có được bằng cách đi qua -S
-gcc
:
LBB2_1:
movapd -48(%rbp), %xmm0
movl $1, %eax
movaps %xmm0, -80(%rbp)
movaps -80(%rbp), %xmm1
movl %eax, %edi
callq ___builtin_ia32_shufpd
movapd %xmm0, -32(%rbp)
này về cơ bản là một chức năng gọi là mất 3 đối số: một số nguyên trong %eax
và hai đối XMM trong %xmm0
và %xmm1
, với kết quả được trả lại trong %xmm0
(theo quy ước gọi hàm SysV AMD64 ABI). Ngược lại, các mã được tạo mà không -fopenmp
là một mở rộng hướng dẫn cấp của nội tại vì nó là vụ xảy ra:
LBB1_3:
movapd -64(%rbp), %xmm0
shufpd $1, %xmm0, %xmm0
movapd %xmm0, -80(%rbp)
gì xảy ra khi bạn vượt qua -D_FORTIFY_SOURCE=0
là memcpy
không được thay thế bởi phiên bản kiểm tra "củng cố" và một cuộc gọi thông thường đến memcpy
được sử dụng thay thế. Điều này giúp loại bỏ các tham chiếu đến object_size
và __memcpy_chk
nhưng không thể xóa cuộc gọi đến số ia32_shufpd
được tích hợp sẵn.
Đây rõ ràng là lỗi trình biên dịch. Nếu bạn thực sự thực sự thực sự phải sử dụng GCC của Apple để biên dịch mã, sau đó là một giải pháp tạm thời sẽ được di chuyển mã vi phạm đến một chức năng bên ngoài như các lỗi dường như chỉ ảnh hưởng đến mã đó được chiết xuất từ parallel
vùng:
void func(char *temp, char *argv0)
{
__m128d v_a, v_ar;
memcpy(temp, argv0, 10);
v_ar = _mm_shuffle_pd(v_a, v_a, _MM_SHUFFLE2 (0,1));
}
int main(int argc, char *argv[])
{
char *temp;
#pragma omp parallel
{
func(temp, argv[0]);
}
}
Các chi phí của một cuộc gọi chức năng bổ sung là neglegible so với chi phí của việc nhập và thoát khỏi khu vực parallel
. Bạn có thể sử dụng các pragmas OpenMP bên trong func
- chúng sẽ hoạt động vì phạm vi động của vùng parallel
.
Có thể Apple sẽ cung cấp trình biên dịch cố định trong tương lai, có thể họ sẽ không, với cam kết thay thế GCC bằng Clang.
biên dịch tốt cho tôi (OSX10.8.2, Xcode 4.5, macports gcc 4.7.1) khi sử dụng -fopenmp -O1 (hoặc cao hơn, nhưng không -O0: điều này cho lỗi liên kết bị thiếu '___ gxx_personality_v0'). Tuy nhiên mã sản xuất segfault khi chạy. Khi biên dịch mà không có -fopenmp, mã biên dịch cho bất kỳ -O, nhưng một lần nữa segfaults (ngoại trừ -O0: bus error). – Walter
@Walter Cảm ơn. segfault không phải là một vấn đề, mã chỉ là một ví dụ - tất nhiên nó là sai. Bạn đang sử dụng gcc 4.7.1, vì vậy không phải trình biên dịch Xcode, phải không? Bạn có thể biên dịch với dòng lệnh mà tôi đã đưa ra không? Thay đổi mức tối ưu hóa không giúp được gì ở đây .. – angainor
Đây là lỗi trong trình biên dịch 'llvm-gcc' mà Xcode giao với. Nó là một trình biên dịch LLVM với lối vào GCC. Giai đoạn OpenMP tạo ra một số nội trang mà backend không thể nhận ra. Khi Xcode đang dần chuyển sang hướng thay thế hoàn toàn GCC bằng 'clang', lỗi này có thể sẽ không bao giờ được sửa chữa. Chỉ cần cài đặt GCC thực từ nguồn hoặc thông qua một số phương pháp khác và sử dụng nó để biên dịch mã OpenMP. –