2009-12-02 43 views
10

Trong C++, biến cục bộ luôn được cấp phát trên ngăn xếp. Ngăn xếp là một phần của bộ nhớ được phép mà ứng dụng của bạn có thể chiếm. Bộ nhớ đó được lưu trong bộ nhớ RAM của bạn (nếu không được chuyển đổi sang đĩa). Bây giờ, trình biên dịch C++ có tạo mã lắp ráp lưu trữ các biến cục bộ trên ngăn xếp không?C++ CPU Đăng ký sử dụng

Lấy ví dụ, đoạn code đơn giản sau đây:

int foo(int n) { 
    return ++n; 
} 

Trong mã lắp ráp MIPS, điều này có thể trông như thế này:

foo: 
addi $v0, $a0, 1 
jr $ra 

Như bạn thấy, tôi không cần phải sử dụng ngăn xếp ở tất cả cho n. Trình biên dịch C++ có nhận ra điều đó và trực tiếp sử dụng thanh ghi của CPU không?

Chỉnh sửa: Rất tiếc, cảm ơn rất nhiều câu trả lời gần như ngay lập tức và mở rộng của bạn! Phần thân hàm của foo dĩ nhiên là return ++n;, không phải là return n++;. :)

+0

Trình biên dịch sẽ tối ưu hóa. Hãy thử 'gcc -fverbose-asm -O2 -S yoursource.c' rồi xem bên trong' yoursource.s' –

Trả lời

9

Disclaimer: Tôi không biết MIPS, nhưng tôi biết một số x86, và tôi nghĩ rằng các nguyên tắc cần được như vậy ..

Trong quy ước gọi hàm thông thường, trình biên dịch sẽ đẩy giá trị của n lên ngăn xếp để chuyển nó tới hàm foo. Tuy nhiên, có quy ước fastcall mà bạn có thể sử dụng để yêu cầu gcc chuyển giá trị thông qua thanh ghi thay thế. (MSVC cũng có tùy chọn này, nhưng tôi không chắc cú pháp của nó là gì.)

test.cpp:

int foo1 (int n) { return ++n; } 
int foo2 (int n) __attribute__((fastcall)); 
int foo2 (int n) { 
    return ++n; 
} 

Biên dịch trên với g++ -O3 -fomit-frame-pointer -c test.cpp, tôi nhận được cho foo1:

mov eax,DWORD PTR [esp+0x4] 
add eax,0x1 
ret 

Như bạn thấy, nó đọc trong giá trị từ stack.

Và đây là foo2:

lea eax,[ecx+0x1] 
ret 

Bây giờ nó mất giá trị trực tiếp từ thanh ghi.

Tất nhiên, nếu bạn inline chức năng trình biên dịch sẽ làm một bổ sung đơn giản trong cơ thể của chức năng lớn hơn của bạn, bất kể quy ước gọi bạn chỉ định. Nhưng khi bạn không thể hiểu được, điều này sẽ xảy ra.

Tuyên bố từ chối trách nhiệm 2: Tôi không nói rằng bạn nên liên tục đoán lần hai trình biên dịch. Nó có lẽ không thực tế và cần thiết trong hầu hết các trường hợp. Nhưng đừng cho rằng nó tạo ra mã hoàn hảo.

Chỉnh sửa 1: Nếu bạn đang nói về biến cục bộ (không phải đối số hàm), thì có, trình biên dịch sẽ phân bổ chúng trong sổ đăng ký hoặc trên ngăn xếp khi nó khớp.

Chỉnh sửa 2: Dường như quy ước gọi là kiến ​​trúc cụ thể và MIPS sẽ vượt qua bốn đối số đầu tiên trên ngăn xếp, như Richard Pennington đã nêu trong câu trả lời của mình. Vì vậy, trong trường hợp của bạn, bạn không cần phải chỉ định thuộc tính bổ sung (mà trên thực tế là thuộc tính x86 cụ thể.)

+1

-O vô hiệu hóa thiết lập khung ngăn xếp trên các máy mà nó không can thiệp vào việc gỡ lỗi - x86 isn 't một trong số họ, bạn cần một riêng biệt-fomit-frame-pointer để loại bỏ' dư thừa 'stack khung thiết lập (mà thực sự là hữu ích cho gỡ lỗi, tức là trong stack khung thư giãn) – matja

+0

yeah, tôi hoàn toàn quên về điều đó. Tôi sẽ sửa chữa nó. Nhưng sự khác biệt vẫn còn. – int3

+0

Trình biên dịch tối ưu hóa thời gian liên kết cũng có thể nhận ra rằng một cuộc gọi có thể được chuyển thành một cuộc gọi nhanh chóng một mình vì nó có thể xem và sửa tất cả các trang web cuộc gọi. –

12

Có. Không có quy tắc rằng "biến luôn được phân bổ trên ngăn xếp". Tiêu chuẩn C++ không nói gì về một ngăn xếp. Nó không cho rằng một ngăn xếp tồn tại, hoặc các thanh ghi đó tồn tại. Nó chỉ nói làm thế nào mã nên hành xử, không phải như thế nào nó nên được thực hiện.

Trình biên dịch chỉ lưu trữ các biến trên ngăn xếp khi nó phải - khi họ phải sống qua một cuộc gọi hàm chẳng hạn, hoặc nếu bạn cố gắng lấy địa chỉ của chúng.

Trình biên dịch không ngu ngốc. ;)

8

Có, tốt, tối ưu hóa C/C++ sẽ tối ưu hóa điều đó. Và thậm chí MUCH khác: See here: Felix von Leitners Compiler Survey.

Trình biên dịch C/C++ bình thường sẽ không đặt mọi biến trên ngăn xếp. Vấn đề với hàm foo() của bạn có thể là biến có thể được chuyển qua ngăn xếp đến hàm (ABI của hệ thống của bạn (phần cứng/HĐH) định nghĩa).

Với C's register từ khóa, bạn có thể cung cấp cho trình biên dịch một gợi ý rằng có lẽ sẽ tốt để lưu trữ biến trong sổ đăng ký. Ví dụ:

register int x = 10; 

Nhưng hãy nhớ: Trình biên dịch miễn phí không lưu trữ x trong sổ đăng ký nếu muốn!

+0

+1 cho liên kết tuyệt vời –

6

Câu trả lời là có, có thể. Nó phụ thuộc vào trình biên dịch, mức tối ưu hóa và bộ xử lý đích.

Trong trường hợp của mips, bốn tham số đầu tiên, nếu nhỏ, được chuyển vào sổ đăng ký và giá trị trả lại được trả về trong sổ đăng ký. Vì vậy, ví dụ của bạn không có yêu cầu để phân bổ bất cứ điều gì trên stack.

Thực ra, sự thật là lạ hơn tiểu thuyết. Trong trường hợp của bạn các thông số được trả về không thay đổi: giá trị trả về là của n trướC++ hành:

foo: 
    .frame $sp,0,$ra 
    .mask 0x00000000,0 
    .fmask 0x00000000,0 

    addu $2, $zero, $4 
    jr  $ra 
    nop 
2

Từ ví dụ của bạn foo chức năng là một chức năng nhận dạng (nó chỉ trả về nó lập luận), C của tôi ++ biên dịch (VS 2008) hoàn toàn loại bỏ cuộc gọi chức năng này. Nếu tôi thay đổi nó để:

int foo(int n) { 
    return ++n; 
} 

các inlines trình biên dịch này với

lea edx, [eax+1] 
+0

Có, một lần nữa với ví dụ mips: static int foo (int n) { trả về n ++; } phí int() { return foo (5); } cho: .text .align 2 phí .globl phí .ent Phí: .frame $ sp, 0, $ ra .mask 0x00000000,0 .fmask 0x00000000,0 addiu $ 2 , $ 0, 5 jr $ ra nop .set macro .set reorder .end phí .size fee,.-fee –

0

Vâng, Các thanh ghi được sử dụng trong C++. MDR (bộ nhớ dữ liệu bộ nhớ) chứa dữ liệu đang được tìm nạp và lưu trữ. Ví dụ, để lấy nội dung của ô 123, chúng ta sẽ nạp giá trị 123 (trong nhị phân) vào MAR và thực hiện một thao tác tìm nạp. Khi hoạt động được thực hiện, một bản sao nội dung của ô 123 sẽ nằm trong MDR. Để lưu trữ giá trị 98 vào ô 4, chúng ta nạp một số 4 vào MAR và 98 vào MDR và ​​thực hiện một cửa hàng. Khi hoạt động được hoàn thành, nội dung của ô 4 sẽ được đặt thành 98, bằng cách loại bỏ bất kỳ nội dung nào đã có trước đó. Dữ liệu & thanh ghi địa chỉ hoạt động với chúng để đạt được điều này.Trong C++, khi chúng ta khởi tạo một var với một giá trị hoặc hỏi giá trị của nó, cùng một hiện tượng xảy ra.

Và, One More Thing, Trình biên dịch hiện đại cũng thực hiện Phân bổ đăng ký, nhanh hơn phân bổ bộ nhớ.

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