函数式编程如何在汇编级别工作?
How does functional programming work at the assembly level?
我目前的项目是为了好玩而制作一个小型编译器。
目前它能够为子程序调用生成代码。
我想用我的语言启用函数式编程。
但是我偶然发现了这个问题,我不知道我在堆栈上传递的函数所在的标签地址(在代码段中)。 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;
这个:
Int main() {
subr2(subr);
}
Int subr2(Int() myfn) {
return myfn();
}
Int subr() {
return 1;
}
会(没有任何优化,并且假设调用约定并不糟糕)变成:
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;
}
然后你会做一些"constant propogation"得到这个:
Int main() {
return subr();
}
Int subr() {
return 1;
}
然后你将 subr()
内联到 main()
得到这个:
Int main() {
return 1;
}
那么你会得到这样的结果:
main:
mov eax,1
ret
请注意,main()
只是一个普通函数。通常有启动代码,链接器将其注入到可执行文件中以初始化事物(标准库、堆等),调用 main()
,然后在 main()
[=39 时执行 exit()
=].
我目前的项目是为了好玩而制作一个小型编译器。 目前它能够为子程序调用生成代码。
我想用我的语言启用函数式编程。 但是我偶然发现了这个问题,我不知道我在堆栈上传递的函数所在的标签地址(在代码段中)。 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;
这个:
Int main() {
subr2(subr);
}
Int subr2(Int() myfn) {
return myfn();
}
Int subr() {
return 1;
}
会(没有任何优化,并且假设调用约定并不糟糕)变成:
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;
}
然后你会做一些"constant propogation"得到这个:
Int main() {
return subr();
}
Int subr() {
return 1;
}
然后你将 subr()
内联到 main()
得到这个:
Int main() {
return 1;
}
那么你会得到这样的结果:
main:
mov eax,1
ret
请注意,main()
只是一个普通函数。通常有启动代码,链接器将其注入到可执行文件中以初始化事物(标准库、堆等),调用 main()
,然后在 main()
[=39 时执行 exit()
=].