代码之家  ›  专栏  ›  技术社区  ›  Mason Wheeler

跳出块时有没有安全的方法来清理基于堆栈的代码?

  •  1
  • Mason Wheeler  · 技术社区  · 16 年前

    我一直在努力 Issue 14 在PascalScript脚本引擎中,使用Goto命令跳出Case块会产生编译器错误,即使这是完全有效的(如果难看的话)对象Pascal代码也是如此。

    在编译器中的ProcessCase例程调用hasvalidjumps,它扫描Case块之外的任何goto,如果找到,则给出编译器错误。如果我对签出进行注释,它编译得很好,但最终在运行时崩溃。字节码的反汇编说明了原因。我用原始脚本代码注释了它:

    [TYPES]
    <SNIPPED>
    [VARS]
    Var [0]: 27 Class TFORM
    Var [1]: 28 Class TAPPLICATION
    Var [2]: 11 S32 //i: integer
    [PROCS]
    Proc [0] Export: !MAIN -1
    {begin}
     [0] ASSIGN GlobalVar[2], [1]
    { i := 1;}
     [15] PUSHTYPE 11(S32) // 1
     [20] ASSIGN Base[1], GlobalVar[2]
    { case i of}
     [31] PUSHTYPE 25(U8) // 2
    {   0:}
     [36] COMPARE into Base[2]: [0] = Base[1]
     [57] COND_NOT_GOTO currpos + 5 Base[2] [72]
    {   end;}
     [67] GOTO currpos + 41 [113]
    {   1:}
     [72] COMPARE into Base[2]: [1] = Base[1]
     [93] COND_NOT_GOTO currpos + 10 Base[2] [113]
    {     goto L1;}
     [103] GOTO currpos + 8 [116]
    {   end;}
     [108] GOTO currpos + 0 [113]
    { end; //<-- case}
     [113] POP // 1
     [114] POP // 0
    { Exit;}
     [115] RET
    {L1:
     Writeln('Label L1');}
     [116] PUSHTYPE 17(WideString) // 1
     [121] ASSIGN Base[1], ['????????']
     [144] CALL 1
    {end.}
     [149] POP // 0
     [150] RET
    Proc [1]: External Decl: \00\00 WRITELN
    

    有人知道如何修补codegen以便它能正确清理堆栈吗?

    3 回复  |  直到 16 年前
        1
  •  3
  •   Marco van de Voort    16 年前

    经典帕斯卡语中的goto规则是:

    • 只允许跳出块(从树的“同一”分支上的较高嵌套级别向下跳转)

    后者从未得到博尔兰衍生的帕斯卡语的支持,但第一种仍然有效。

    所以你需要像Martin说的那样生成退出的代码,但可能是针对多个块级别的,所以你不能为每个goto生成一个可能的代码,而是必须生成代码(以精确地退出所需块的数量)。

    典型的测试模式是使用goto退出多个嵌套的ifs(可能在一个循环中),因为这是一个经典的微优化,至少在D7之前更快。

    请记住,if求值和分支的begin..end块可能生成了需要清理的temp。

    ----------稍后添加

    我认为codegenerator需要一种方法来遍历goto和它的端点之间的作用域,同时为块生成相关的退出代码。这样一来,修复程序就适用于一般情况,而不仅仅是本例。 既然你只能跳出范围,而不能跳进范围,那就没那么难了。

    LGOTO1代码: //退出代码第一块 流行音乐 流行音乐 //退出代码第一块 流行音乐B 去真正的目的地

        2
  •  1
  •   Martin Konicek Phil Windley    16 年前

    简单的解决方案是:

    当生成GOTO for GOTO语句时,在GOTO前面加上与RET之前相同的清除代码。

        3
  •  1
  •   skamradt    16 年前