2011-07-05 47 views
6

Tôi hiểu trong assembly x86_64, ví dụ như đăng ký rax (64 bit), nhưng nó cũng có thể được truy cập như đăng ký 32 bit, eax, 16 bit, axe, và 8 bit, al. Trong tình huống nào tôi sẽ không chỉ sử dụng 64 bit đầy đủ, và tại sao, lợi thế nào sẽ có?Lắp ráp 64 bit, khi sử dụng thanh ghi kích thước nhỏ hơn

Như một ví dụ, với chương trình này đơn giản hello world:

section .data 
msg: db "Hello World!", 0x0a, 0x00 
len: equ $-msg 

section .text 
global start 

start: 
mov rax, 0x2000004  ; System call write = 4 
mov rdi, 1    ; Write to standard out = 1 
mov rsi, msg   ; The address of hello_world string 
mov rdx, len   ; The size to write 
syscall     ; Invoke the kernel 
mov rax, 0x2000001  ; System call number for exit = 1 
mov rdi, 0    ; Exit success = 0 
syscall     ; Invoke the kernel 

RDI và RDX, ít nhất, chỉ cần 8 bit và không 64, phải không? Nhưng nếu tôi thay đổi chúng để pha loãng và dl, tương ứng (thấp hơn tương đương 8-bit của họ), chương trình lắp ráp và liên kết nhưng không sản xuất bất cứ điều gì.

Tuy nhiên, nó vẫn hoạt động nếu tôi sử dụng eax, edi và edx, vì vậy tôi có nên sử dụng chúng thay vì toàn bộ 64 bit không? Tại sao hay tại sao không?

+1

Thực tế trong Linux (và có thể là mọi thứ khác?) Các thông số cho một syscall rộng 32 bit, vì vậy bạn nên sử dụng EDI và EDX. http://www.win.tue.nl/~aeb/linux/lk/lk-4.html#ss4.3 –

+0

điều gì về rax, điều đó có nên thay đổi thành eax không?Tôi đã cố gắng thay đổi những thứ 3 và nó hoạt động, nhưng những gì tôi muốn biết là tại sao tôi nên làm điều này và lợi thế là gì. – mk12

+0

Trong trường hợp của chương trình này, sự khác biệt đáng kể duy nhất là giá trị bằng chữ (4, 1, 0, v.v.) lớn gấp đôi khi chúng là 64 bit, do đó chương trình của bạn sẽ lớn hơn vài byte và theo lý thuyết, có thể mất nhiều thời gian hơn để tải vào CPU từ đĩa/bộ nhớ. –

Trả lời

2

Đầu tiên và quan trọng nhất là khi tải giá trị nhỏ hơn (ví dụ 8 bit) từ bộ nhớ (đọc char, làm việc trên cấu trúc dữ liệu, deserialising gói mạng, v.v.) vào sổ đăng ký.

MOV AL, [0x1234] 

so

MOV RAX, [0x1234] 
SHR RAX, 56 
# assuming there are actually 8 accessible bytes at 0x1234, 
# and they're the right endianness; otherwise you'd need 
# AND RAX, 0xFF or similar... 

Hoặc, tất nhiên, viết cho biết giá trị trở lại vào bộ nhớ.


(Chỉnh sửa, như 6 năm sau):

Vì đây tiếp tục trở lên:

MOV AL, [0x1234] 
  • chỉ đọc một byte duy nhất của bộ nhớ tại 0x1234 (nghịch đảo sẽ chỉ ghi đè lên một byte bộ nhớ)
  • giữ mọi thứ nằm trong 56 bit khác của RAX
    • Điều này tạo ra sự phụ thuộc giữa các giá trị trước đây và tương lai của RAX, do đó CPU không thể tối ưu hóa hướng dẫn sử dụng register renaming.

Ngược lại:

MOV RAX, [0x1234] 
  • đọc 8 byte của bộ nhớ bắt đầu từ 0x1234 (nghịch đảo sẽ ghi đè lên 8 byte của bộ nhớ)
  • ghi đè tất cả của rax
  • giả sử các byte trong bộ nhớ có cùng độ bền như CPU ​​(thường không đúng trong các gói mạng, do đó m trước y SHR năm giảng dạy)

Cũng quan trọng cần lưu ý:

MOV EAX, [0x1234] 

Sau đó, như đã đề cập trong các ý kiến, có:

MOVZX EAX, byte [0x1234] 
  • chỉ đọc một byte duy nhất của bộ nhớ tại 0x1234
  • mở rộng giá trị để điền vào tất cả các EAX (và do đó RAX) với zeroes (loại bỏ sự phụ thuộc và cho phép tối ưu hóa đăng ký đổi tên).

Trong tất cả các trường hợp, nếu bạn muốn viết từ 'A' đăng ký vào bộ nhớ bạn phải chọn chiều rộng của bạn:

MOV [0x1234], AL ; write a byte (8 bits) 
MOV [0x1234], AX ; write a word (16 bits) 
MOV [0x1234], EAX ; write a dword (32 bits) 
MOV [0x1234], RAX ; write a qword (64 bits) 
+2

Erm ... x86_64 là _always_ ít endian, vì vậy ví dụ của bạn sẽ mang lại kết quả khác nhau. – Ruslan

+1

Lựa chọn tốt nhất ở đây là 'movzx eax, [0x1234]'. –

+0

Peter cordes là đúng. "Mov al" không phá vỡ chuỗi phụ thuộc. –

1

Nếu bạn muốn làm việc với chỉ một số lượng 8 bit, sau đó bạn sẽ làm việc với thanh ghi AL. Tương tự cho AX và EAX. Ví dụ, bạn có thể có giá trị 64 bit chứa hai giá trị 32 bit. Bạn có thể làm việc với 32 bit thấp bằng cách truy cập thanh ghi EAX. Khi bạn muốn làm việc trên các bit cao 32 bit, bạn có thể hoán đổi hai số lượng 32 bit (đảo ngược các DWORD trong thanh ghi) để các bit cao hiện có trong EAX.

+0

Làm cách nào để chuyển đổi số lượng 32 bit? – mk12

+0

Xoay RAX qua 32 bit. –

+0

Hướng dẫn thực tế trong nasm là gì? Tôi là loại mới với điều này. – mk12

1

64-bit là bộ nhớ lớn nhất bạn có thể làm việc với tư cách một đơn vị duy nhất. Điều đó không có nghĩa là bạn cần sử dụng bao nhiêu.

Nếu bạn cần 8 bit, hãy sử dụng 8. Nếu bạn cần 16, hãy sử dụng 16. Nếu không quan trọng có bao nhiêu bit thì không quan trọng bạn sử dụng bao nhiêu bit.

Phải thừa nhận rằng, khi sử dụng bộ xử lý 64 bit, có rất ít chi phí để sử dụng toàn bộ 64 bit. Nhưng nếu, ví dụ, bạn tính toán một giá trị byte, làm việc với một byte sẽ có nghĩa là kết quả sẽ là kích thước chính xác.

+0

Tôi đã chỉnh sửa câu hỏi của tôi liên quan đến cách điều này gây nhầm lẫn cho tôi. – mk12

5

Bạn đang đặt một số câu hỏi tại đây.

Nếu bạn chỉ tải 8 bit thấp của thanh ghi, phần còn lại của thanh ghi sẽ giữ giá trị trước đó của nó. Điều đó có thể giải thích tại sao cuộc gọi hệ thống của bạn có thông số sai.

Một lý do để sử dụng 32 bit khi đó là tất cả những gì bạn cần là nhiều hướng dẫn sử dụng EAX hoặc EBX ngắn hơn một byte so với sử dụng RAX hoặc RBX. Nó cũng có thể có nghĩa là các hằng số được nạp vào thanh ghi ngắn hơn.

Bộ hướng dẫn đã phát triển trong một thời gian dài và có khá nhiều điều kỳ quặc!

2

Nếu bạn chỉ cần đăng ký 32 bit, bạn có thể làm việc an toàn với chúng, điều này là OK dưới 64 bit. Nhưng nếu bạn chỉ cần đăng ký 16 bit hoặc 8 bit, hãy cố gắng tránh chúng hoặc luôn sử dụng movzx/movsx để xóa các bit còn lại. Được biết, dưới x86-64, việc sử dụng các toán hạng 32 bit sẽ xóa các bit cao hơn của thanh ghi 64 bit. Mục đích chính của việc này là tránh các chuỗi phụ thuộc sai.

Vui lòng tham khảo phần có liên quan - 3.4.1.1 - The Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 1:

32-bit toán hạng tạo ra một kết quả 32-bit, zero-mở rộng đến một kết quả 64-bit trong General- điểm đến mục đích register

Breaking chuỗi phụ thuộc cho phép các hướng dẫn để thực hiện song song, theo thứ tự ngẫu nhiên, bởi Out-of-Order algorithm thực hiện trong nội bộ của CPU từ Pentium Pro vào năm 1995.

AQ uote từ số Intel® 64 and IA-32 Architectures Optimization Reference Manual, Mục 3.5.1.8:

Các chuỗi mã có thể bị trì hoãn trong chuỗi phụ thuộc, nhưng có thể tránh được bằng cách sử dụng thành ngữ phụ thuộc. Trong các bộ vi xử lý dựa trên kiến ​​trúc vi mô của Intel Core, một số hướng dẫn có thể giúp xóa phụ thuộc thực thi khi phần mềm sử dụng các hướng dẫn này để xóa nội dung đăng ký về không. Break phụ thuộc vào các phần của thanh ghi giữa các lệnh bằng cách sử dụng các thanh ghi 32 bit thay vì các thanh ghi một phần. Đối với di chuyển, điều này có thể được thực hiện với các động tác 32 bit hoặc bằng cách sử dụng MOVZX.

hội/Compiler Mã hóa Rule 37. (M va chạm, MH tổng quát): Phá vỡ sự phụ thuộc vào các phần của thanh ghi giữa các hướng dẫn bởi hoạt động trên 32-bit đăng ký thay vì đăng ký một phần. Đối với di chuyển, điều này có thể được thực hiện với các động tác 32 bit hoặc bằng cách sử dụng MOVZX.

MOVZX và MOV với các toán hạng 32 bit cho x64 tương đương - tất cả đều phá vỡ chuỗi phụ thuộc.

Đó là lý do tại sao mã của bạn sẽ thực thi nhanh hơn nếu bạn luôn thử xóa các bit cao nhất của thanh ghi lớn hơn khi sử dụng thanh ghi nhỏ hơn. Khi các bit luôn luôn rõ ràng, thre không phụ thuộc vào giá trị trước đó của thanh ghi, CPU có thể đổi tên nội bộ thanh ghi.

Register renaming là kỹ thuật được sử dụng trong nội bộ bởi một CPU loại bỏ các phụ thuộc dữ liệu sai phát sinh từ việc sử dụng lại sổ đăng ký bằng các lệnh liên tiếp không có bất kỳ phụ thuộc dữ liệu thực nào giữa chúng.

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