# 5.2.3生成指令的执行功能

> 译者： [https://sicp.comp.nus.edu.sg/chapters/104](https://sicp.comp.nus.edu.sg/chapters/104)

汇编器调用`make_execution_function`生成指令的执行功能。 就像 [4.1.7](80) 部分的评估器中的分析函数一样，它调度指令的类型以生成适当的执行函数。

```js
function make_execution_function(inst, labels, machine, pc, flag, stack, ops) {
    const x = head(inst);

    return x === "assign"
        ? make_assign(inst, machine, labels, ops, pc)
        : x === "test"
        ? make_test(inst, machine, labels, ops, flag, pc)
        : x === "branch"
        ? make_branch(inst, machine, labels, flag, pc)
        : x === "go_to"
        ? make_goto(inst, machine, labels, pc)
        : x === "save"
        ? make_save(inst, machine, stack, pc)
        : x === "restore"
        ? make_restore(inst, machine, stack, pc)
        : x === "perform"
        ? make_perform(inst, machine, labels, ops, pc)
        : error(inst, "Unknown instruction type: ASSEMBLE");
}
```

对于注册机语言中的每种指令，都有一个生成器，用于构建适当的执行功能。 这些功能的细节决定了寄存器机械语言中各个指令的语法和含义。 我们使用数据抽象来将寄存器机器表达式的详细语法与常规执行机制隔离开，就像我们在 [4.1.2](75) 部分中针对评估器所做的那样，通过使用语法函数来提取和 对指令的各个部分进行分类。

## 分配指令

`make_assign`函数处理分配指令：

```js
function make_assign(inst, machine, labels, operations, pc) {
    const target = get_register(machine, assign_reg_name(inst));
    const value_exp = assign_value_exp(inst);
    const value_fun = is_operation_exp(value_exp)
          ? make_operation_exp(value_exp, machine, labels, operations)
          : make_primitive_exp(head(value_exp), machine, labels);

    function perform_make_assign() {
        set_contents(target, value_fun());
        advance_pc(pc); 
    }

    return perform_make_assign;
}
```

`Make_assign` extracts the target register name (the second element of the instruction) and the value expression (the rest of the list that forms the instruction) from the assign instruction using the selectors

```js
function assign_reg_name(assign_instruction) {
    return head(tail(assign_instruction));
}

function assign_value_exp(assign_instruction) { 
    return tail(tail(assign_instruction));
}
```

用`get_register`查找寄存器名称以生成目标寄存器对象。 如果值是运算结果，则将值表达式传递给`make_operation_exp`，否则传递给`make_primitive_exp`。 这些函数（如下所示）解析值表达式并生成该值的执行函数。 这是一个没有参数的函数，称为`value_fun`，它将在仿真过程中进行评估以产生要分配给寄存器的实际值。 请注意，查找寄存器名称和解析值表达式的工作仅在汇编时执行一次，而不是在每次模拟指令时执行。 节省工作是我们使用执行功能的原因，直接对应于在 [4.1.7](80) 部分的评估程序中通过将程序分析与执行分离而获得的工作节省。

`make_assign`返回的结果是分配指令的执行功能。 调用此函数时（通过机器模型的执行函数），它将目标寄存器的内容设置为通过执行`value_fun`获得的结果。 然后通过运行函数将PC前进到下一条指令

```js
function advance_pc(pc) {
    set_contents(pc, tail(get_contents(pc))); 

}
```

`Advance_pc` is the normal termination for all instructions except branch and `go_to`.

## 测试，分支和`go_to`指令

`Make_test`以类似的方式处理测试指令。 它提取指定要测试条件的表达式，并为其生成执行函数。 在仿真时，将调用该条件的函数，并将结果分配给标志寄存器，然后将pc推进：

```js
function make_test(inst, machine, labels, operations, flag, pc) {
    const condition = test_condition(inst);

    if (is_operation_exp(condition)) {
        const condition_fun = make_operation_exp(condition, machine, labels, operations);

        function perform_make_test() {
            set_contents(flag, condition_fun());
            advance_pc(pc); 
        }

        return perform_make_test; 
    } else {
        error(inst, "Bad TEST instruction: ASSEMBLE");
    }
}

function test_condition(test_instruction) {
    return tail(test_instruction);
}
```

分支指令的执行功能将检查标志寄存器的内容，并将pc的内容设置为分支目标（如果使用了分支），或者只是将pc前进（如果未使用分支）。 请注意，分支指令中指示的目标必须是标签，并且make-branch函数会强制执行此操作。 还要注意，标签是在汇编时查找的，而不是在每次模拟分支指令时查找的。

```js
function make_branch(inst, machine, labels, flag, pc) {
    const dest = branch_dest(inst);

    if (is_label_exp(dest)) {
        const insts = lookup_label(labels, label_exp_label(dest));

        function perform_make_branch() {
            if (get_contents(flag)) {
                set_contents(pc, insts);

            } else {
                advance_pc(pc);
            }
        }

        return perform_make_branch;

    } else {
        error(inst, "Bad BRANCH instruction: ASSEMBLE");
    }
}

function branch_dest(branch_instruction) {
    return head(tail(branch_instruction));
}
```

`go_to`指令与分支相似，不同之处在于可以将目标指定为标签或寄存器，并且无条件检查— pc始终设置为新目标。

```js
function make_goto(inst, machine, labels, pc) {
    const dest = goto_dest(inst);

    if (is_label_exp(dest)) {
        const insts = lookup_label(labels, label_exp_label(dest));
        return () => set_contents(pc, insts);

    } else if (is_register_exp(dest)) {
        const reg = get_register(machine, register_exp_reg(dest));
        return () => set_contents(pc, get_contents(reg));

    } else {
        error(inst, "Bad GOTO instruction: ASSEMBLE");
    }
}

function goto_dest(goto_instruction) {
    return head(tail(goto_instruction));
}
```

## 其他说明

堆栈指令的保存和恢复只需将堆栈与指定的寄存器配合使用，并使pc前进：

```js
function make_save(inst, machine, stack, pc) {
    const reg = get_register(machine, stack_inst_reg_name(inst));

    function perform_make_save() {
        push(stack, get_contents(reg));
        advance_pc(pc);
    }

    return perform_make_save;
}

function make_restore(inst, machine, stack, pc) {
    const reg = get_register(machine, stack_inst_reg_name(inst));

    function perform_make_restore() {
        set_contents(reg, pop(stack));
        advance_pc(pc); 
    }

    return perform_make_restore;
}

function stack_inst_reg_name(stack_instruction) {
    return head(tail(stack_instruction));
}
```

由`make_perform`处理的最终指令类型为要执行的动作生成执行功能。 在仿真时，将执行动作功能并使PC前进。

```js
function make_perform(inst, machine, labels, operations, pc) {
    const action = perform_action(inst);

    if (is_operation_exp(action)) {
        const action_fun = make_operation_exp(action, machine, labels, operations);
        return () => { action_fun(); advance_pc(pc); }

    } else {
        error(inst, "Bad PERFORM instruction: ASSEMBLE");
    }
}

function perform_action(inst) {
    return tail(inst); 
}
```

## 子表达式的执行函数

reg，label或常数表达式的值可能需要分配给寄存器（`make_assign`）或输入操作（以下`make_operation_exp`）。 以下函数生成执行函数，以在仿真期间为这些表达式生成值：

```js
function make_primitive_exp(exp, machine, labels) {
    if (is_constant_exp(exp)) {
        const c = constant_exp_value(exp);
        return () => c;

    } else if (is_label_exp(exp)) {
        const insts = lookup_label(labels, label_exp_label(exp));
        return () => insts;

    } else if (is_register_exp(exp)) {
        const r = get_register(machine, register_exp_reg(exp));
        return () => get_contents(r); 

    } else {
        error(exp, "Unknown expression type: ASSEMBLE");
    }
}
```

reg，label和常量表达式的语法由

```js
function is_register_exp(exp) {
    return is_tagged_list(exp, "reg");
}

function register_exp_reg(exp) {
    return head(tail(exp));
}

function is_constant_exp(exp) {
    return is_tagged_list(exp, "constant");
}

function constant_exp_value(exp) {
    return head(tail(exp));
}

function is_label_exp(exp) {
    return is_tagged_list(exp, "label");
}

function label_exp_label(exp) {
    return head(tail(exp));
}
```

分配，执行和测试指令可能包括将机器操作（由op表达式指定）应用于某些操作数（由reg和常量表达式指定）。 以下函数为&lt;quote&gt;操作表达式&lt;/quote&gt;生成执行函数-包含指令中的操作和操作数表达式的列表：

```js
function make_operation_exp(exp, machine, labels, operations) {
    const op = lookup_prim(operation_exp_op(exp), operations);
    const aprocs = map(e => make_primitive_exp(e, machine, labels),
                       operation_exp_operands(exp));

    function perform_make_operation_exp() {
        return op(map(p => p(), aprocs));
    }

    return perform_make_operation_exp;
}
```

操作表达式的语法由

```js
function is_operation_exp(exp) {
    return is_pair(exp)  is_tagged_list(head(exp), "op");
}

function operation_exp_op(operation_exp) {
    return head(tail(head(operation_exp)));
}

function operation_exp_operands(operation_exp) {
    return tail(operation_exp);
}
```

观察到操作表达式的处理与 [4.1.7](80) 部分的评估器中`analyze_application`函数对函数应用程序的处理非常相似，因为我们生成了一个执行函数 每个操作数。 在仿真时，我们调用操作数函数，并将对操作进行仿真的Scheme函数应用于结果值。 通过在机器的操作表中查找操作名称可以找到仿真功能：

```js
function lookup_prim(symbol, operations) {
    const val = assoc(symbol, operations);

    return val === undefined
        ? error(symbol, "Unknown operation: ASSEMBLE")
        : head(tail(val));
}
```

<exercise>The treatment of machine operations above permits them to operate on labels as well as on constants and the contents of registers. Modify the expression-processing functions to enforce the condition that operations can be used only with registers and constants.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_104_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Design a new syntax for register-machine instructions and modify the simulator to use your new syntax. Can you implement your new syntax without changing any part of the simulator except the syntax functions in this section?<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_104_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>When we introduced save and restore in section <ref name="sec:stack-recursion">[5.1.4](99)</ref>, we didn't specify what would happen if you tried to restore a register that was not the last one saved, as in the sequence

```js
save(y);
save(x);
restore(y);
```

There are several reasonable possibilities for the meaning of restore:

1.  restore（y）将保存在堆栈中的最后一个值放入y中，而不管该值来自哪个寄存器。 这就是我们的模拟器的行为方式。 说明如何利用此行为从 [5.1.4](99)[5.12](99#fig_5.12) ）。
2.  restore（y）将保存在堆栈中的最后一个值放入y中，但前提是该值是从y保存的； 否则，它指示一个错误。 修改模拟器以这种方式运行。 您将必须更改保存以将寄存器名称和值一起放入堆栈。
3.  restore（y）将y中保存的最后一个值放入y中，无论在y之后保存了哪些其他寄存器且未还原其他寄存器。 修改模拟器以这种方式运行。 您将必须将单独的堆栈与每个寄存器关联。 您应该使`initialize_stack`操作初始化所有寄存器堆栈。

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_104_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>The simulator can be used to help determine the data paths required for implementing a machine with a given controller. Extend the assembler to store the following information in the machine model:

*   所有指令的列表，已删除的重复项按指令类型（赋值，`go_to`等）排序；
*   用于保存入口点的寄存器列表（无重复）（这些是`go_to`指令引用的寄存器）；
*   保存或恢复的寄存器列表（无重复）；
*   对于每个寄存器，为其分配源的列表（无重复）（例如，图 [5.11](99#fig_5.11) 的阶乘机器中寄存器val的源是恒定的（1 ）和`op("*", reg("n"), reg("val"))`）。

Extend the message-passing interface to the machine to provide access to this new information. To test your analyzer, define the Fibonacci machine from figure <ref name="fig:fib-machine">[5.12](99#fig_5.12)</ref> and examine the lists you constructed.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_104_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Modify the simulator so that it uses the controller sequence to determine what registers the machine has rather than requiring a list of registers as an argument to `make_machine`. Instead of pre-allocating the registers in `make_machine`, you can allocate them one at a time when they are first seen during assembly of the instructions.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_104_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise>