Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding GHC assembly output

When compiling a haskell source file using the -S option in GHC the assembly code generated is not clear. There's no clear distinction between which parts of the assembly code belong to which parts of the haskell code. Unlike GCC were each label is named according to the function it corresponds to.

Is there a certain convention in these names produced by GHC? How can I relate certain parts in the generated assembly code to their corresponding parts in the haskell code?

like image 888
haskelline Avatar asked Jul 24 '11 18:07

haskelline


People also ask

Does Haskell compile to assembly?

The Haskell compiler hsc, which—in normal use—takes its input from the C pre-processor and produces assembly-language output (sometimes: ANSI C output). The ANSI C Haskell high-level assembler :-) compiles hsc's C output into assembly language for a particular target architecture.

How do I run a compiled Haskell file?

Open a command window and navigate to the directory where you want to keep your Haskell source files. Run Haskell by typing ghci or ghci MyFile. hs. (The "i" in "GHCi" stands for "interactive", as opposed to compiling and producing an executable file.)

Does Haskell compile to machine code?

Unlike Python, Ruby, JavaScript, Lua, and other interpreted languages, Haskell is compiled ahead-of-time, directly to native machine code.

How do I open ghci in terminal?

If you have installed the Haskell Platform, open a terminal and type ghci (the name of the executable of the GHC interpreter) at the command prompt. Alternatively, if you are on Windows, you may choose WinGHCi in the Start menu.


1 Answers

For top level declarations, it's not too hard. Local definitions can be harder to recognize as their names get mangled and they are likely to get inlined.

Let's see what happens when we compile this simple module.

module Example where  add :: Int -> Int -> Int add x y = x + y 
.data     .align 8 .globl Example_add_closure .type Example_add_closure, @object Example_add_closure:     .quad   Example_add_info .text     .align 8     .quad   8589934604     .quad   0     .quad   15 .globl Example_add_info .type Example_add_info, @object Example_add_info: .LckX:     jmp base_GHCziBase_plusInt_info .data     .align 8 _module_registered:     .quad   0 .text     .align 8 .globl __stginit_Example_ .type __stginit_Example_, @object __stginit_Example_: .Lcl7:     cmpq $0,_module_registered     jne .Lcl8 .Lcl9:     movq $1,_module_registered     addq $-8,%rbp     movq $__stginit_base_Prelude_,(%rbp) .Lcl8:     addq $8,%rbp     jmp *-8(%rbp) .text     .align 8 .globl __stginit_Example .type __stginit_Example, @object __stginit_Example: .Lcld:     jmp __stginit_Example_ .section .note.GNU-stack,"",@progbits .ident "GHC 7.0.2" 

You can see that our function Example.add resulted in the generation of Example_add_closure and Example_add_info. The _closure part, as the name suggests, has to do with closures. The _info part contains the actual instructions of the function. In this case, this is simply a jump to the built-in function GHC.Base.plusInt.

Note that assembly generated from Haskell code looks quite different from what you might get from other languages. The calling conventions are different, and things can get reordered a lot.

In most cases you don't want to jump straight to assembly. It is usually much easier to understand core, a simplified version of Haskell. (Simpler to compile, not necessarily to read). To get at the core, compile with the -ddump-simpl option.

Example.add :: GHC.Types.Int -> GHC.Types.Int -> GHC.Types.Int [GblId, Arity=2] Example.add =   \ (x_abt :: GHC.Types.Int) (y_abu :: GHC.Types.Int) ->     GHC.Num.+ @ GHC.Types.Int GHC.Num.$fNumInt x_abt y_abu 

For some good resources on how to read core, see this question.

like image 133
hammar Avatar answered Oct 06 '22 05:10

hammar