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.
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