我有以下 c 函式,它只是對給定輸入陣列的三個元素求和。
#include <array>
using namespace std;
int square(array<int, 3> ar) {
int res = 0;
for(int idx = 0; idx < ar.size(); idx ){
res = ar[idx];
}
return res;
}
此代碼使用 Clang(gcc 和 icc 生成相同的代碼)和編譯器標志 -O3 編譯生成以下 x86-64 程式集
sum(std::array<int, 3ul>):
mov rax, rdi
shr rax, 32
add eax, edi
add eax, esi
ret
我目前對大會的解釋是發生以下情況:
- 64 位從 64 位輸入暫存器 rdi 移動到 64 位輸出暫存器 rax。這對應于 32 位整數。
- shr 將 rax 的內容移動 32 位,從而只保留 rdi 中包含的前 32 位 int。
- 將 32 位輸入暫存器 edi 的內容添加到 32 位輸出暫存器 eax
- 將第二個 32 位輸入暫存器 esi 的內容添加到 eax
- 回傳 eax
然而,我留下了一些問題:
- 計算機能否像前兩條指令那樣簡單地在 32 位和 64 位暫存器之間移動?
- 不應該使用 shr 導致第一個 int 被添加兩次,因為第二個 int 被移出?(這與位元組序有關嗎?)
額外說明:當提供基于范圍的 for 回圈時,編譯器會生成相同的匯編指令。
#include <array>
using namespace std;
int sum(array<int, 3> ar) {
int res = 0;
for(const auto& in: ar){
res = in;
}
return res;
}
您可以在此處找到示例:https : //godbolt.org/z/s3fera7ca
uj5u.com熱心網友回復:
該陣列被打包到用于引數傳遞的暫存器中,就好像它是一個 3int秒的簡單結構。
因此,兩個 32 位int元素在第一個引數暫存器中傳遞,剩下的一個在第二個引數暫存器中傳遞。
考慮到此示例中不涉及記憶體,因此前兩個如何打包到一個暫存器中似乎有些武斷,而且要清楚的是,暫存器本身本身沒有概念位元組序。位元組序是由采用多個記憶體地址的數字資料引入的——不是由暫存器中的任何內容或暫存器的任何內容引入的:暫存器只能命名(在機器代碼指令中),但不能尋址,因此沒有位元組序的概念暫存器內。
但是,對于涉及從記憶體中存盤和加載相同結構的其他一些操作,如果該打包遵循處理器的位元組序是有效的,因此這是 ABI 設計者的邏輯選擇,他們指定(按規則)在暫存器中作為引數傳遞時,結構的第一個元素、第二個元素和第三個元素所在的位置。
當遵循處理器位元組序時,程式可以使用四字加載或存盤和雙字加載或存盤來復制結構——64 位操作后跟 32 位操作。如果暫存器中沒有遵循處理器的自然位元組序(這實際上仍然可以作業),那么將需要三個雙字加載或存盤操作,以從/到記憶體中獲取陣列元素的正確順序。
通過遵循自然位元組序,機器代碼可以混合 64 位和 32 位加載和存盤操作,即使該結構僅包含 32 位專案。
edi 如何融入其中?
edi是陣列/結構的第一個元素。 rdi >> 32是第二個,因為它被打包到 的高 32 位rdi,而第一個元素被打包到 的低 32 位rdi。并且esi是第三個。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/392651.html
