2012-01-25 1 views
10

Tôi tháo rời một tập tin đối tượng (rất có thể được tạo ra sử dụng Visual biên dịch ++ C) sử dụng DumpBin và thấy đoạn mã sau:Tại sao trình biên dịch tạo mã này?

...   ... 
mov   dword ptr [ebp-4],eax  // Why save EAX? 
push  dword ptr [ebp+14h] 
push  dword ptr [ebp+10h] 
push  dword ptr [ebp+0Ch] 
push  dword ptr [ebp+8] 
mov   eax,dword ptr [ebp-4]  // Why restore EAX? Did it change at all? 
call  <function> 
...   ... 

Có thể ai đó xin vui lòng giải thích tại sao thanh ghi EAX đang được lưu và khôi phục qua những 4 push hướng dẫn?

+2

Tôi đã nhìn thấy trình biên dịch thực hiện những điều dumber hơn ... – Mysticial

+0

@Micicial: Oh lol ... đây là lần đầu tiên tôi nhận thấy một cái gì đó như thế này. :) Tốt để biết. – Mehrdad

+7

Có lẽ có một nhánh vào lần đẩy đầu tiên. –

Trả lời

9

Ngoài ra, có thể nó được biên dịch trong chế độ phát hành, nhưng biến đó đã được đánh dấu là volatile, cho trình biên dịch biết rằng biến này có thể thay đổi mà không biết. Vì vậy, nó buộc phải ghi/khôi phục lại nó trên/từ ngăn xếp

+0

Ahhhh điều này là hoàn toàn có thể! 1 cảm ơn. – Mehrdad

+0

Tôi không chắc rằng 'volatile' sẽ tạo ra sự khác biệt ở đây. 'volatile' liên quan đến một vị trí bộ nhớ, nhưng EAX là một thanh ghi; bạn không thể đánh dấu một thanh ghi là dễ bay hơi. Vì vậy, 'volatile' sẽ giải thích [ebp-4] được nạp lại * lên * eax ngay trước mỗi thao tác, nhưng không được lưu lại. – Crashworks

+0

Đối với một số arithmetics trình biên dịch _has_ để tải một vị trí bộ nhớ được đánh dấu dễ bay hơi vào một thanh ghi, bởi vì thao tác này không thể thực hiện được khi đọc-sửa-ghi lệnh. Sau phân đoạn mã này, một cửa hàng có thể theo sau và trước khi tải này có thể xảy ra, do đó, từ thông tin trong tầm tay, nó có thể hoàn toàn khả thi. Nhưng nó không chỉ là một sự tối ưu hóa bị bỏ lỡ bởi VC. – hirschhornsalz

6

Điều này có được xây dựng trong chế độ gỡ lỗi không? Nếu vậy, trình biên dịch lưu trữ mọi biến cục bộ trên ngăn xếp để trình gỡ lỗi có thể tìm thấy chúng theo cách nhất quán.

Việc cắt bỏ các cửa hàng không cần thiết và tải lại là một trong những tối ưu hóa cấu thành chế độ "phát hành".

+0

Tôi tin rằng đó là * giả định * là mã chế độ phát hành (không có phiên bản 'gỡ lỗi' của bất kỳ thứ gì có liên quan tôi có thể thấy ở bất kỳ đâu ...), nhưng tôi không chắc ... Tôi không có mã nguồn hoặc. Nhưng +1 đó là một dự đoán hợp lý, cảm ơn. – Mehrdad

2

volatile hay không, chỉ kỹ thuật lý do tại sao EAX sẽ đã được khởi tạo trực tiếp trước khi thực hiện cuộc gọi chức năng trên Windows là nếu đó function được khai báo __syscall, tức là sử dụng các quy ước gọi của Windows CS_SYSCALL. Về mặt khái niệm, điều này hơi giống với quy ước của UN * X x86_64, trong đó %al chứa số lượng các loại dấu phẩy động được chuyển qua trong thanh ghi %xmm.

Quy ước gọi điện trên syscall trên Windows giống hệt với __cdecl, tức là hàm args trên stack theo thứ tự ngược lại, nhưng với phần bổ sung AL chứa số lượng đối số; điều này được thực hiện sao cho mã hạt nhân thường ở đầu cuối cùng của điều này biết có bao nhiêu dữ liệu để đọc từ ngăn xếp người dùng lên ngăn xếp hạt nhân để truy lục args.

EAX là đăng ký đầu cho tất cả các quy ước gọi trên Windows 32 bit, giá trị của nó không bao giờ được bảo toàn qua cuộc gọi hàm, khởi chạy trực tiếp trước khi thực hiện cuộc gọi. Ngay cả khi biến nó giữ là volatile - bởi vì tải lại đơn giản không phải là một rào cản bộ nhớ và không "cam kết" một cửa hàng trước đó. Ngoài ra, vị trí [EBP - 4] nằm trong ngăn xếp , do đó, biến là địa phương (và bộ định tính volatile không có ý nghĩa gì).

Nếu nó không phải là một tối ưu hóa nhỡ sau đó nó có thể là một lời kêu cầu của một __syscall function(...) với số lượng khác nhau của các đối số, như, giả thuyết,

__syscall printf_syscall_conv(char *fmt, ...); 

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3) 
{ 
    if (*strchr('%', fmt) == '\0') // if no "%" in fmt, pass no args 
     printf_syscall_conv(fmt); 
    else 
     printf_syscall_conv(fmt, val1, val2, val3); 
} 

này hình dung có thể tạo ra sản lượng lắp ráp như của bạn.