深入理解计算机系统 习题3.4

题目

假设变量sp和dp被声明为类型

src_t *sp;
dest_t *dp;
这里src_t和dest_t是用typedef声明的数据类型。我们想使用适当的数据传送指令来实现下面的操作
*dp = (dest_t) *sp;

假设sp和dp的值分别存储在寄存器%rdi和%rsi中。对于表中的每个表项,给出实现指令数据传送的两条指令。其中第一条指令应该从内存中读数,做适当的转换,并设置寄存器%rax的适当部分。然后,第二条指令要把%rax的适当部分写到内存。在这两种情况中,寄存器的部分可以使%rax、%eax、%ax或%al,两者可以互不相同。
记住,当执行强制类型转换既涉及大小变化又涉及C语言中符号变化时,操作应先改变大小。

src_t dest_t 指令
long long movq (%rdi),%rax
movq %rax,(%rsi)
char int
char unsigned
unsigned char long
int char
unsigned unsigned char
char short

解答

这个题目本身不难,但是有一点很绕,什么时候用符号扩展传送指令,什么时候用零扩展传送指令

我们一行一行解答

  1. char -> int

    首先char是有符号类型,int也是有符号类型,所以可以肯定需要符号扩展传送指令,由于char是1字节,而int是是双字,所以结果应该为
    movqsbl (%rdi),%eax
    movq %eax,(%rsi)

  2. char -> unsigned

    由于char是有符号类型,unsigned是无符号类型,那么一个有符号一个无符号,我怎么选择扩展指令呢,这是题目的那句,记住,当执行强制类型转换既涉及大小变化又涉及C语言中符号变化时,操作应先改变大小。,所以我们应该先改变大小,改变有符号数的大小还是应该用movqs指令,所以结果与上面相同,
    movqsbl (%rdi),%eax
    movq %eax,(%rsi)

  3. unsigned char -> long

    现在又来了,第一个是无符号数,第二个是有符号数,第一个是一字节,第二个是4字,根据上面的原则应该先改变大小,
    movzbq (%rdi),%rax
    movq %rax,(%rsi),
    等等为什么答案不对,为什么答案是
    movzbl(%rdi),%eax
    movq %rax,(%rsi)
    这个问题网上基本上是没有答案了,我咨询了一下做CPU的同学,movzbl movzbq的效率不同,所以为了优化cpu效率,选择了下面的答案

  4. int -> char

    这个基本上就没有什么要讲的了,int有符号,char有符号,也不需要扩展,
    movl (%rdi),%eax
    movq %al,(%rsi)

  5. unsigned -> unsigned char

    这个答案也很简单,
    movl (%rdi),%eax
    movq %al,(%rsi)

  6. char -> short

    这个也不难了,char是有符号一个字节,short是有符号的一个字,所以答案是
    movsbw (%rdi),%ax
    movw %ax,(%rsi)

总结

所以上面的答案为

src_t dest_t 指令
long long movq (%rdi),%rax
movq %rax,(%rsi)
char int
movqsbl (%rdi),%eax
movq %eax,(%rsi)
char unsigned
movqsbl (%rdi),%eax
movq %eax,(%rsi)
unsigned char long movzbl(%rdi),%eax
movq %rax,(%rsi)
int char movl (%rdi),%eax
movq %al,(%rsi)
unsigned unsigned char movl (%rdi),%eax
movq %al,(%rsi)
char short movsbw (%rdi),%ax
movw %ax,(%rsi)

根据这个例子,我推断,所有强制转换操作,多会优化为效率最优的mov指令,就是说能转成32的肯定不会转成64,这样是为了提高CPU效率。

你可能感兴趣的:(深入理解计算机系统)