Hướng dẫn NOP sau khi phân nhánh trên ARMv7 Cortex M3


7

Tôi quan tâm, tại sao trình biên dịch vi điều khiển Cortex M3 (stm32f103) đôi khi tạo ra một lệnh NOP sau nhánh. Và tại sao đôi khi nó không.

Ví dụ:

0x08000496 2400      MOVS     r4,#0x00
0x08000498 4625      MOV      r5,r4
0x0800049A E006      B        0x080004AA
    64: res=res+a[i];
    65: }
0x0800049C F85A0034  LDR      r0,[r10,r4,LSL #3] // No NOP after B
0x080004A0 EB100808  ADDS     r8,r0,r8
0x080004A4 1C64      ADDS     r4,r4,#1
0x080004A6 F1450500  ADC      r5,r5,#0x00
0x080004AA 1BA0      SUBS     r0,r4,r6
0x080004AC EB750007  SBCS     r0,r5,r7
0x080004B0 DBF4      BLT      0x0800049C
    66: int64_t avg=res/x;
0x080004B2 BF00      NOP      // <------------------- NOP after BLT
    69: int v=countbits1(5);
0x080004B4 2005      MOVS     r0,#0x05
0x080004B6 F7FFFFA2  BL.W     countbits1 (0x080003FE)
0x080004BA 9001      STR      r0,[sp,#0x04]     // No NOP after BL.W
    72: unsigned int b=countLeadingZeros(5);
    73:  
0x080004BC 2005      MOVS     r0,#0x05

Dự đoán ban đầu của tôi là hướng dẫn dài cần căn chỉnh từ nhưng BL.W sau khi NOP thực sự không có nó. Nếu NOP này có liên quan đến đường ống bằng cách nào đó hơn tại sao có các nhánh không có nop sau chúng?

Tôi bối rối.

CẬP NHẬT:

Hóa ra chi nhánh có thể không liên quan. Tôi đã cố gắng di chuyển khai báo biến cục bộ không sử dụng int64_t avg - và NOP di chuyển cùng với nó. Vì vậy, tôi tin rằng pjc50 bình luận là đúng và NOP này chỉ để cho trình gỡ lỗi đặt một điểm dừng trên dòng này.


3
Hãy thử di chuyển dòng int64_t của C lên hoặc xuống một chút. Hai cái đầu tiên rõ ràng là một phần của vòng lặp for; nhưng dòng 66 đã không tạo ra mã nào cả. Tôi đoán là NOP là vì lợi ích của trình gỡ lỗi để mỗi dòng C tạo ra ít nhất một lệnh.
pjc50

@ pjc50 bây giờ thật thú vị! NOP này thực sự được kết nối với int64_t avg; di chuyển dòng 66 di chuyển NOP. Nhưng nếu tôi thay đổi loại avg thành int32_t thì không có NOP. Biến cục bộ này, trên thực tế, không được sử dụng, do đó trình biên dịch không tạo ra bất kỳ mã nào cho nó .. Ngoài NOP này cho loại 64 bit. Nếu tôi thay đổi loại, tôi vẫn có thể đặt điểm dừng trên dòng đó (và nó sẽ nằm trên MOVS). Rất thú vị.
Amomum

Bất kỳ ý tưởng tại sao biến 64 bit được vinh danh với NOP?
Amomum

ARM và LinkedIn đều có những diễn đàn tuyệt vời cho loại câu hỏi này - bạn có thể thử ở đó. Tôi có một vài ý kiến ​​để thêm vào: đôi khi cả hai kết quả của một nhánh đều được tìm nạp và tùy thuộc vào kết quả của nhánh đó, một phương án sẽ bị loại bỏ. Ngoài ra, Cortex M3 có thể chọn không chạy NOP, một điểm thú vị nếu bạn đang sử dụng chúng cho sự chậm trễ ngắn. Tôi hy vọng những ý kiến ​​có liên quan.
Ant

Vì đây chủ yếu là một câu hỏi về trình biên dịch ít liên quan đến EE ... bạn có thể gặp may mắn hơn khi hỏi nó trên SO.
Fizz

Câu trả lời:


1

Hãy thử di chuyển dòng int64_t của C lên hoặc xuống một chút. Hai cái đầu tiên rõ ràng là một phần của vòng lặp for; nhưng dòng 66 đã không tạo ra mã nào cả. Tôi đoán là NOP là vì lợi ích của trình gỡ lỗi để mỗi dòng C tạo ra ít nhất một lệnh.

(Không phải tất cả các trình sửa lỗi đều thực hiện việc này trên tất cả các nền tảng; Visual Studio sẽ chỉ di chuyển điểm dừng của bạn đến dòng gần nhất có mã được liên kết với nó.)


Bạn đã đúng, di chuyển khai báo biến đã di chuyển lệnh NOP; hướng dẫn chi nhánh hóa ra không liên quan.
Amomum

4

Nhiều trình biên dịch (hầu hết? Tất cả?) Cuối cùng đưa ra các hướng dẫn NOP sau một số hướng dẫn kiểu nhảy / nhánh (không phải khác).

Khi trình biên dịch nhìn thấy một hướng dẫn loại "nhảy", nó có hai hướng dẫn khác nhau có thể thực hiện công việc. Một là tương đối, một là tuyệt đối.

Một là một bước nhảy tương đối, và một là một bước nhảy tuyệt đối. Bước nhảy tương đối nhanh hơn và chỉ định bước nhảy tương đối so với lệnh hiện tại - địa chỉ để nhảy tới là một byte đơn, do đó, nó có thể nhảy về phía trước 128 byte hoặc lùi 127 byte.

Cái khác là một bước nhảy tuyệt đối - cái này chậm hơn và chỉ định địa chỉ để nhảy tới. Điều này có thể nhảy đến bất cứ nơi nào.

Vấn đề là, khi nhảy về phía trước, địa chỉ đích có thể chưa được biết - nó sẽ phải biên dịch mã đến đích nhảy, sau đó xử lý nếu nó nhỏ hơn 128 byte. Tất nhiên, để tìm ra có bao nhiêu byte để nhảy về phía trước, bạn cần phải biết có bao nhiêu byte lệnh NIS này, cũng như mọi hướng dẫn giữa đây và đó.

Đây là cách bên ngoài sự hoàn trả của một trình biên dịch; nó để lại không gian cho một bước nhảy tuyệt đối, sau đó trên đường chuyền thứ hai, khi nó biết vị trí của tất cả các địa chỉ, nó sẽ lấp đầy các khoảng trống - hoặc đưa vào một bước nhảy tương đối hiệu quả hơn (+ một NOP, vì nó phải chiếm cùng số lượng byte!) hoặc đưa vào một bước nhảy tuyệt đối.


0

Bộ vi điều khiển Cortex-M3 được xây dựng với kiến ​​trúc 32 bit nhưng chúng không giới hạn đầu vào ở kích thước bus này (xem http://www.silabs.com/Support%20Document/TechnicalDocs/EFM32-Cortex-M3-RM.pdf ) . Kiến trúc có một bộ đệm tìm nạp nhỏ để xử lý trường hợp các hướng dẫn từ không được sắp xếp để khi nhánh được lấy, bạn không gặp vấn đề gì. Khi nhánh không được thực hiện, nó sẽ cần phải làm sạch đường ống để loại bỏ lựa chọn sai và tìm nạp lại. Nó không thể tìm nạp trực tiếp từ 0x080004B2 vì đây không phải là 4 byte được căn chỉnh và nếu nó được tìm nạp từ 0x080004B0, nó sẽ đọc lại nhánh. NOP sau đó rất cần thiết cho phần đệm thêm mà nó cần tìm nạp từ từ được căn chỉnh 0x080004B4 và tiếp tục từ đó.

http://community.arm.com/groups/embedded/blog/2014/11/07/itern-inquiry-1031--assugging-on-cortex-m3-question


Tôi sợ đó không phải là trường hợp. Xin vui lòng xem cập nhật cho câu hỏi.
Amomum
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.