| Version 9 (modified by TomMD, 6 years ago) |
|---|
Primitive Operations (PrimOps)
A PrimOp is a function that cannot be implemented in Haskell, and are provided natively by GHC. For example, adding two Int# values is provided as the PrimOp +#, and allocating a new mutable array is the PrimOp newArray#.
PrimOps? are made available to Haskell code through the virtual module GHC.Prim. This module has no implementation, and its interface never resides on disk: if GHC.Prim is imported, we use a built-in ModIface value - see ghcPrimIface in compiler/iface/LoadIface.lhs.
The primops.txt.pp file
The file compiler/prelude/primops.txt.pp includes all the information the compiler needs to know about a PrimOp, bar its actual implementation. For each PrimOp, primops.txt.pp lists:
- Its name, as it appears in Haskell code (eg. int2Integer#)
- Its type
- The name of its constructor in GHC's PrimOp data type.
- Various properties, such as whether the operation is commutable, or has side effects.
For example, here's the integer multiplication PrimOp:
primop IntegerMulOp "timesInteger#" GenPrimOp
Int# -> ByteArr# -> Int# -> ByteArr# -> (# Int#, ByteArr# #)
with commutable = True
out_of_line = True
The primops.txt.pp file is processed first by CPP, and then by the genprimopcode program (see utils/genprimopcode). genprimopcode generates the following bits from primops.txt.pp:
- Various files that are #included into compiler/prelude/PrimOp.lhs, containing declarations of data types and functions describing the PrimOps. See compiler/Makefile.
- libraries/base/GHC/PrimopWrappers.hs, a file that contains (curried) wrapper functions for each of the PrimOps, so that they are accessible from byte-code, and so that the byte-code interpreter doesn't need to implement any PrimOps at all: it just invokes the compiled ones from GHC.PrimopWrappers.
- libraries/base/GHC/Prim.hs, a source file containing dummy declarations for all the PrimOps, solely so that Haddock can include documentation for GHC.Prim in its documentation for the base package. The file GHC/Prim.hs is never actually compiled, only processed by Haddock.
Implementation of PrimOps
PrimOps are divided into two categories for the purposes of implementation: inline and out-of-line.
Inline PrimOps
Inline PrimOps are operations that can be compiled into a short sequence of code that never needs to allocate, block, or return to the scheduler for any reason. An inline PrimOp is compiled directly into Cmm? by the code generator. The code for doing this is in compiler/codeGen/CgPrimOp.hs.
Out-of-line PrimOps
All other PrimOps are classified as out-of-line, and are implemented by hand-written C-- code in the file rts/PrimOps.cmm. An out-of-line PrimOp is like a Haskell function, except that
- PrimOps cannot be partially applied. Calls to all PrimOps are made at the correct arity; this is ensured by the CorePrep? pass.
- Out-of-line PrimOps have a special, fixed, calling convention: all arguments are in the registers R1-R8. This is to make it easy to write the C-- code for these PrimOps: we don't have to write code for multiple calling conventions.
Adding a new PrimOp
To add a new primop, you currently need to update the following files:
- compiler/prelude/primops.txt.pp, which includes the type of the primop, and various other properties. Syntax and examples are in the file.
- if the primop is inline, then: compiler/codeGen/CgPrimOp.hs defines the translation of the primop into Cmm.
- for an out-of-line primop:
- includes/StgMiscClosures.h (just add the declaration),
- rts/PrimOps.cmm (implement it here)
- rts/Linker.c (declare the symbol for GHCi)
See also AddingNewPrimitiveOperations, a blow-by-blow description of the process for adding a new out-of-line primop from someone who went through the process.
Explanation of attributes
TBV (To be verified)
has_side_effects
default = False
out_of_line
default = False
Set to True if there is a function in PrimOps?.cmm. This also changes to code generator to push the continuation of any followon code onto the stack.
commutable
default = False
needs_wrapper
default = False
strictness
default = [lazyDmd, ... ] TopRes?
This is the strictness of the PrimOp?. Unboxed things should be marked as lazyDmd. (Someone explain why?).
usage
default = nomangle other
