|
|
# The Haskell Execution Model
|
|
|
|
|
|
|
|
|
The [STG language](commentary/compiler/stg-syn-type) has a clear *operational* model, as well as having a declarative lambda-calculus reading. The business of the code generator is to translate the STG program into `C--`, and thence to machine code, but that is mere detail. From the STG program you should be able to understand:
|
|
|
The [STG language](commentary/compiler/stg-syn-type) has a clear *operational* model, as well as having a declarative lambda-calculus reading. The business of the [code generator](commentary/compiler/code-gen) is to translate the STG program into `C--`, and thence to machine code, but that is mere detail. From the STG program you should be able to understand:
|
|
|
|
|
|
- What functions are in the compiled program, and what their entry and return conventions are
|
|
|
- What heap objects are allocated, when, and what their layout is
|
... | ... | @@ -26,7 +26,7 @@ During execution of Haskell code the following (virtual) registers are always va |
|
|
- `SpLim` points to the last available byte in the current stack.
|
|
|
|
|
|
|
|
|
There are bunch of other virtual registers, used for temporarily for argument passing, for words, floats and doubles: `R1` .. `R9`, `RF1` .. `RF4`, `RD1` .. `RD4`.
|
|
|
There are bunch of other virtual registers, used for temporarily for argument passing, for words, floats and doubles: `R1` .. `R10`, `F1` .. `F4`, `D1` .. `D4`, `L1` .. `L2`.
|
|
|
|
|
|
|
|
|
In a register-rich machine, many of these virtual registers will be mapped to real registers. In a register-poor machine, they are instead allocated in a static memory record, pointed to by a real register, `BaseReg`.
|
... | ... | @@ -66,9 +66,9 @@ When compiling a call, there are several cases to consider, which are treated se |
|
|
|
|
|
- **Known function, saturated call**. The function is applied to exactly the right number of arguments to satisfy its arity. In that case, we simply load the arguments according to the standard entry convention, and tail-call (jump to) the function's entry point.
|
|
|
|
|
|
- **Known function, too few arguments**. In this case, we build a partial application (PAP), and return with a pointer to the PAP in the return register.
|
|
|
- **Known function, too few arguments**. In this case, we want to build a partial application (PAP), and return with a pointer to the PAP in the return register. Since building a PAP is a complicated business, instead we just behave as for an unknown function call, which will end up calling into the [Generic apply](#Genericapply) code, which will build the PAP for us.
|
|
|
|
|
|
- **Known function, too many arguments**. Save the extra arguments on the stack, push a return address, and then behave just like a saturated call. When the result comes back, behave like "unknown call".
|
|
|
- **Known function, too many arguments**. We want to save the extra arguments on the stack, push a return address, and then behave just like a saturated call. When the result comes back, we should behave like "unknown call". However, to avoid needing to generate code for a new continuation here, the return address that we push on the stack is that of an appropriate [Generic apply](#Genericapply) function, which will perform the application of the extra arguments to the (unknown) function returned by the saturated call.
|
|
|
|
|
|
- **Unknown function**; a call in which we do not statically know what the function is. In that case we must do a "generic apply". This is so exciting that it deserves its [own section](commentary/rts/haskell-execution#generic-apply).
|
|
|
|
... | ... | @@ -78,7 +78,7 @@ When compiling a call, there are several cases to consider, which are treated se |
|
|
Files: [utils/genapply](/trac/ghc/browser/ghc/utils/genapply)
|
|
|
|
|
|
|
|
|
When compiling a call that has an unknown function, we must generate code
|
|
|
When compiling a call that has an unknown function, we must generate code to
|
|
|
|
|
|
- Evaluate the function
|
|
|
- Scrutinise the function value returned to see its arity, and dispatch into the same three cases as in the case of known calls:
|
... | ... | @@ -88,13 +88,17 @@ When compiling a call that has an unknown function, we must generate code |
|
|
- Too many arguments: save the excess arguments, and tail call the function as for a saturated cal.
|
|
|
|
|
|
|
|
|
All of this takes quite a lot of code, so we pre-generate a whole bunch of generic-apply code sequencues, one for each combination of arguments. This code is generated by the tool [utils/genapply](/trac/ghc/browser/ghc/utils/genapply), and appears in [rts/AutoApply.cmm](/trac/ghc/browser/ghc/rts/AutoApply.cmm).
|
|
|
All of this takes quite a lot of code, so we pre-generate a whole bunch of generic-apply code sequencues, one for each combination of arguments. This code is generated by the tool [utils/genapply](/trac/ghc/browser/ghc/utils/genapply), and the generated code appears in `rts/AutoApply.cmm`.
|
|
|
|
|
|
|
|
|
For example, if we find a call to an unknown function applied to two (boxed) `Int` arguments, load the function and its two arguments into the standard places (?) and jump to `stg_ap_pp_entry`. This latter code is in [rts/AutoApply.cmm](/trac/ghc/browser/ghc/rts/AutoApply.cmm), generated by the `autoapply` tool. The "`pp`" part is the bit that says the code is specialised for two pointer arguments.
|
|
|
For example, if we find a call to an unknown function applied to two (boxed) `Int` arguments, load the function and its two arguments as for the standard entry convention and jump to `stg_ap_pp_fast`. This latter code is in `rts/AutoApply.cmm`, generated by the `genapply` tool. The "`pp`" part is the bit that says the code is specialised for two pointer arguments.
|
|
|
|
|
|
|
|
|
If none of the canned sequences apply, then we have to do somthing yet more exotic, and I have forgotten what it is. Simon! Help!
|
|
|
In addition to the family of `stg_ap_<pattern>_fast` functions for making calls to unknown functions with various argument patterns, there is a corresponding family of return addresses `stg_ap_<pattern>_info`. The idea is that you can push a continuation that will make a call to the function that is returned to it. For example, to push a continuation that will apply a single pointer argument, we would push the following words on the stack:
|
|
|
|
|
|
<table><tr><th> arg
|
|
|
</th></tr>
|
|
|
<tr><th>`stg_ap_p_info`</th></tr></table>
|
|
|
|
|
|
## Entry conventions
|
|
|
|
... | ... | |