2011-12-03 25 views
8

Tôi đang sử dụng Freescale Kinetis K60 và sử dụng CodeWarrior IDE (mà tôi tin rằng sử dụng GCC cho trình biên dịch).Toán điểm cố định với trình biên dịch ARM Cortex-M4 và gcc

Tôi muốn nhân hai số 32 bit (kết quả là số 64 bit) và chỉ giữ lại 32 bit trên.

Tôi nghĩ rằng hướng dẫn lắp ráp chính xác cho ARM Cortex-M4 là lệnh SMMUL. Tôi muốn truy cập hướng dẫn này từ mã C hơn là lắp ráp. Làm thế nào để tôi làm điều này?

Tôi tưởng tượng mã lý tưởng sẽ là một cái gì đó như thế này:

int a,b,c; 

a = 1073741824; // 0x40000000 = 0.5 as a D0 fixed point number 
b = 1073741824; // 0x40000000 = 0.5 as a D0 fixed point number 

c = ((long long)a*b) >> 31; // 31 because there are two sign bits after the multiplication 
          // so I can throw away the most significant bit 

Khi tôi cố gắng này trong CodeWarrior, tôi nhận được kết quả chính xác cho c (536.870.912 = 0,25 như một số D0 FP). Tôi không thấy hướng dẫn SMMUL ở bất cứ nơi nào và nhân là 3 hướng dẫn (UMULL, MLA, và MLA - Tôi không hiểu tại sao nó sử dụng một số unsigned, nhưng đó là một câu hỏi khác). Tôi cũng đã thử một sự thay đổi đúng của 32 kể từ đó có thể có ý nghĩa hơn cho các hướng dẫn SMMUL, nhưng điều đó không làm bất cứ điều gì khác nhau.

+0

Bạn có đang biên soạn tối ưu hóa không? Nếu trình biên dịch của bạn không tạo ra mã tối ưu, bạn có thể viết một hàm assembly nhỏ hoặc sử dụng một số assembly nội bộ từ C. – TJD

Trả lời

6

Vấn đề bạn nhận được với việc tối ưu hóa mã đó là:

08000328 <mul_test01>: 
8000328: f04f 5000 mov.w r0, #536870912 ; 0x20000000 
800032c: 4770  bx lr 
800032e: bf00  nop 

đang doesnt bạn làm bất cứ điều gì runtime nên tôi ưu hoa chỉ có thể tính toán câu trả lời chính thức.

này:

.thumb_func 
.globl mul_test02 
mul_test02: 
    smull r2,r3,r0,r1 
    mov r0,r3 
    bx lr 

gọi với điều này:

c = mul_test02(0x40000000,0x40000000); 

cho 0x10000000

UMULL cho kết quả tương tự bởi vì bạn đang sử dụng các số dương, các toán hạng và kết quả là tất cả tích cực để nó không nhận được vào sự khác biệt đã ký/unsigned.

Hmm, bạn đã hiểu rõ điều này. Tôi sẽ đọc mã của bạn như nói với trình biên dịch để thúc đẩy nhân lên đến 64 bit. smull là hai toán hạng 32 bit cho kết quả 64 bit, không phải mã của bạn yêu cầu .... nhưng cả gcc và clang đều sử dụng smull anyway, ngay cả khi tôi để nó như là một hàm chưa được nén, vì vậy nó không biết biên dịch thời gian mà các toán hạng không có chữ số đáng kể trên 32, chúng vẫn sử dụng smull.

Có lẽ sự thay đổi là lý do.

Yup, đó là ..

int mul_test04 (int a, int b) 
{ 
    int c; 
    c = ((long long)a*b) >> 31; 
    return(c); 
} 

cho

cả gcc và kêu vang (cũng kêu vang tái chế r0 và r1 thay vì sử dụng r2 và r3)

08000340 <mul_test04>: 
8000340: fb81 2300 smull r2, r3, r1, r0 
8000344: 0fd0  lsrs r0, r2, #31 
8000346: ea40 0043 orr.w r0, r0, r3, lsl #1 
800034a: 4770  bx lr 

nhưng điều này

int mul_test04 (int a, int b) 
{ 
    int c; 
    c = ((long long)a*b); 
    return(c); 
} 

cho

này

gcc:

08000340 <mul_test04>: 
8000340: fb00 f001 mul.w r0, r0, r1 
8000344: 4770  bx lr 
8000346: bf00  nop 

kêu vang:

0800048c <mul_test04>: 
800048c: 4348  muls r0, r1 
800048e: 4770  bx lr 

Vì vậy, với các bit chuyển các trình biên dịch nhận ra rằng bạn chỉ quan tâm đến phần trên của kết quả để họ có thể loại bỏ các phần trên của toán hạng có nghĩa smull thể được dùng.

Bây giờ nếu bạn làm điều này:

int mul_test04 (int a, int b) 
{ 
    int c; 
    c = ((long long)a*b) >> 32; 
    return(c); 
} 

cả các trình biên dịch nhận được thậm chí thông minh hơn, đặc biệt là kêu vang:

0800048c <mul_test04>: 
800048c: fb81 1000 smull r1, r0, r1, r0 
8000490: 4770  bx lr 

gcc:

08000340 <mul_test04>: 
8000340: fb81 0100 smull r0, r1, r1, r0 
8000344: 4608  mov r0, r1 
8000346: 4770  bx lr 

Tôi có thể thấy rằng 0x40000000 coi là một phao mà bạn đang theo dõi vị trí thập phân và địa điểm đó là một vị trí cố định. 0x20000000 sẽ có ý nghĩa như câu trả lời. Tôi chưa thể quyết định liệu sự thay đổi 31 bit đó có hoạt động phổ biến hay chỉ cho một trường hợp này.

Một ví dụ hoàn chỉnh dùng để ở trên là ở đây

https://github.com/dwelch67/stm32vld/tree/master/stm32f4d/sample01

và tôi đã chạy nó trên một stm32f4 để xác minh nó hoạt động và kết quả.

EDIT:

Nếu bạn vượt qua các thông số vào các chức năng thay vì hardcoding chúng trong các chức năng:

int myfun (int a, int b) 
{ 
    return(a+b); 
} 

Trình biên dịch buộc phải làm cho mã runtime thay vì tối ưu hóa câu trả lời tại thời gian biên dịch.

Bây giờ nếu bạn gọi hàm từ chức năng khác với những con số hardcoded:

... 
c=myfun(0x1234,0x5678); 
... 

Trong chức năng gọi điện thoại này trình biên dịch có thể chọn để tính toán các câu trả lời và chỉ cần đặt nó ở đó vào thời điểm biên dịch. Nếu hàm myfun() là toàn cục (không khai báo là tĩnh) thì trình biên dịch không biết nếu một số mã khác được liên kết sau sẽ sử dụng nó ngay cả gần điểm gọi trong tệp này, nó tối ưu hóa câu trả lời nó vẫn phải tạo ra hàm thực và để nó trong đối tượng cho mã khác trong các tệp khác để gọi, vì vậy bạn vẫn có thể kiểm tra trình biên dịch/trình tối ưu hóa làm gì với mã C đó. Trừ khi bạn sử dụng llvm ví dụ, nơi bạn có thể tối ưu hóa toàn bộ dự án (qua các tệp), mã bên ngoài gọi hàm này sẽ sử dụng hàm thực và không phải là một câu trả lời được tính toán thời gian biên dịch.

cả gcc và clang đã làm những gì tôi mô tả, mã thời gian chạy bên trái cho hàm như hàm toàn cầu, nhưng trong tệp nó đã tính toán câu trả lời lúc biên dịch và đặt câu trả lời được mã hóa cứng vào mã thay vì gọi hàm:

int mul_test04 (int a, int b) 
{ 
    int c; 
    c = ((long long)a*b) >> 31; 
    return(c); 
} 

trong một chức năng trong cùng một tập tin:

hexstring(mul_test04(0x40000000,0x40000000),1); 

chức năng chính được thực hiện trong các mã:

0800048c <mul_test04>: 
800048c: fb81 1000 smull r1, r0, r1, r0 
8000490: 0fc9  lsrs r1, r1, #31 
8000492: ea41 0040 orr.w r0, r1, r0, lsl #1 
8000496: 4770  bx lr 

nhưng mà nó được gọi là họ đã hardcoded câu trả lời vì họ đã có tất cả các thông tin cần thiết để làm như vậy:

8000520: f04f 5000 mov.w r0, #536870912 ; 0x20000000 
8000524: 2101  movs r1, #1 
8000526: f7ff fe73 bl 8000210 <hexstring> 

Nếu bạn không muốn câu trả lời hardcoded bạn cần phải sử dụng một chức năng mà không có trong cùng một tối ưu hóa vượt qua.

Thao tác trình biên dịch và trình tối ưu hóa có rất nhiều thực hành và nó không phải là khoa học chính xác vì trình biên dịch và trình tối ưu hóa liên tục phát triển (tốt hơn hoặc tệ hơn).
Bằng cách tách một đoạn mã nhỏ trong một hàm bạn đang gây ra sự cố theo cách khác, các hàm lớn hơn có nhiều khả năng cần một khung ngăn xếp và loại bỏ các biến từ sổ đăng ký sang ngăn xếp khi nó đi, các chức năng nhỏ hơn có thể không cần thực hiện và các trình tối ưu hóa có thể thay đổi cách thực thi mã. Bạn kiểm tra đoạn mã một cách để xem những gì trình biên dịch đang làm sau đó sử dụng nó trong một hàm lớn hơn và không nhận được kết quả mà bạn muốn. Nếu có một hướng dẫn chính xác hoặc trình tự của các hướng dẫn bạn muốn thực hiện .... Thực hiện chúng trong assembler. Nếu bạn đang nhắm mục tiêu một bộ hướng dẫn cụ thể trong một bộ/bộ xử lý cụ thể thì hãy tránh trò chơi, tránh mã của bạn thay đổi khi bạn thay đổi máy tính/trình biên dịch/v.v, và chỉ sử dụng trình kết hợp cho mục tiêu đó. nếu cần ifdef hoặc sử dụng các tùy chọn biên dịch có điều kiện để xây dựng cho các mục tiêu khác nhau mà không có trình biên dịch.

+0

Tôi đã sử dụng trình biên dịch gcc trước đây được biết đến là mã và trình biên dịch từ bản phát hành llvm 3.0. –

+2

Có vẻ như tôi đã sai. CodeWarrior dường như không sử dụng GCC. Nó tham chiếu đến một tệp thực thi được gọi là mwccarm, có vẻ như là một trình biên dịch cụ thể của Freescale. Tôi đã thử xác định nhân trong một chức năng và vẫn không thể làm cho trình biên dịch sử dụng lệnh smull - trong thực tế lệnh umull vẫn còn đó. Tôi sẽ thử tải xuống gcc hoặc clang và xem điều gì sẽ xảy ra. – EpicAdv

+1

Tôi không nghĩ rằng tôi đang thành công nói với trình biên dịch để biên dịch một hàm độc lập. Nó dường như đang nhìn lại nơi (và nếu) nó được gọi và tối ưu hóa. Làm thế nào tôi có thể buộc nó để biên dịch các chức năng chung như bạn đang làm ở đây? – EpicAdv

-1

GCC hỗ trợ các loại điểm cố định thực tế: http://gcc.gnu.org/onlinedocs/gcc/Fixed_002dPoint.html

Tôi không chắc chắn những gì hướng dẫn nó sẽ sử dụng, nhưng nó có thể làm cho bạn cuộc sống dễ dàng hơn.

+1

Từ trang được liên kết: "Không phải tất cả các mục tiêu đều hỗ trợ các loại điểm cố định". Theo kinh nghiệm của tôi, rất ít mục tiêu hỗ trợ các loại điểm cố định. –