3.4 访问数据

x86-64的CPU中包含十六个通用寄存器,用于存储64位值(例如整数和指针)。图3.2 画出了这16个寄存器。
在这里插入图片描述
Their names all begin with %r, but otherwise follow multiple different naming conventions, owing to the historical evolution of the instruction set. 8086最初有8个16位寄存器,即图3.2中15处标注的%ax到%sp。每个寄存器都用特定用处,通过它们的名字进行反应。With the extension
to IA32, these registers were expanded to 32-bit registers, labeled %eax through %esp. In the extension to x86-64, the original eight registers were expanded to 64 bits, labeled %rax through %rsp. In addition, eight new registers were added, and these were given labels according to a new naming convention: %r8 through %r15.

As the nested boxes in Figure 3.2 indicate, instructions can operate on data of different sizes stored in the low-order bytes of the 16 registers. Byte-level operations can access the least significant byte, 16-bit operations can access the least significant 2 bytes, 32-bit operations can access the least significant 4 bytes, and 64-bit operations can access entire registers.

In later sections, we will present a number of instructions for copying and generating 1-, 2-, 4-, and 8-byte values. When these instructions have registers as destinations, two conventions arise for what happens to the remaining bytes in the register for instructions that generate less than 8 bytes: Those that generate 1- or 2-byte quantities leave the remaining bytes unchanged. Those that generate 4- byte quantities set the upper 4 bytes of the register to zero. The latter convention was adopted as part of the expansion from IA32 to x86-64.

如图3.2中右方的注释所描述的,不同的寄存器有不同的用处。这其中最特殊的是栈寄存器%rsp,用于指示运行时栈的栈顶。一些指令用于专门读写这个寄存器,其他15个寄存器的用法相对灵活。A small number of instructions make specific use of certain registers. More importantly, a set of standard programming conventions governs how the registers are to be used for managing the stack, passing function arguments, returning values from functions, and storing local and temporary data.
We will cover these conventions in our presentation, especially in Section 3.7, where we describe the implementation of procedures

1 Operand Specifiers

Most instructions have one or more operands specifying the source values to use in performing an operation and the destination location into which to place the result. x86-64 supports a number of operand forms 如图3.3所示:
在这里插入图片描述
Source values可以是给定的常量,或者从寄存器和内存中进行读取。结果可以被存储在寄存器或内存中。Thus, the different operand possibilities can be classified into three types.

  • 第一种类型immediate, is for constant values. 在AT&T汇编代码中,these are written with a ‘$’ followed by an integer using standard C notation—for example, $-577 or $0x1F. 不同的指令允许使用不同范围的immediate values; the assembler will automatically select the most compact way of encoding a value.
  • 第二种类型register, denotes the contents of a register, one of the sixteen 8-, 4-, 2-, or 1-byte low-order portions of the registers for operands having 64, 32, 16, or 8 bits, respectively. In Figure 3.3,we use the notation r a r_a ra to denote an arbitrary register a and indicate its value with the reference R [ r a ] R[r_a] R[ra], viewing the set of registers as an array R indexed by register identifiers.
  • 第三种类型是memory reference, in which we access some
    memory location according to a computed address, often called the effective address. Since we view the memory as a large array of bytes, we use the notation
    Mb[Addr] to denote a reference to the b-byte value stored in memory starting at
    address Addr. To simplify things, we will generally drop the subscript b.

As Figure 3.3 shows, there are many different addressing modes allowing different forms of memory references. The most general form is shown at the bottom
of the table with syntax Imm(rb,ri,s). Such a reference has four components: an
immediate offset Imm, a base register rb, an index register ri, and a scale factor
s, where s must be 1, 2, 4, or 8. Both the base and index must be 64-bit registers.
The effective address is computed as Imm + R[rb] + R[ri]. s. This general form is
often seen when referencing elements of arrays. The other forms are simply special cases of this general form where some of the components are omitted. As we will see, the more complex addressing modes are useful when referencing array
and structure elements.

2 数据移动指令

最常用的指令就是将数据从一个地址拷贝到另一个地址的指令。The generality of the operand notation allows a simple data movement instruction to express a range of possibilities that in many machines would require a number of different instructions. We present a number of different data movement instructions, differing in their source and destination types, what conversions they perform, and other side effects they may have. 在我们的展示中,我们将指令划分为不同的指令类,同一个类中的指令执行相同的操作,但是操作数的数据大小不同。

图3.4列出了最简单的数据移动指令—mov类。这类指令将数据从源地址拷贝到目的地址(不进行任何转换)。这个类包括四个指令:movb, movw, movl, and movq。这四条指令分别对应1、2、4、8字节。

在这里插入图片描述
源操作数指明一个存储在内存或寄存器中的立即数。目的操作数指明一个位置(可以是寄存器也可以是内存地址)。x86-64的一个限制是mov指令两端的操作数不能均为内存地址。将值从一个内存地址拷贝到另一个内存地址需要两条指令——第一条将source value载入寄存器,第二条将寄存器的值写入到destination。

如图3.2所示,这些指令的寄存器操作数可以是这十六个寄存器的任意一个标记部分,但是其大小必须与指令的最后一个字符(‘b’, ‘w’, ‘l’, or ‘q’)相匹配。在大多数情况下,mov指令仅会更新由目标操作数所指定的寄存器或内存地址的特定字节。仅有一个例外, 当movl的目标操作数是一个寄存器时,它还会同时将这个寄存器的高4位字节设定为0。This exception arises from the convention, adopted in x86-64, that any instruction that generates a 32-bit value for a register also sets the high-order portion of the register to 0。

如下的5个mov指令展示了五个可能的source以及destination组合。
在这里插入图片描述
图3.4中最后一个指令用于处理64位立即数。The regular movq instruction can only have immediate source operands that can be represented as 32-bit two’s-complement numbers. This value is then sign extended to produce the 64-bit value for the destination. The movabsq 指令 can have an arbitrary 64-bit immediate value as its source operand and can only have a register as a destination.

图3.5和3.6 document two classes of data movement instructions for use when copying a smaller source value to a larger destination.
在这里插入图片描述
在这里插入图片描述

All of these
instructions copy data from a source, which can be either a register or stored in memory, to a register destination. Instructions in the movz class fill out the
remaining bytes of the destination with zeros, while those in the movs class fill
them out by sign extension, replicating copies of the most significant bit of the
source operand. Observe that each instruction name has size designators as its
final two characters—the first specifying the source size, and the second specifying
the destination size. As can be seen, there are three instructions in each of these
classes, covering all cases of 1- and 2-byte source sizes and 2- and 4-byte destination
sizes, considering only cases where the destination is larger than the source, of
course.

在这里插入图片描述

Note the absence of an explicit instruction to zero-extend a 4-byte source
value to an 8-byte destination in Figure 3.5. Such an instruction would logically
be named movzlq, but this instruction does not exist. Instead, this type of data
movement can be implemented using a movl instruction having a register as the
destination. This technique takes advantage of the property that an instruction
generating a 4-byte value with a register as the destination will fill the upper 4
bytes with zeros. Otherwise, for 64-bit destinations, moving with sign extension is
supported for all three source types, and moving with zero extension is supported
for the two smaller source types.

Figure 3.6 also documents the cltq instruction. This instruction has no
operands—it always uses register %eax as its source and %rax as the destination for
the sign-extended result. It therefore has the exact same effect as the instruction
movslq %eax, %rax, but it has a more compact encoding.

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3 Data Movement Example

图3.7展示了一个使用data movement instructions的数据交换routine(C程序以及GCC生成的反汇编代码)
在这里插入图片描述
当procedure开始执行时,procedure参数xp和y存储在寄存器%rdi以及%rsi中。Instruction 2 then reads x from memory and stores the value in register %rax, a direct implementation of the operation x = *xp in the C program. Later, register %rax will be used to return a value from the function, and so the return value will be x. Instruction 3 writes y to the memory location designated by xp in register %rdi, a direct implementation of the operation *xp = y. This example illustrates how the mov instructions can be used to read from memory to a register (line 2), and to write from a register to memory (line 3).

上面的汇编代码中需要注意两点。首先,我们可以看出C程序中的指针仅仅是地址。间接访问一个指针包括将指针拷贝到寄存器,接下来将这个寄存器作为memory reference。其次,局部变量(例如xare often kept in registers rather than stored in memory locations. 寄存器访问要比内存访问快很多。在这里插入图片描述

4 Pushing and Popping Stack Data

The final two data movement operations are used to push data onto and pop data
from the program stack, as documented in Figure 3.8. As we will see, the stack
plays a vital role in the handling of procedure calls. By way of background, a stack
is a data structure where values can be added or deleted, but only according to
a “last-in, first-out” discipline. We add data to a stack via a push operation and
remove it via a pop operation, with the property that the value popped will always
be the value that was most recently pushed and is still on the stack. A stack can be
implemented as an array, where we always insert and remove elements from one end of the array. This end is called the top of the stack. With x86-64, the program
stack is stored in some region of memory.

如图3.9所示,栈从高地址向低地址增长,因此栈顶的数据有着所有栈元素中最低的地址。(By convention, we draw stacks upside down, with the stack “top” shown at the bottom of the figure.)

在这里插入图片描述
栈指针%rsp存储了栈顶元素的地址。

pushq指令的功能是向栈顶压入数据,popq指令弹出数据。这两个指令仅需一个操作数——the data source for pushing and the data destination for popping.

将一个quad word value压入栈首先需要将栈指针减8然后将数据写入新的栈顶地址。因此,指令pushq %rbp等价于如下的语句:

在这里插入图片描述

except that the pushq instruction is encoded in the machine code as a single byte,
whereas the pair of instructions shown above requires a total of 8 bytes. The first
two columns in Figure 3.9 illustrate the effect of executing the instruction pushq
%rax when %rsp is 0x108 and %rax is 0x123. First %rsp is decremented by 8, giving
0x100, and then 0x123 is stored at memory address 0x100.

Popping a quad word involves reading from the top-of-stack location and
then incrementing the stack pointer by 8. Therefore, the instruction popq %rax
is equivalent to the following pair of instructions:

在这里插入图片描述
The third column of Figure 3.9 illustrates the effect of executing the instruction
popq %edx immediately after executing the pushq. Value 0x123 is read from
memory and written to register %rdx. Register %rsp is incremented back to 0x108.
As shown in the figure, the value 0x123 remains at memory location 0x104 until it
is overwritten (e.g., by another push operation). However, the stack top is always
considered to be the address indicated by %rsp.

Since the stack is contained in the same memory as the program code and
other forms of program data, programs can access arbitrary positions within the
stack using the standard memory addressing methods. For example, assuming the
topmost element of the stack is a quad word, the instruction movq 8(%rsp),%rdx
will copy the second quad word from the stack to register %rdx.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值