標籤:
1.問題
MIPS 下使用訪存指令讀取或寫入資料單元時,目標地址必須是所訪問之資料單元位元組數的整數倍,這個叫做地址對齊。
比如在 MIPS 平台上,lh 讀取一個半字時,儲存空間的地址必須是 2 的整數倍; lw 讀取一個字時,儲存空間的地址必須是 4的整數倍; sd 寫入一個雙字時,儲存空間的地址必須是 8 的整數倍。倘若訪存時,目標地址不對齊,則會引起異常,典型的是系統提示“匯流排錯誤”後,直接殺死進程。
看一個測試程式(龍芯2E平台):
#include <stdio.h>
#include <sys/sysmips.h>
unsigned short data[] = {
0x1, 0x2, 0x3, 0x4,
0x55aa, 0x66bb, 0x77cc, 0x0000,
};
inline void unaligned_access(unsigned short * const row)
{
asm volatile
(
".set mips3\n\t"
".set noreorder\n\t"
//"lwr $10, 1(%1)\n\t"
//"lwl $11, 4(%1)\n\t"
//"or $10, $11\n\t"
"ld $10, 2(%1)\n\t" /* %1 is double word aligned, %1+2 is double word unaligned */
"sd $10, %0\n\t"
".set reorder\n\t"
".set mips0\n\t"
: "=m"(*(row))
: "r"(row+4)
: "$8", "$9", "$10"
);
}
int main()
{
printf("---------------------------------------------------------\n");
printf(" Testing Godson2 unaligned access Instruction \n");
printf("---------------------------------------------------------\n\n");
// sysmips(MIPS_FIXADE, 0);
unaligned_access(data);
printf("result is: 0x%04x %04x %04x %04x\n", data[3], data[2], data[1], data[0]);
}
程式運行後系統提示“非法指令”後退出。
CISC 下(如x86)訪存時,如果目標地址不對齊,CPU 不會陷入異常,因為其內部有處理非對齊訪問的微程式。
2. 解決
進階語言中一般不會遇到這種問題,編譯器常常會處理好資料類型的對齊。但萬一遇到、抑或在彙編裡遇到,避不開怎麼辦?
可以使用 MIPS 的指令集裡提供的 lwr/lwl, swr/srl, ldr/ldl, sdr/sdl 指令對。關於他們的原理可以用來簡單的示意一下(以ldr/ldl 為例,其他類似):
解釋的是小端模式下的情況,大端模式的情況則相反:首先 ldl t0, 0(t1),然後再 ldr t0, 7(t1)。
可以看到無論大端模式還是小端模式,非對齊訪問的解決都是將原來的一條指令(對齊訪問)完成的事分兩步完成,即首先取始地址 addr 到下一個對齊地址處的部分資料,置入寄存器右部(小端),(大端置入左部(高位)),然後取從該對齊地址到 addr + len - 1 處的部分資料(len 為資料單元長度,半字為2, 雙字為8),置入寄存器左部(小端)。
如小端機器上,始地址為 t1 = 0x1022,則:
ldr t0, 0(t1) 取 0x1022~0x1027 到 t0 的右部
ldl t0, 7(t1) 取 0x1028~0x1029 到 t0 的左部
注意上述指令的尾碼 r(right), l(left) 都是相對寄存器而言,load 操作是把取到的部分資料,置入寄存器的 left 或者 right, store 操作是將寄存器中資料的 left 或者 right 部分,寫入目標地址而已。無論大端和小端寄存器的格式都是固定的,即右端為低位,左端為高位。任意第一條ldr/ldl/lwr/lwl/sdr/sdl/swr/swl 只能訪問記憶體的始地址到下一個對齊地址處。
[轉]MIPS 下非對齊訪問的問題