2012-05-09 41 views
17

Khi sử dụng các chỉ lệnh SSE2 như PADDD (tức là, _mm_add_epi32 nội tại), có cách nào để kiểm tra xem có bất kỳ thao tác nào bị tràn không?Kiểm tra tràn số nguyên SSE2

Tôi nghĩ rằng có thể một lá cờ trên thanh ghi điều khiển MXCSR có thể được đặt sau khi tràn, nhưng tôi không thấy điều đó xảy ra. Ví dụ, _mm_getcsr() in giá trị như nhau trong cả hai trường hợp dưới đây (8064):

#include <iostream> 
#include <emmintrin.h> 

using namespace std; 

void main() 
{ 
    __m128i a = _mm_set_epi32(1, 0, 0, 0); 
    __m128i b = _mm_add_epi32(a, a); 
    cout << "MXCSR: " << _mm_getcsr() << endl; 
    cout << "Result: " << b.m128i_i32[3] << endl; 

    __m128i c = _mm_set_epi32((1<<31)-1, 3, 2, 1); 
    __m128i d = _mm_add_epi32(c, c); 
    cout << "MXCSR: " << _mm_getcsr() << endl; 
    cout << "Result: " << d.m128i_i32[3] << endl; 
} 

Có một số cách khác để kiểm tra tràn với SSE2?

+3

bạn có thể lặp lại tính toán ở chế độ bão hòa (PADDS) và so sánh kết quả. –

+2

Bạn có quan tâm đến tràn hoặc đã ký không? –

+3

@Dmitri: không có thêm 32 bit bão hòa trong SSE2 (chỉ 8 bit và 16 bit) –

Trả lời

10

Đây là một phiên bản hơi hiệu quả hơn @hirschhornsalz'ssum_and_overflow function:

void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow) 
{ 
    __v4si sa, sb; 

    sum = _mm_add_epi32(a, b);     // calculate sum 
    sa = _mm_xor_si128(sum, a);     // compare sign of sum with sign of a 
    sb = _mm_xor_si128(sum, b);     // compare sign of sum with sign of b 
    overflow = _mm_and_si128(sa, sb);   // get overflow in sign bit 
    overflow = _mm_srai_epi32(overflow, 31); // convert to SIMD boolean (-1 == TRUE, 0 == FALSE) 
} 

Nó sử dụng một biểu thức để phát hiện tràn từ Hacker's Delight trang 27:

sum = a + b; 
overflow = (sum^a) & (sum^b);    // overflow flag in sign bit 

Lưu ý rằng các vector tràn sẽ chứa hơn giá trị boolean SIMD thông thường là -1 cho TRUE (tràn) và 0 cho FALSE (không tràn). Nếu bạn chỉ cần tràn trong bit dấu và các bit khác "không quan tâm" thì bạn có thể bỏ qua dòng cuối cùng của hàm, giảm số hướng dẫn SIMD từ 5 xuống 4.

NB: this giải pháp, cũng như previous solution on which it is based là dành cho các giá trị số nguyên đã ký. Giải pháp cho giá trị chưa ký sẽ yêu cầu một cách tiếp cận hơi khác (xem) của @Stephen Canon).

+1

Cảm ơn! Đó là một số thủ thuật tốt đẹp, đặc biệt. sự dịch chuyển đúng để tái tạo bit dấu. –

+1

Giá trị làm nổi bật giải pháp này chỉ hợp lệ cho các giá trị được ký tên - không phải là UNSIGNED. – SquareRootOfTwentyThree

+0

@SquareRootOfTwentyThree: cảm ơn - điều đó phải rõ ràng từ tất cả các tham chiếu đến bit dấu, v.v., nhưng tôi sẽ thêm một câu nữa chỉ để làm cho nó rõ ràng 100%. –

2

Không có cờ nào được chạm vào theo hướng dẫn PADDD cơ bản.

Vì vậy, để kiểm tra điều này, bạn phải viết mã bổ sung, tùy thuộc vào những gì bạn muốn làm.

Lưu ý: Bạn đang có một chút cản trở bởi sự thiếu epi32 intrisics

9

Vì bạn có 4 tràn có thể, đăng ký kiểm soát sẽ rất nhanh chóng chạy ra khỏi bit, đặc biệt, nếu bạn muốn carrys, ký vv và rằng ngay cả đối với việc bổ sung vector bao gồm 16 byte :-)

Cờ tràn được đặt, nếu bit dấu đầu vào bằng nhau và bit dấu kết quả khác với bit dấu đầu vào.

Chức năng này tính toán sum = a+b và tràn theo cách thủ công. Đối với mọi lần tràn 0x80000000, hãy trả lại trong overflow.

void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow) { 
    __v4si signmask = _mm_set1_epi32(0x80000000); 
    sum = a+b; 
    a &= signmask; 
    b &= signmask; 
    overflow = sum & signmask; 
    overflow = ~(a^b) & (overflow^a); // overflow is 1 if (a==b) and (resultbit has changed) 
} 

Lưu ý: Nếu bạn không có gcc, bạn phải thay thế ^&+ khai thác bởi intrinsics SSE thích hợp, như _mm_and_si128(), _mm_add_epi32(), vv

Chỉnh sửa: Tôi chỉ nhận thấy and với mặt nạ tất nhiên có thể được thực hiện ở phần cuối của chức năng, tiết kiệm hai hoạt động and. Nhưng trình biên dịch sẽ rất có khả năng đủ thông minh để tự làm điều đó.

+0

Cảm ơn, drhirsch! Tôi chấp nhận câu trả lời của Paul R vì đó là câu trả lời có ý nghĩa nhất để làm nổi bật cho hậu thế, nhưng câu trả lời của bạn rất hữu ích và tôi đánh giá cao thời gian bạn đã viết để viết lên. –

4

Tôi nhận thấy bạn đã yêu cầu giải pháp chưa ký; may mắn thay, đó là khá dễ dàng quá:

__v4si mask = _mm_set1_epi32(0x80000000); 
sum = _mm_add_epi32(a, b); 
overflow = _mm_cmpgt_epi32(_mm_xor_si128(mask, a), _mm_xor_si128(mask, sum)); 

Thông thường để phát hiện tràn unsigned, bạn chỉ cần kiểm tra hoặc sum < a hoặc sum < b. Tuy nhiên, SSE không có các so sánh chưa ký; xor -ing các đối số với 0x80000000 cho phép bạn sử dụng so sánh đã ký để có được kết quả tương tự.

+1

AVX512 cuối cùng thêm các so sánh chưa ký: ['_mm512_cmp [eq | ge | gt | le | lt | neq] _epu32_mask'] (https://github.com/HJLebbink/asm-dude/wiki/VPCMPD_VPCMPUD). Đầy đủ các kích thước phần tử 8/16/32/64 bit có sẵn cho chữ ký và chưa ký ('epi' so với' epu'), với một biến vị ngữ so sánh tùy ý thay vì chỉ 'gt' và' eq' cho AVX2 và trước đó so sánh số nguyên. –

+1

@PeterCordes * cuối cùng *! –

Các vấn đề liên quan