Assembly 如何在编译器中实现转到内部(或“;同级”;)作用域?

Assembly 如何在编译器中实现转到内部(或“;同级”;)作用域?,assembly,compiler-construction,goto,Assembly,Compiler Construction,Goto,(注意:这是一个伴奏。我相信这个问题的答案将有助于解决另一个问题。) 在C语言中,goto语句超越范围是完全合法的,如下所示: int-cond(无效); 无效使用(int); void foo() { { int-y; 标签: y=2; 使用(y); } { int z=3; 使用(z); /*跳转到同级作用域:*/if(cond())转到标签; } /*跳转到内部作用域:*/if(cond())转到标签; } 相比之下,类似这样的代码在Algol 60或Algol 68中是不允许的,可能在

(注意:这是一个伴奏。我相信这个问题的答案将有助于解决另一个问题。)

在C语言中,
goto
语句超越范围是完全合法的,如下所示:

int-cond(无效);
无效使用(int);
void foo()
{
{
int-y;
标签:
y=2;
使用(y);
}
{
int z=3;
使用(z);
/*跳转到同级作用域:*/if(cond())转到标签;
}
/*跳转到内部作用域:*/if(cond())转到标签;
}
相比之下,类似这样的代码在Algol 60或Algol 68中是不允许的,可能在其他语言中也是不允许的。(Algol 60.)

这种跳跃应该如何实现?在我的语言中,进入一个块会创建一个新的堆栈框架;同样地处理块激活和过程调用很方便,因为块也返回一个值,就像Algol 68或Common Lisp的
progn
中的封闭子句一样。因此,向块的传输需要为当前层和传输目标之间的任何层创建帧。另一方面,转移到外部作用域很简单,只需要将堆栈展开到某个级别。(当然,同一范围内的转移是微不足道的。)

假设我们正在为传统的基于堆栈的虚拟机生成字节码

以下是一个例子:

begin
  integer x, p;
  p := 10;
  x := 20;
  go to label;
  begin
    integer y;
    y := begin
           integer q;
           label:
             q := x + 2;
             p + q;
         end;
  end;
end;

这应该将
p+q
(即32)分配给
x

我认为内部作用域不应该算作单独的堆栈帧。根据经验,只有当作用域可以作为单独的函数提取而没有副作用时,才应该创建单独的框架。我想不出任何一种无条件跳出堆栈帧的方法是一个好主意。C允许这些跳转,正是因为C块不必是堆栈帧,而且很少有编译器这样对待它们。总的来说,堆栈框架足够大,可以容纳最深的封闭块,因此在函数代码周围跳跃不会出现问题。有一个明显的例外:如果在块中分配了可变长度数组,则需要内部堆栈帧。在本例中,C不允许跳入VLA的范围,正是因为跳入堆栈帧很棘手。显然,该注释是C特定的。但问题的前提也是如此。我认为它在语义上不合理。块可以在循环中(也可以是循环),如果您的语言支持闭包,则每个迭代都有自己的绑定变量。跳入绑定变量的范围可能意味着什么?在这个问题上,在变量被绑定之前向后跳意味着什么?语言必须对其允许的结构的语义有精确的定义。有时,唯一合理的解决方案是不允许构造。(对于第二个问题,将变量初始化视为…@rici是完全合理的,以前我认为省略内部goto语句是一种“不必要的限制”,但现在你已经向我证明了这不是完全正确的。我不允许这样做;标签和goto将像普通Lisp的
标记体
一样受到约束。