深入理解計算機系統家庭作業第三章
***3.54
***寫出decode2的原型
*/
int decode2(int x ,int y, int z)
{
int a = z - y;
int b = (a << 15) >> 15;
return (x ^ a) * b;
}
/*
***3.55
*/
typedef long long ll_t;
void store_prod(ll_t *dest, ll_t x, int y){
*dest = x*y;
}
// dest at %ebp+8, x at %ebp + 12, y at %ebp + 20 movl 12(%ebp), %esi //將x的低位存到%esi movl 20(%ebp), %eax //將y存到%eax movl %eax, %edx sarl $31, %edx //將(y >> 31)存到%edx movl %edx, %ecx imull %esi, %ecx //計算x_low * (y >> 31)存到%ecx movl 16(%ebp), %ebx //將x的高位存到%ebp imull %eax, %ebx //計算x_high * y addl %ebx, %ecx //計算 x_high * y + x_low * (y >> 31) 存到%ecx mull %esi //計算y * x_low 的無符號64位乘積 leal (%ecx, %edx), %edx //將64位乘積的高位與x_high * y + x_low * (y >> 31)得到最終結果的高位 movl 8(%ebp), %ecx movl %eax, (%ecx) movl %edx, 4(%ecx) //將結果寫入目的記憶體地址
說明:
該彙編程式碼其實是y擴充套件至64位再進行兩個64位數的乘積然後進行截斷得到的。
事實上有:
y *{signed} x =
(y_high * 2^32 + y_low) *{signed} (x_high * 2^32 + x_low) =
y_high *{signed} x_high * 2^64 +
y_high *{signed} x_low * 2^32 +
y_low *{signed} x_high * 2^32 +
y_low *{signed} x_low(有符號的x_low與無符號的x_low相等,故可用mull指令)
而y_high *{signed} x_high由於乘以2^64,所以對結果不會產生影響。
/*
***3.56寫出loop函式原型
*/
int loop(int x, int n)
{
int result = 0x55555555;
int mask;
for(mask = 0x80000000;mask !=0; mask = (unsigned)mask >> (n & 0xFF))
{
result ^= x & mask;
}
return result;
}
/*
***3.57用條件傳送指令寫函式cread_alt
*/
int cread_alt(int *xp){ int a = 0; return *(xp ? xp : &t); }
思路:原來函式中的錯誤就是無論xp是否為NULL,都對其發生了間接引用。將引用符號放在最外面,那麼當xp為NULL時,
條件傳送指令將臨時變數a的地址傳送至%eax,故不會產生對xp的間接引用。下面貼上產生的彙編程式碼:
subl $16, %esp
.cfi_def_cfa_offset 20
movl 20(%esp), %eax
movl $0, 12(%esp)
leal 12(%esp), %edx
testl %eax, %eax
cmove %edx, %eax
movl (%eax), %eax
addl $16, %esp
.cfi_def_cfa_offset 4
ret
/*
***3.58
*/
int switch3(int *p1,int *p2,mode_t action)
{
int result = 0;
switch(action){
case MODE_A:
result = *p1;
*p1 = *p2;
break;
case MODE_B:
result = *p1 + *p2;
*p2 = result;
break;
case MODE_C:
*p2 = 15;
result = *p1;
break;
case MODE_D:
*p2 = *p1;
result = 17;
break;
case MODE_E:
result = 17;
break;
default:
result = -1;
break;
}
return result;
}
/*
***3.59根據反彙編程式碼補全c語言程式碼
*/
int switch_prob(int x,int n)
{
int result = x;
switch(n){
case 40:
case 42:
result <<=3;
break;
case 43:
result >>=3
break;
case 44:
result = (result<<3) - result;
case 45:
result *= result;
result += 17;
break;
default:
result += 17;
break;
}
return result;
}
/*
***3.60
*/
A. &A[i][j][k] = Xa + L(i * S*T + j * T + K)
B. 根據彙編程式碼可得,9*j存在暫存器%eax中,63*i存在暫存器%edx中,對照公式可得S=7,T=9;
最後一句movl $2772 %eax 可知R = 2772/S/T/4 = 11
/*
***迴圈中的值的數量從6個減少到5個
*/
int car_prod_ele(int n,int A[n][n],int B[n][n],int i,int k)
{
int j;
int result = 0;
int *ap = A[i];
int *bp = &B[0][k];
for(j = 0; j < n; j++)
{
result += *ap * *bp;
ap++;
bp += n;
}
return result;
}
這題沒有想到很好的解法,以上程式碼經測試是將4n放到暫存器%ebp當中,實際上並沒有減少迴圈值的數量,望高人指點。
/*
***3.62
*/
A. M的值為19
B. %edi儲存i,%ecx儲存j
C.
void transpose(int A[M][M]){
int i,j;
for(i = 0;i < M;i++)
{
int *p1 = &A[0][i];
for(j = 0;j < i;j++)
{
int *p2 = &A[i][0];
int t = *p1; //交換對稱的兩個值
*p1 = *p2;
*p2 = t;
p1 += M; //改變兩個元素的指標
p2 += 1;
}
}
}
/*
***3.63
*/
E1 = 3n
E2 = 2n - 1
說明:%esi中存放的值為3n,再對照18行可知E1 = 3n;
每次指標的值變化為8n - 4,可知E2 = 2n - 1。
/*
***3.64
*/
A. 8(%ebp)存放返回值result的地址;12(%ebp)存放s1.p;16(%ebp)存放s1.v。
B. 從高到低依次為s2.sum,s2.prod,s1.v,s1.p,&s2
C. 傳遞結構體引數時,結構體各引數從高到低依次壓入棧中
D. 在8(%ebp)處有一個指向返回值結構體的指標,指標的值作為返回地址,根據返回地址寫入值
/*
***3.65
*/
A=3
B=7
首先分析B,t的首地址和u的首地址相差20個位元組,t本身佔4位元組,所以short陣列s[B]佔16個位元組,根據四位元組對齊的原則,可知B=7或8;
short x[A][B]佔44個位元組,當B=8時,A無解,當B=7時,可得A=3.
/*
***3.66
*/
A. CNT = 7;
B. struct a_struct{
int idx;
int x[6];
}
這個題最難的地方是彙編程式碼的第十行,對照彙編程式碼和C程式碼可知,彙編程式碼的第十二行對應C程式碼的第九行,彙編程式碼的十三行對應C程式碼的十一行。
經過分析(事實上帶有一些猜測),第十行add的第一個運算元就是ap->idx的值,加上7i的原因是a_struct中有7個整型變數(這與我們計算ap->idx的地址
為bp + 28i + 4)也是吻合的;然後我們分析彙編程式碼的十三行,前面的0x8應該看做0x4 + 0x4,第一個0x4是b_struct中left佔的位元組數,第二個0x4是a_struct
中idx佔的位元組數,所以可知idx是結構體第一個變數,另外,x是長度為6的陣列。
/*
***3.67
*/
A. 下列欄位的偏移量值是多少(以位元組為單位)
e1.p: 0
e1.x: 4
e2.y: 0
e2.next: 4
B. 這個結構總共需要8個位元組
C.
void proc (union ele *up)
{
up->e2.next->e1.x = *(up->e2.next->e1.p) - up->e2.y;
}
/*
***3.68寫一個good_echo函式,對任意長度的輸入行都能工作(即時超過了緩衝區)
*/
#define N 50
void good_echo()
{
char buf[N];
while(fgets(buf,N,stdin) != NULL)
{
for(int i = 0;i < N;i++)
{
if(buf[i] == '1')
return;
if(buf[i] == '\0')
break;
putchar(buf[i]);
}
}
}
/*
***3.69寫出函式trace
*/
A. 給出該函式的C版本,使用while迴圈
long trace(tree_ptr tr)
{
long result = 0;
while(tp)
{
result = tp->val;
tp = tp->left;
}
return result;
}
B. 該函式得到最左邊葉子的val值。
/*
***3.70
*/
A. 生成這個函式的C版本
long traverse(tree_ptr tp)
{
if(!tp)
return 0x7fffffffffffffff;
long min_left = traverse(tp ->left);
long min_right = traverse(tp ->right);
long min = min_left < min_right ? min_left : min_right;
min = min < (tp ->val)? min : (tp ->val);
return min;
}
B. 這個函式是求樹中val的最小值,若為空樹,則返回0x7fffffffffffffff;