CS 752: 高级计算机体系结构 I 作业 2 (2015 年秋季 1 组)

截止日期:9/21 星期一 下午 1 点

您应该独自完成此作业。不接受逾期作业。

目的

本作业的目的是帮助您熟悉 gem5 用于描述指令集的语言。您将浏览 src/arch/x86/isa 中的 ISA 文件,了解指令如何解码并分解为最终执行的微操作。为了更好地理解,您将实现一个缺失的 x87 指令 (FSUBR)。请注意,x87 是 x86 ISA 的子集。此子集最初是为了提供浮点支持而添加的,但现在已不再多用。为了测试指令的实现,您将编写一个小程序,该程序将通过 GCC 的内联汇编功能使用此特定指令。然后将使用 gem5 模拟该程序。

您可能已经知道,x86 指令通常做很多工作。虽然可以单独实现每条指令的功能,但由于许多指令有很多共同的工作,因此通常每条指令都实现为几个较小部分的组合。整个指令通常称为宏操作 (macro-op),而较小的部分称为微操作 (micro-ops)。要在 gem5 中实现一条指令,我们首先向 ISA 解码器提供有关宏操作的信息,然后我们根据微操作提供宏操作的实现。最后,我们实现尚未实现的微操作。我们将对 FSUBR 指令执行这些步骤。我们要实现的 FSUBR 将镜像 FSUB,后者的实现已在 gem5 中可用。

  1. x86 ISA 中有许多编码指令的方法。我们将专注于 x87 子集。您可以从 AMD 提供的 手册 中阅读有关指令编码的更多信息。让我们浏览文件 src/arch/x86/isa/decoder/one_byte_opcodes.isa 以了解 gem5 如何解码 x86 ISA 中的指令。该文件是用一种专为表达指令集而设计的语言编写的。文件的内容最终转换为 C++ switch case。我们首先解码操作码字节的前 5 位。我们可以用 5 位构造 32 种可能的二进制数。switch case 列出了所有可能的情况。

所有 x87 指令均以 0xD8 到 0xDF 范围内的操作码字节开头。因此,最高 5 位始终为 0x1B。对于这种情况,我们包含文件 src/arch/x86/isa/decoder/x87.isa。让我们跳转到那个文件。在这个文件中,我们从解码底部的 3 位开始。您可以查看上述手册中的表 A-15(第 443 页),了解底部三位不同情况所代表的指令。例如,FSUB 和 FSUBR由操作码 0xD8 和 0xDC 表示,即情况 0x0 和 0x4。为了区分同一指令的不同操作码提供的功能,您必须了解指令的 ModRM 字段的含义。在链接的手册中阅读相关内容。在文件 x87.isa 中,您可以检查我们是否在 0x0 和 0x4 的 case 语句中出现了 FSUB。您还可以观察到缺少 FSUBR 的实现。

作为第一步,了解 FSUBR 指令的两种实现之间的区别:一种带有操作码字节 D8h,另一种带有操作码字节 DCh。为此,您应该阅读 手册 中关于 x87 指令的 FSUBR 描述。

  1. 现在,找出 fsubr 在文件 x87.isa 中出现的三个位置。将当前出现的语句替换为与同一文件中为 FSUB 指定的语句类似的语句。通过提及诸如 Inst::FSUBR 之类的内容,您要求使用该指令,而不是默认指令,默认指令只是打印一条警告,指出该指令未实现。

  2. 现在,我们需要根据一些微操作提供 FSUBR 宏操作的实现。我们将再次镜像 FSUB 指令的实现。转到目录 src/arch/x86/isa/insts/x87/arithmetic/。该目录包含根据微操作定义的不同 x87 算术指令。看看 FSUB 指令是如何使用微操作实现的。FSUB1 和 FSUB2 对应于我们之前提到的两种不同的操作码。对于每种类型,我们必须提供三种不同的实现:一种只使用寄存器,一种使用指令中提供的地址从内存中读取一个操作数,最后一种使用指令指针的地址读取操作数。用于这三种实现的微操作应该很容易理解。

gem5 指令解析器的工作方式要求我们定义 FSUBR 指令的所有三种实现。总之,您应该有六个用于 FSUBR 的单独代码块,就像为 FSUB 指定的那样。

  1. 最后,我们需要提供微操作 subfp 的实现。您可以检查该实现是否已在文件 src/arch/x86/isa/micro-ops/fpop.isa 中可用。所以,这一步你不需要做任何事情。

  2. 为 x86 ISA 编译 gem5 以测试您在实现中没有犯任何错误。

gem5 的 ISA 语言还有很多方面我们根本没有讨论过。这些方面中的大多数根本没有记录,需要通过查看相关文件中的代码来弄清楚。

  1. 现在,我们将测试 FSUBR 指令的实现。为此,我们将首先编写一个简短的 C 程序,该程序读取包含两个浮点数的文件,将它们相减并打印输出。为了确保使用 FSUBR 进行减法,我们将使用 GCC 的内联汇编功能显式使用它。

汇编指令使用 ‘asm’ 代码块与其余代码内联编写。此代码块包含两个部分:指令部分和约束部分。在指令部分是包含汇编指令的字符串。GNU C 编译器不检查此字符串的正确性,因此允许任何内容。约束部分指定 GCC 可以或不能对输入和输出操作数做什么,哪些寄存器或内存受指令部分影响。GCC 和其他来源提供了文档。我建议阅读:

你们中的大多数人很可能从未使用过 GCC 的此功能。由于很难理解如何正确处理与汇编指令相关的所有约束,我们提供了一个带有一些解释的实现。

  float subtract(float in1, float in2)
  {
    float ret = 0.0;
    asm ("fsubr %2, %0" : "=&t" (ret) : "%0" (in1), "u" (in2));
    return ret;
  }

该函数的目的是减去两个浮点输入值并返回结果。为此,我们在 ‘asm’ 代码块中使用 FSUBR。在我们的例子中,指令字符串是 “fsubr %2, %0”。之后,我们指定对输出操作数的约束,我们要求它是变量 ret。然后我们指定两个输入操作数:in1 和 in2。字母 ‘t’ 和 ‘u’ 指定 x87 堆栈的顶部和倒数第二个寄存器。

好的,回到我们的主要目的。在一个 C 程序中使用上面提供的函数,该程序将文件名作为输入,从文件中读取两个浮点数,使用 FSUBR 减去这些数字并将结果打印到标准输出。静态编译程序以生成二进制文件。您还可以使用 -S 标志查看编译器生成的汇编代码。

  1. 现在使用 gem5 模拟该程序。您需要弄清楚如何向 gem5 中的程序提供命令行参数。您可以使用 gem5 自带的 configs/examples/se.py 中的示例脚本,也可以使用您在作业 1 中创建的脚本。查看文件 configs/examples/se.py 和文件 configs/common/Options.py 以弄清楚如何提供输入参数。

提交内容

通过发送电子邮件给 David Wood 教授 david@cs.wisc.edu 和 Nilay Vaish nilay@cs.wisc.edu 提交您的作业,主题行:”[CS752 Homework2]”。

  1. 电子邮件应包含提交作业的学生的姓名和 ID 号。以下文件应作为 zip 文件附加到电子邮件中。

  2. 一个名为 fsubr.c 的文件,用于测试 FSUBR 的实现。

  3. 一个补丁文件,包含对 src/arch/x86/isa/insts/x87/arithmetic/src/arch/x86/isa/decoder/x87.isa 所做的更改。 您可以使用 mercurial 命令 hg diff src/arch/x86/isa > /tmp/changes.patch 生成补丁。

  4. 程序的两个不同模拟的 stats.txt 文件:一个使用 TimingSimpleCPU,另一个使用 MinorCPU。

  5. 一份关于您对 gem5 用于实现不同 ISA 的语言/方法的体验的简短报告(200 字)。