Search code examples
assemblyx86compiler-construction

How does functional programming work at the assembly level?


my current project is to make a small compiler, for fun. Currently it is able to generate code for subroutine calls.

I want to enable functional programming in my language. But i stumbled upon the problem, that i do not know the address of the label (in the code segment) where the function is which i am passing on the stack. can nasm calculate it for me? how is this handled in other functional languages?

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

How would this (nonsensical) code be translated? I tried to make the smallest example i could.

The problem i see is that you do not know the offset in the code segment of the label (which gets removed by the assembler?) as a compiler that only compiles down until the assembly level.

How can this be solved without much overhead?

Thanks for your time !

EDIT: @Jester pointed out that you can push labels on the stack in assembly


Solution

  • This:

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

    Would (without any optimization, and assuming the calling convention isn't awful) become:

    main:
      mov eax, subr
      call subr2
      ret
    
    subr2:
        call eax
        ret
    
    subr:
        mov eax,1
        ret
    

    With optimization; first you'd inline the subr2() into main() to get this:

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

    Then you'd do some "constant propogation" to get this:

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

    Then you'd inline the subr() into main() to get this:

    Int main() {
        return 1;
    }
    

    Then you'd end up with this:

    main:
        mov eax,1
        ret
    

    Note that main() is just a normal function. Typically there's start-up code that the linker injects into an executable file that initializes things (standard library, heap, etc), calls main(), and then does exit() if main() returns.