Assembly 函数式编程如何在汇编级别工作?

Assembly 函数式编程如何在汇编级别工作?,assembly,x86,compiler-construction,Assembly,X86,Compiler Construction,我目前的项目是制作一个小型编译器,以获得乐趣。 目前,它能够为子程序调用生成代码 我想用我的语言启用函数式编程。 但我偶然发现了一个问题,即我不知道在堆栈上传递的函数所在的代码段中标签的地址。nasm能帮我算一下吗?在其他函数式语言中如何处理这个问题 Int main(){subr2(subr);} Int subr2(Int() myfn){return myfn();} Int subr(){return 1;} 这个荒谬的代码将如何翻译? 我试着做一个我能做的最小的例子 我看到的问题是,

我目前的项目是制作一个小型编译器,以获得乐趣。 目前,它能够为子程序调用生成代码

我想用我的语言启用函数式编程。 但我偶然发现了一个问题,即我不知道在堆栈上传递的函数所在的代码段中标签的地址。nasm能帮我算一下吗?在其他函数式语言中如何处理这个问题

Int main(){subr2(subr);} Int subr2(Int() myfn){return myfn();} Int subr(){return 1;}
这个荒谬的代码将如何翻译? 我试着做一个我能做的最小的例子

我看到的问题是,您不知道汇编程序删除的标签代码段中的偏移量?作为只向下编译到程序集级别的编译器

如何在没有太多开销的情况下解决这个问题

谢谢你的时间

编辑: @Jester指出,您可以在assembly中推送堆栈上的标签。

@Jester找到了解决方案。 实际上,您可以将标签推送到程序集中的堆栈中

示例代码:

   section  .text

   global _start     ;must be declared for linker (ld)

   _start:              ;tells linker entry point

   push subr ; pushing label on stack
   pop eax ; 
   push _start_continue; together these 2 should make a 'call'
   jmp eax    ;

   _start_continue:

   mov eax,1; sys_exit
   int 0x80;

   subr:

   mov  edx,len     ;message length
   mov  ecx,msg     ;message to write
   mov  ebx,1       ;file descriptor (stdout)
   mov  eax,4       ;system call number (sys_write)
   int  0x80        ;call kernel

   ret;
这:

在没有任何优化的情况下,假设调用约定不可怕,则会变成:

main:
  mov eax, subr
  call subr2
  ret

subr2:
    call eax
    ret

subr:
    mov eax,1
    ret
具有优化能力;首先,将subr2内联到main中,以获得以下结果:

Int main() {
    temp = subr;
    return temp();
}

Int subr() {
    return 1;
}
Int main() {
    return subr();
}

Int subr() {
    return 1;
}
Int main() {
    return 1;
}
然后你要做一些持续的传播来得到这个:

Int main() {
    temp = subr;
    return temp();
}

Int subr() {
    return 1;
}
Int main() {
    return subr();
}

Int subr() {
    return 1;
}
Int main() {
    return 1;
}
然后将subr内联到main中,以获得以下结果:

Int main() {
    temp = subr;
    return temp();
}

Int subr() {
    return 1;
}
Int main() {
    return subr();
}

Int subr() {
    return 1;
}
Int main() {
    return 1;
}
然后你会得到这样的结果:

main:
    mov eax,1
    ret

注意main只是一个普通函数。通常,链接器会将启动代码注入到可执行文件中,该文件初始化标准库、堆等,调用main,然后在main返回时退出。

不确定问题出在哪里?填充偏移量不是你的工作,汇编程序和链接程序会帮你完成。您只使用标签。@Jester我不能使用标签,因为我想传递对堆栈上函数的引用。我不确定你是否可以把标签推到堆栈上?当然可以。你为什么不能?杰斯特你刚刚解决了我的问题!谢谢:虽然您的具体问题已经解决了,但应该注意的是,在函数语言中传递一级函数比传递函数的地址更重要:闭包;不需要先推imm32/pop eax,然后推jmp eax。此外,您还可以使用call subr为您推送回信地址。所有这些都不演示将函数指针传递给另一个函数。您可以通过使用标签来实现这一点;标签地址是链接时间常数,因此链接器可以将它们填充到绝对mov reg、标签或相对jmp标签引用中。或者,如果无法内联(例如,函数指针不是编译时常数),则您可以优化调用eax/ret tailcall到subr2中的jmp eax中。