《深入理解计算机系统》第三版 第三章家庭作业答案
簡述
相信大部分人在做這些題的時(shí)候,因?yàn)闀袥]有給答案,而去網(wǎng)上找參考答案,比如那些高閱讀量的博客和git。當(dāng)然,我也是這樣,但他們的答案中還是有好多錯(cuò)誤,比如3.59他們幾乎都沒講清楚提示中的公式怎么來的,3.60中對(duì)移位操作中對(duì)%cl的讀取,等等。。希望讀者們在閱讀這些文章時(shí),要帶著自己的思想和疑問去理解,而不是一味地覺得答案就肯定是對(duì)的,當(dāng)然,本文有任何錯(cuò)誤,也歡迎各位指出。
3.58
long decode2(long x,long y,long z) {y = y - z;x = x * y;y <<= 63;y >>= 63;return y ^ x; }y先左移63位,再右移63位,如果之前y是奇數(shù),那么y的二進(jìn)制全是1;y是偶數(shù),那么y的二進(jìn)制全是0.
3.59
首先講解一下,提示里的公式x=264?xh+xlx=2^{64}*x_h+x_lx=264?xh?+xl?,之所以可以這么寫是因?yàn)?strong>符號(hào)拓展,以4位二進(jìn)制int為例:
1111的補(bǔ)碼數(shù),為-1.將其進(jìn)行符號(hào)拓展后為1111 1111,其值也為-1,但這里可以將1111 1111寫為高位1111的補(bǔ)碼數(shù) * 242^424 + 低位1111的無符號(hào)數(shù):
即-1 * 242^424 + 15 = -1.
原理:%rdx和%rax的二進(jìn)制連起來表示這個(gè)數(shù),既然連起來了,符號(hào)位就跑到了%rdx的最高位了,除符號(hào)位權(quán)值為負(fù)外,其余位的權(quán)值均為正。所以,高位寄存器%rdx當(dāng)做補(bǔ)碼數(shù),低位寄存器%rax當(dāng)做無符號(hào)數(shù)。因?yàn)榉?hào)位現(xiàn)在在高位寄存器那兒呢,所以高位寄存器當(dāng)做補(bǔ)碼數(shù)了;而低位寄存器的每一位的權(quán)值現(xiàn)在都是正的了,所以低位寄存器要當(dāng)做無符號(hào)數(shù)。
所以xlx_lxl?為T2U(x)T2U(x)T2U(x)即x的二進(jìn)制表示作為無符號(hào)數(shù)。xlx_lxl?與xxx有相同的位級(jí)表示。
xhx_hxh?,當(dāng)原數(shù)符號(hào)位為1,64位二進(jìn)制位上全為1,其值為-1;當(dāng)原數(shù)符號(hào)位為0時(shí),64位二進(jìn)制位上全為0,其值為0。
再講解一下本文用到的數(shù)學(xué)公式:有x=264?xh+xlx=2^{64}*x_h+x_lx=264?xh?+xl?和y=264?yh+yly=2^{64}*y_h+y_ly=264?yh?+yl?,那么有:
x?y=(264?xh+xl)?(264?yh+yl)x*y=(2^{64}*x_h+x_l)*(2^{64}*y_h+y_l)x?y=(264?xh?+xl?)?(264?yh?+yl?)
=xhyh2128+(xhyl+xlyh)264+xlyl=x_hy_h2^{128}+(x_hy_l+x_ly_h)2^{64}+x_ly_l=xh?yh?2128+(xh?yl?+xl?yh?)264+xl?yl?
但這個(gè)公式其實(shí)并不陌生,它與2.3.5補(bǔ)碼乘法(P67) 里面的公式2.18有異曲同工之妙,另外理解本題需要閱讀此節(jié)。
第一項(xiàng)xhyh2128x_hy_h2^{128}xh?yh?2128肯定溢出,雙寄存器都裝不下,截?cái)嗪笕珵?,忽略。
關(guān)于第二項(xiàng),(xhyl+xlyh)(x_hy_l+x_ly_h)(xh?yl?+xl?yh?)這個(gè)數(shù)值是需要放在高位寄存器中的(因?yàn)檫@一項(xiàng)乘以的數(shù)為2642^{64}264),假設(shè)xhylx_hy_lxh?yl?分別是-1和UMAX,僅僅是它倆的乘積都會(huì)使得高位寄存器溢出(考慮補(bǔ)碼數(shù)和無符號(hào)數(shù)的表示范圍就能想到),如果溢出,放入高位寄存器時(shí)會(huì)自行截?cái)唷?/p>
第三項(xiàng)xlylx_ly_lxl?yl?,直接使用雙寄存器來保存結(jié)果。
下面開始講解匯編代碼:
第一個(gè)參數(shù)*dest在%rdi中,第二個(gè)參數(shù)x在%rsi中,第三個(gè)參數(shù)y在%rdx中。
重點(diǎn)講一下6-8行,發(fā)現(xiàn)這里代碼計(jì)算的是(xhy+xyh)(x_hy+xy_h)(xh?y+xyh?),而數(shù)學(xué)公式里面要求是(xhyl+xlyh)(x_hy_l+x_ly_h)(xh?yl?+xl?yh?),之所以匯編要如此計(jì)算,是利用了相同的位級(jí)向量,無論用無符號(hào)數(shù)乘法還是補(bǔ)碼乘法,其結(jié)果的截?cái)嗟奈患?jí)表示肯定是一樣的。
但這里有點(diǎn)不一樣,給定x?\vec xx和y?\vec yy?兩個(gè)位級(jí)向量,固定將x?\vec xx看作補(bǔ)碼數(shù),而將y?\vec yy?分別看作補(bǔ)碼數(shù)和無符號(hào)數(shù),那么x與y的兩種乘積的截?cái)嗟奈患?jí)表示是一樣的。接下來用個(gè)小例子來證明該結(jié)論。(注意代碼是將乘積的截?cái)嗟奈患?jí)表示看作補(bǔ)碼數(shù)的)
假設(shè)整數(shù)類型為3位,x?\vec xx和y?\vec yy?分別為111和111,x的值為-1,而y的值分別為-1,7.
首先看-1 * -1 = 1,那么位級(jí)表示為001
再看-1 * 7 = -7,那么位級(jí)表示為1001,截?cái)嗪鬄?01
證畢。
考慮下第9行是否會(huì)溢出,無符號(hào)數(shù)最大為264?12^{64}-1264?1,所以兩個(gè)無符號(hào)數(shù)的乘積最大為(264?1)2(2^{64}-1)^2(264?1)2等于2128+1?2652^{128}+1-2^{65}2128+1?265.而128位的補(bǔ)碼數(shù)的最大范圍為2127?12^{127}-12127?1.
而(2128+1?265)?(2127?1)(2^{128}+1-2^{65})-(2^{127}-1)(2128+1?265)?(2127?1) = 2127+2?2652^{127}+2-2^{65}2127+2?265 > 0,所以可能溢出。
3.60
long loop(long x,int n) {long result = 0;long mask;for(mask = 1;maks != 0;mask=mask << (n % 64))//如果這里不能保證是正余數(shù)(0-63)的話,就用下面的寫法{result |= (x & mask);}return result; }這里難點(diǎn)主要在于salq %cl, %rdx這里的移位量到底是多少,根據(jù)移位操作中的解釋,因?yàn)楸灰莆粩?shù)為64位二進(jìn)制(26=642^6 = 6426=64),所以只看%cl的低6位,或者循環(huán)的執(zhí)行可以改為mask=mask<<(n & 0x3F)
3.61
首先看上圖c語句與其匯編語句的對(duì)應(yīng)(3.6.6節(jié)),題目要求新函數(shù)對(duì)應(yīng)的匯編代碼也會(huì)用到條件傳送,即要求有三目表達(dá)式。對(duì)于第4行,看起來可能是多余的,但3.6.6節(jié)講到條件傳送中,第一個(gè)操作數(shù)可以是源寄存器或者內(nèi)存地址,所以立即數(shù)是不可以,所以這里多了一步。
如果函數(shù)改成long cread_alt(long *xp) { return (!xp ? 0 : *xp); },那么匯編代碼可能是:
cread_alt:movl $0, %eaxtestq %rdi, %rdicmovne (%rdi), %rax #直接傳送ret當(dāng)然也可以改成如下:
long cread_alt(long *xp) {long t = 0;long *p = xp ? xp : &t; //得到xp指針或者0的地址,這句轉(zhuǎn)換為條件傳送語句后,也不會(huì)可能去讀取空指針return *p; //解引用,現(xiàn)在讀取指針指向值肯定不會(huì)出錯(cuò) }為了驗(yàn)證匯編代碼,本人用MinGW進(jìn)行了編譯,使用命令gcc -Og -S test.c,c文件內(nèi)容為long cread(long *xp) { return (xp ? *xp : 0); },發(fā)現(xiàn)不管優(yōu)化程度是多少,生成匯編基本都是(發(fā)現(xiàn)并沒有使用條件傳送,且沒怎么看懂):
LFB0:movl 4(%esp), %eax #得到了xp指針testl %eax, %eax je L3movl (%eax), %eax #指針不為空,讀取指針指向的值ret L3:xorl %eax, %eaxret3.62
鍛煉你的反向工程能力。注意有的語句可以簡化,不用非得照著匯編原封不動(dòng)翻譯。
long switch3(long *p1, long *p2, mode_t action) {long result = 0;switch(action) {case MODE_A:result = *p2;*p2 = *p1;break;case MODE_B:*p1 = *p1 + *p2;result = *p1;break;case MODE_C:*p1 = 59;result = *p2;break;case MODE_D:*p1 = *p2;result = 27;break;case MODE_E:result = 27;break;default:result = 12;break;}return result; }3.63
0000000000400590<switch_prob>:400590: 48 83 ee 3c sub $0x3c, %rsi #n -= 60,說明最后n的實(shí)際數(shù)要加60400594: 48 83 fe 05 cmp $0x5, %rsi #比較n > 5400598: 77 29 ja 4005c3 <switch_prob+0x33> #如果n > 5那么跳轉(zhuǎn)到default# 所以n <= 5的情況就只有交給跳轉(zhuǎn)表處理40059a: ff 24 f5 f8 06 40 00 jmpq *0x4006f8(,%rsi,8) #間接跳轉(zhuǎn)到0x4006f8 + 8*n# 跳到跳轉(zhuǎn)表對(duì)應(yīng)的位置,從跳轉(zhuǎn)表來看,n的取值只能是0-5,因?yàn)橹挥?個(gè)八字節(jié)# 0和2會(huì)跳到這個(gè)位置4005a1: 48 8d 04 fd 00 00 00 lea 0x0(,%rdi,8),%rax4005a8: 00400593: c3 retq# 3會(huì)跳到這個(gè)位置4005aa: 48 89 f8 mov %rdi, %rax4005ad: 48 c1 f8 03 sar $0x3, %rax4005b1: c3 retq# 4會(huì)跳到這個(gè)位置4005b2: 48 89 f8 mov %rdi, %rax4005b5: 48 c1 e0 04 shl $0x4, %rax4005b9: 48 29 f8 sub %rdi, %rax4005bc: 48 89 c7 mov %rax, %rdi# 5會(huì)跳到這個(gè)位置4005bf: 48 0f af ff imul %rdi, %rdi# 大于5和1會(huì)跳到這個(gè)位置4005c3: 48 8d 47 4b lea 0x4b(%rdi), %rax4005c7: c3 retq而且從匯編代碼來看,如果n的值是<60,那么n-60<0,那么匯編代碼就會(huì)執(zhí)行到j(luò)mpq *0x4006f8(,%rsi,8),本來應(yīng)該跳轉(zhuǎn)到這6個(gè)八字節(jié),但最終間接跳轉(zhuǎn)到非法的八字節(jié)。但也許此題重點(diǎn)不在于此,應(yīng)假設(shè)n>=60.
long switch_prob(long x, long n){long result = x;switch(n):{case 60:case 62:result = x * 8;break;case 63:result = result >> 3;break;case 64:result = (result << 4) - x;x = result;case 65:x = x * x;//注意64,65后面沒有breakdefault:result = x + 75;} }3.64
假設(shè)有數(shù)組D[S][T]D[S][T]D[S][T],等式3.1為D+L(T?i+j)D+L(T \cdot i+j)D+L(T?i+j),這里T明顯為列數(shù),更加深入的說,代表第一維度中每個(gè)維度的元素個(gè)數(shù)。
假設(shè)有數(shù)組D[R][S][T]D[R][S][T]D[R][S][T],等式3.1應(yīng)為D+L(ST?i+T?j+k)D+L(ST \cdot i+ T \cdot j + k)D+L(ST?i+T?j+k),ST為第一維度中每個(gè)維度的元素個(gè)數(shù)。
則有:
S * T = 65 T = 13 S * T * R * 8 = 3640得到:R = 7 ; S = 5 ; T = 13
3.65
.L6:movq (%rdx), %rcx # t1 = A[i][j]movq (%rax), %rsi # t2 = A[j][i]movq %rsi, (%rdx) # A[i][j] = t2movq %rcx, (%rax) # A[j][i] = t1addq $8, %rdx # A[i][j] -> A[i][j+1]addq $120, %rax # A[j][i] -> A[j+1][i], 120 == 8*Mcmpq %rdi, %rax jne .L6 # if A[j][i] != A[M][M]A.從第6行就能看出來%rdx是A[i][j],因?yàn)槊看沃患?,即一個(gè)元素大小。
B.因?yàn)榧拇嫫?rdx是A[i][j],所以另一個(gè)寄存器%rax是A[j][i]。
C.根據(jù)公式,120 == 8*M,所以M為15.
3.66
sum_col:leaq 1(, %rdi, 4), %r8 # %r8 = 4 * n + 1leaq (%rdi, %rdi, 2), %rax # result = 3 * nmovq %rax, %rdi # %rdi = 3 * ntestq %rax, %raxjle .L4 # if %rax <= 0, goto L4salq $3, %r8 # %r8 = 8 * (4 * n + 1)leaq (%rsi, %rdx, 8), %rcx # %rcx = A[0][j]的地址movl $0, %eax # result = 0movl $0, %edx # i = 0 .L3:addq (%rcx), %rax # result += A[i][j]addq $1, %rdx # i += 1addq %r8, %rcx # 這里每次+8*(4n+1),說明每一行有4n+1個(gè),因此NC(n)為4*n+1cmpq %rdi, %rdx jne .L3 # 當(dāng)%rdx等于3*n才循環(huán)結(jié)束,所以可以說明一共有3n行,因此NR(n)為3*nrep; ret .L4:movl $0, %eaxret所以有NR(n) = 3 * n; NC(n) = 4 * n + 1;
3.67
# strB process(strA s) # s in %rdi process:movq %rdi, %rax #第一個(gè)參數(shù)作為返回值,即要返回的結(jié)構(gòu)體的開始地址movq 24(%rsp), %rdx #棧指針開始的第4個(gè)八字節(jié)的內(nèi)容,存入%rdx,內(nèi)容為結(jié)構(gòu)體A的第二個(gè)成員:指針pmovq (%rdx), %rdx #讀取指針p指向的long型對(duì)象,再存入%rdxmovq 16(%rsp), %rcx #棧指針開始的第3個(gè)八字節(jié)的內(nèi)容,內(nèi)容為結(jié)構(gòu)體A的成員數(shù)組的第2個(gè)元素:D[1]movq %rcx, (%rdi) #將D[1],存入返回結(jié)構(gòu)體的第1個(gè)八字節(jié)movq 8(%rsp), %rcx #棧指針開始的第2個(gè)八字節(jié)的內(nèi)容,內(nèi)容為結(jié)構(gòu)體A的成員數(shù)組的第1個(gè)元素:D[0]movq %rcx, 8(%rdi) #將D[0],存入返回結(jié)構(gòu)體的第2個(gè)八字節(jié)movq %rdx, 16(%rdi) #將long型對(duì)象,存入返回結(jié)構(gòu)體的第3個(gè)八字節(jié)#棧指針開始的第1個(gè)八字節(jié),這里并沒有使用,因?yàn)榇娴氖钦{(diào)用后的返回地址ret # long eval(long x, long y, long z) # x in %rdi, y in %rsi, z in %rdx eval:subq $104, %rsp #為棧分配了13*8字節(jié)空間,即13個(gè)八字節(jié)movq %rdx, 24(%rsp) #z存入棧指針開始的第4個(gè)八字節(jié)leaq 24(%rsp), %rax #棧指針開始的第4個(gè)八字節(jié)中的第一個(gè)字節(jié)的地址,存入%rax,作為結(jié)構(gòu)體A的指針成員pmovq %rdi, (%rsp) #x存入棧指針開始的第1個(gè)八字節(jié)movq %rsi, 8(%rsp) #y存入棧指針開始的第2個(gè)八字節(jié)movq %rax, 16(%rsp) #p存入棧指針開始的第3個(gè)八字節(jié)leaq 64(%rsp), %rdi #棧指針開始的第9個(gè)八字節(jié),的開始地址call process #這里有隱藏操作,分配八字節(jié)棧空間,存入返回地址,即下一行代碼地址movq 72(%rsp), %rax #這三行匯編執(zhí)行加法addq 64(%rsp), %raxaddq 80(%rsp), %raxaddq $104, %rsp #回收棧空間retA.
注意此圖中,從下往上是地址增加方向。
B.
傳遞了%rsp+64,即棧指針開始的第9個(gè)八字節(jié),的開始地址。
C.
因?yàn)榻Y(jié)構(gòu)參數(shù)s存在棧空間里,所以用%rsp+偏移量來訪問的。
D.
r的空間是分配在棧空間里,所以也是%rsp+偏移量來設(shè)置的。
E.
F.
結(jié)構(gòu)體作為參數(shù)傳入和返回時(shí),都是以指針來傳遞。
3.68
從匯編movslq 8(%rsi), %rax中,可以看出結(jié)構(gòu)體str2中int t是從第2個(gè)八字節(jié)開始:
左邊為最大情況,右邊為最小情況。在最小情況中,如果數(shù)組再少一個(gè)元素,即數(shù)組大小由5字節(jié)變成4字節(jié),那么int變量就會(huì)跑到第1個(gè)八字節(jié)中去了。
所以5<=B<=8.
從匯編addq 32(%rsi), %rax中,可以看出結(jié)構(gòu)體str2中l(wèi)ong u是從第5個(gè)八字節(jié)開始:
左邊為最大情況,右邊為最小情況。
所以7<=A<=10.
從匯編movq %rax, 184(%rdi)中,184既可能是最大情況,也可能是8字節(jié)補(bǔ)齊情況。
所以184-8<A*B*4<=184.
答案唯一解為:A=9; B=5;。
3.69
從c語句ap->x[ap->idx] = n;知道a_struct的兩個(gè)成員分別是數(shù)組和整數(shù)類型。
# void test(long i, b_struct *bp) # i in %rdi, bp in %rsi test:mov 0x120(%rsi), %ecx # bp+288 匹配bp->lastadd (%rsi), %ecx # bp->first + bp->lastlea (%rdi,%rdi,4), %rax # %rax = i*5lea (%rsi,%rax,8), %rax # %rax = bp+i*40# ap = &bp->a[i] = bp+8+i*40, +8意味著從bp開始的第1個(gè)八字節(jié)里面只有int,且a_struct大小必為8字節(jié)或更大,若為4字節(jié),就不是+8而是+4了# 因?yàn)槭莍*40,所以a_struct大小為40字節(jié)# 此句很明顯取出了一個(gè)數(shù),再結(jié)合倒數(shù)第二條指令mov %rcx, 0x10(%rax,%rdx,8),所以%rdx為ap->idx# 而且在結(jié)構(gòu)體a_struct中,第一個(gè)成員為整數(shù)類型的idxmov 0x8(%rax), %rdxmovslq %ecx, %rcx # mov時(shí)符號(hào)拓展成4字8字節(jié)# 先看0x10(%rax,)部分,是bp+16+i*40,比ap多了8字節(jié),這里是a_struct數(shù)組成員的開始地址,也說明了idx大小為8字節(jié)# 再看(,%rdx,8)部分,是idx*8,所以說明了a_struct數(shù)組成員的大小為8字節(jié)# 合起來看就是bp+8+i*40+8 +idx*8,第二個(gè)+8跳過了a_struct的整數(shù)成員idxmov %rcx, 0x10(%rax,%rdx,8)# a_struct大小為40字節(jié),第一個(gè)成員idx為long,8字節(jié),還剩32字節(jié)# 第二個(gè)成員是long型數(shù)組,按照剩余字節(jié),數(shù)組大小為4retqA.
因?yàn)?*40 + 8 = 288 = 0x120,所以CNT=7,要推出CNT必須先推理出a_struct的大小。
B.
3.70
proc:movq 8(%rdi), %rax #偏移量為8,存的是up->e1.y或者是up->e2.nextmovq (%rax), %rdx #用作內(nèi)存引用,所以上面是up->e2.next,取出*(up->e2.next)的偏移量為0的內(nèi)容,也有兩種情況movq (%rdx), %rdx #用作內(nèi)存引用,所以上面是*(up->e2.next).e1.p,取出*( *(up->e2.next).e1.p )的內(nèi)容,為long型subq 8(%rax), %rdx #取出*(up->e2.next)的偏移量為8的內(nèi)容,因?yàn)橐鳛闇p數(shù),所以減數(shù)是*(up->e2.next).e1.ymovq %rdx, (%rdi) #將減法之差存入,up->e2.xretA.
e1.p 0 e1.y 8 e2.x 0 e2.next 8B.
16
C.
up->e2.x = *( *(up->e2.next).e1.p ) - *(up->e2.next).e1.y,具體看注釋。
3.71
這道題主要需要了解fgets函數(shù)(char * fgets ( char * str, int num, FILE * stream );)。下面將fgets函數(shù)的api文檔進(jìn)行翻譯。
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
A terminating null character is automatically appended after the characters copied to str.
Notice that fgets is quite different from gets: not only fgets accepts a stream argument, but also allows to specify the maximum size of str and includes in the string any ending newline character.
從流中讀取字符,并將它們作為C string存儲(chǔ)進(jìn)str參數(shù)中,直到num-1個(gè)字符已經(jīng)被讀取,或者是到達(dá)新行或者EOF,這三個(gè)條件誰先到達(dá)都會(huì)使得讀取停止。
換行字符使得fgets函數(shù)停止讀取,不過換行符也會(huì)被當(dāng)做一個(gè)合法字符來讀取。
一個(gè)空字符將會(huì)自動(dòng)加在讀取的字符后,然后再復(fù)制給str。
On success, the function returns str.
If the end-of-file is encountered while attempting to read a character, the eof indicator is set (feof). If this happens before any characters could be read, the pointer returned is a null pointer (and the contents of str remain unchanged).
If a read error occurs, the error indicator (ferror) is set and a null pointer is also returned (but the contents pointed by str may have changed).
當(dāng)函數(shù)執(zhí)行成功,返回str。
當(dāng)讀取字符時(shí)遇到一個(gè)EOF時(shí),EOF標(biāo)識(shí)符被設(shè)置。如果在任何字符都沒有進(jìn)行讀取時(shí),就發(fā)生了這樣的事,那么返回空指針(str指向的文本保持不變)。如果發(fā)生了讀取錯(cuò)誤,那么error標(biāo)識(shí)符被設(shè)置,也返回空指針(但str指向的文本可能會(huì)改變)。
1.根據(jù)翻譯得知,使用fgets函數(shù)便可以保證“當(dāng)輸入字符超過緩沖區(qū)空間大小時(shí),也能正常工作”。
2.關(guān)于“你的代碼還應(yīng)該檢查錯(cuò)誤條件,在遇到錯(cuò)誤條件時(shí)返回”這點(diǎn),其實(shí)判斷條件if (p == NULL)太籠統(tǒng)了,可以通過ferror函數(shù)(int ferror ( FILE * stream );)來判斷(stdin的類型是FILE *),當(dāng)讀取出錯(cuò)時(shí),調(diào)用ferror函數(shù)返回非0值,上述代碼應(yīng)寫成if ( (p == NULL) & (ferror(stdin) != 0) )。
3.72
此題與練習(xí)題3.49幾乎一模一樣,具體講解請(qǐng)看此篇博客。
注意c語句long **p = alloca(n * sizeof(long*));,p的類型為long **即long指針的指針,可以這么理解,分配long型數(shù)組時(shí),返回long *指針;當(dāng)分配long *型數(shù)組時(shí),返回long **指針。
第5行%rax存的是30+8n。
第6行分為兩種情況:(and -16解釋為向下取整到16的倍數(shù))
a.當(dāng)為偶數(shù)時(shí),分成8n和30兩部分,8n and -16得8n,30 and -16得16.
b.當(dāng)為奇數(shù)時(shí),分成8(n-1)和38兩部分,8(n-1) and -16得8(n-1),38 and -16得32.
第8行加上偏置15(24?12^4-124?1),第9行 and -16,執(zhí)行完這兩行,就相當(dāng)于向上取整到16的倍數(shù)。注意在練習(xí)題3.49中,andq $-16, %r8這句是通過兩句匯編來實(shí)現(xiàn)的(先右移再左移,而本題是直接and -16)。
A.
s2=s1?((8?n+30)&0xfffffff0)s_2 = s_1 - ((8 * n + 30) \& 0xfffffff0)s2?=s1??((8?n+30)&0xfffffff0),根據(jù)上面的分析:
當(dāng)n為偶數(shù)時(shí),s2=s1?(8?n+16)s_2 = s_1 - (8 * n + 16)s2?=s1??(8?n+16)
當(dāng)n為奇數(shù)時(shí),s2=s1?(8?n+24)s_2 = s_1 - (8 * n + 24)s2?=s1??(8?n+24)
B.
p=(s2+15)&0xfffffff0p = (s_2 + 15) \& 0xfffffff0p=(s2?+15)&0xfffffff0
C.
大方向分為,當(dāng)s2s_2s2?為16的倍數(shù)(這種情況p數(shù)組就直接從s2s_2s2?開始分配),和s2s_2s2?不為16的倍數(shù)(這種情況p數(shù)組還需要向地址增加方向滑動(dòng)1-15個(gè)字節(jié))。
1.因?yàn)閑1和e2是用來滑動(dòng)的,所以當(dāng)e2為0,即s2s_2s2?為16的倍數(shù)時(shí),當(dāng)e1就會(huì)最大。再看當(dāng)n為奇數(shù)時(shí),分配數(shù)組空間為8 * n + 24,多出來24字節(jié)空間作為e1。e1最大為24,此時(shí)s2s_2s2?為16的倍數(shù),且n為奇數(shù)。
2.當(dāng)s2s_2s2?不為16的倍數(shù)時(shí),p數(shù)組空間需要滑動(dòng)來16對(duì)齊,當(dāng)s2s_2s2?%16=1時(shí),向地址增加方向滑動(dòng)15個(gè)字節(jié),此時(shí)達(dá)到最大滑動(dòng)距離了,即e2=15。而e1=可滑動(dòng)空間-e2,當(dāng)n為偶數(shù)時(shí),滑動(dòng)空間為16字節(jié),則e1=可滑動(dòng)空間-e2=16-15=1。e1最小為1,此時(shí)s2s_2s2?%16=1,且n為偶數(shù)。
D.
p數(shù)組空間是16對(duì)齊的。
s2s_2s2?是容下8 * n字節(jié)的最小的16的倍數(shù)再加16。
3.73
原書中的匯編即圖3-51中的匯編,確實(shí)很亂,這樣改完之后清爽多了。
find_range:vxorps %xmm1, %xmm1, %xmm1vucomiss %xmm1, %xmm0jp .L1ja .L2jb .L3je .L4.L2:movl $2, %eaxret.L3:movl $0, %eaxret.L4:movl $1, %eaxret.L1:movl $3, %eaxrep; ret3.74
這樣的話,連cmovp都不需要用了。
find_range:vxorps %xmm1, %xmm1, %xmm1movq $0, %r8movq $1, %r9movq $2, %r10movq $3, %raxvucomiss %xmm1, %xmm0cmovb %r8, %raxcmove %r9, %raxcmova %r10, %raxret
可以看出在比較大于小于時(shí),有兩套指令可以用,但因?yàn)楸容^浮點(diǎn)數(shù)用到的標(biāo)志位為CF和ZF,所以再看上表,則應(yīng)該使用下面這套指令。
3.75
A.
| 1 | %xmm0 | %xmm1 |
| 2 | %xmm2 | %xmm3 |
| 3 | %xmm4 | %xmm5 |
| n | %xmm(2n-2) | %xmm(2n-1) |
B.
imag部分返回值在%xmm1, real部分返回值在%xmm0.
總結(jié)
以上是生活随笔為你收集整理的《深入理解计算机系统》第三版 第三章家庭作业答案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈机器学习中的过拟合
- 下一篇: 我要换博客啦~Github+Hexo~W