summaryrefslogtreecommitdiff
path: root/utils/genapply/Main.hs
diff options
context:
space:
mode:
authorBen Gamari <bgamari.foss@gmail.com>2015-12-17 12:13:17 +0100
committerBen Gamari <ben@smart-cactus.org>2015-12-17 12:54:31 +0100
commit0cc4aad36f91570b1b489e3d239256d1c781daac (patch)
tree9c78efbbd45b010741ff5010eaa0e88b23eadf3f /utils/genapply/Main.hs
parent27f47cda4a2d91bbeaeeb5efa8d0e3a908798120 (diff)
downloadhaskell-0cc4aad36f91570b1b489e3d239256d1c781daac.tar.gz
Build system: Cabalize genapply
Test Plan: Validate Reviewers: thomie, austin Reviewed By: thomie, austin Differential Revision: https://phabricator.haskell.org/D1639
Diffstat (limited to 'utils/genapply/Main.hs')
-rw-r--r--utils/genapply/Main.hs1044
1 files changed, 1044 insertions, 0 deletions
diff --git a/utils/genapply/Main.hs b/utils/genapply/Main.hs
new file mode 100644
index 0000000000..e58a496f6a
--- /dev/null
+++ b/utils/genapply/Main.hs
@@ -0,0 +1,1044 @@
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -fno-warn-unused-matches #-}
+{-# OPTIONS_GHC -fno-warn-unused-binds #-}
+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
+{-# OPTIONS_GHC -fno-warn-overlapping-patterns #-}
+{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
+-- The above warning suppression flags are a temporary kludge.
+-- While working on this module you are encouraged to remove it and fix
+-- any warnings in the module. See
+-- http://ghc.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
+-- for details
+module Main(main) where
+
+#include "../../includes/ghcconfig.h"
+#include "../../includes/stg/HaskellMachRegs.h"
+#include "../../includes/rts/Constants.h"
+
+-- Needed for TAG_BITS
+#include "../../includes/MachDeps.h"
+
+import Text.PrettyPrint
+import Data.Word
+import Data.Bits
+import Data.List ( intersperse, nub, sort )
+import System.Exit
+import System.Environment
+import System.IO
+import Control.Arrow ((***))
+
+-- -----------------------------------------------------------------------------
+-- Argument kinds (rougly equivalent to PrimRep)
+
+data ArgRep
+ = N -- non-ptr
+ | P -- ptr
+ | V -- void
+ | F -- float
+ | D -- double
+ | L -- long (64-bit)
+ | V16 -- 16-byte (128-bit) vectors
+ | V32 -- 32-byte (256-bit) vectors
+ | V64 -- 64-byte (512-bit) vectors
+
+-- size of a value in *words*
+argSize :: ArgRep -> Int
+argSize N = 1
+argSize P = 1
+argSize V = 0
+argSize F = 1
+argSize D = (SIZEOF_DOUBLE `quot` SIZEOF_VOID_P :: Int)
+argSize L = (8 `quot` SIZEOF_VOID_P :: Int)
+argSize V16 = (16 `quot` SIZEOF_VOID_P :: Int)
+argSize V32 = (32 `quot` SIZEOF_VOID_P :: Int)
+argSize V64 = (64 `quot` SIZEOF_VOID_P :: Int)
+
+showArg :: ArgRep -> String
+showArg N = "n"
+showArg P = "p"
+showArg V = "v"
+showArg F = "f"
+showArg D = "d"
+showArg L = "l"
+showArg V16 = "v16"
+showArg V32 = "v32"
+showArg V64 = "v64"
+
+-- is a value a pointer?
+isPtr :: ArgRep -> Bool
+isPtr P = True
+isPtr _ = False
+
+-- -----------------------------------------------------------------------------
+-- Registers
+
+data RegStatus = Registerised | Unregisterised
+
+type Reg = String
+
+availableRegs :: RegStatus -> ([Reg],[Reg],[Reg],[Reg])
+availableRegs Unregisterised = ([],[],[],[])
+availableRegs Registerised =
+ ( vanillaRegs MAX_REAL_VANILLA_REG,
+ floatRegs MAX_REAL_FLOAT_REG,
+ doubleRegs MAX_REAL_DOUBLE_REG,
+ longRegs MAX_REAL_LONG_REG
+ )
+
+vanillaRegs, floatRegs, doubleRegs, longRegs :: Int -> [Reg]
+vanillaRegs n = [ "R" ++ show m | m <- [2..n] ] -- never use R1
+floatRegs n = [ "F" ++ show m | m <- [1..n] ]
+doubleRegs n = [ "D" ++ show m | m <- [1..n] ]
+longRegs n = [ "L" ++ show m | m <- [1..n] ]
+
+-- -----------------------------------------------------------------------------
+-- Loading/saving register arguments to the stack
+
+loadRegArgs :: RegStatus -> Int -> [ArgRep] -> (Doc,Int)
+loadRegArgs regstatus sp args
+ = (loadRegOffs reg_locs, sp')
+ where (reg_locs, _, sp') = assignRegs regstatus sp args
+
+loadRegOffs :: [(Reg,Int)] -> Doc
+loadRegOffs = vcat . map (uncurry assign_stk_to_reg)
+
+saveRegOffs :: [(Reg,Int)] -> Doc
+saveRegOffs = vcat . map (uncurry assign_reg_to_stk)
+
+-- a bit like assignRegs in CgRetConv.lhs
+assignRegs
+ :: RegStatus -- are we registerised?
+ -> Int -- Sp of first arg
+ -> [ArgRep] -- args
+ -> ([(Reg,Int)], -- regs and offsets to load
+ [ArgRep], -- left-over args
+ Int) -- Sp of left-over args
+assignRegs regstatus sp args = assign sp args (availableRegs regstatus) []
+
+assign sp [] regs doc = (doc, [], sp)
+assign sp (V : args) regs doc = assign sp args regs doc
+assign sp (arg : args) regs doc
+ = case findAvailableReg arg regs of
+ Just (reg, regs') -> assign (sp + argSize arg) args regs'
+ ((reg, sp) : doc)
+ Nothing -> (doc, (arg:args), sp)
+
+findAvailableReg N (vreg:vregs, fregs, dregs, lregs) =
+ Just (vreg, (vregs,fregs,dregs,lregs))
+findAvailableReg P (vreg:vregs, fregs, dregs, lregs) =
+ Just (vreg, (vregs,fregs,dregs,lregs))
+findAvailableReg F (vregs, freg:fregs, dregs, lregs) =
+ Just (freg, (vregs,fregs,dregs,lregs))
+findAvailableReg D (vregs, fregs, dreg:dregs, lregs) =
+ Just (dreg, (vregs,fregs,dregs,lregs))
+findAvailableReg L (vregs, fregs, dregs, lreg:lregs) =
+ Just (lreg, (vregs,fregs,dregs,lregs))
+findAvailableReg _ _ = Nothing
+
+assign_reg_to_stk reg sp
+ = loadSpWordOff (regRep reg) sp <> text " = " <> text reg <> semi
+
+assign_stk_to_reg reg sp
+ = text reg <> text " = " <> loadSpWordOff (regRep reg) sp <> semi
+
+regRep ('F':_) = "F_"
+regRep ('D':_) = "D_"
+regRep ('L':_) = "L_"
+regRep _ = "W_"
+
+loadSpWordOff :: String -> Int -> Doc
+loadSpWordOff rep off = text rep <> text "[Sp+WDS(" <> int off <> text ")]"
+
+-- Make a jump
+mkJump :: RegStatus -- Registerised status
+ -> Doc -- Jump target
+ -> [Reg] -- Registers that are definitely live
+ -> [ArgRep] -- Jump arguments
+ -> Doc
+mkJump regstatus jump live args =
+ text "jump " <> jump <+> brackets (hcat (punctuate comma (map text regs)))
+ where
+ (reg_locs, _, _) = assignRegs regstatus 0 args
+ regs = (nub . sort) (live ++ map fst reg_locs)
+
+-- make a ptr/non-ptr bitmap from a list of argument types
+mkBitmap :: [ArgRep] -> Word32
+mkBitmap args = foldr f 0 args
+ where f arg bm | isPtr arg = bm `shiftL` 1
+ | otherwise = (bm `shiftL` size) .|. ((1 `shiftL` size) - 1)
+ where size = argSize arg
+
+-- -----------------------------------------------------------------------------
+-- Generating the application functions
+
+-- A SUBTLE POINT about stg_ap functions (can't think of a better
+-- place to put this comment --SDM):
+--
+-- The entry convention to an stg_ap_ function is as follows: all the
+-- arguments are on the stack (we might revisit this at some point,
+-- but it doesn't make any difference on x86), and THERE IS AN EXTRA
+-- EMPTY STACK SLOT at the top of the stack.
+--
+-- Why? Because in several cases, stg_ap_* will need an extra stack
+-- slot, eg. to push a return address in the THUNK case, and this is a
+-- way of pushing the stack check up into the caller which is probably
+-- doing one anyway. Allocating the extra stack slot in the caller is
+-- also probably free, because it will be adjusting Sp after pushing
+-- the args anyway (this might not be true of register-rich machines
+-- when we start passing args to stg_ap_* in regs).
+
+mkApplyName args
+ = text "stg_ap_" <> text (concatMap showArg args)
+
+mkApplyRetName args
+ = mkApplyName args <> text "_ret"
+
+mkApplyFastName args
+ = mkApplyName args <> text "_fast"
+
+mkApplyInfoName args
+ = mkApplyName args <> text "_info"
+
+mb_tag_node arity | Just tag <- tagForArity arity = mkTagStmt tag <> semi
+ | otherwise = empty
+
+mkTagStmt tag = text ("R1 = R1 + "++ show tag)
+
+type StackUsage = (Int, Int) -- PROFILING, normal
+
+maxStack :: [StackUsage] -> StackUsage
+maxStack = (maximum *** maximum) . unzip
+
+stackCheck
+ :: RegStatus -- Registerised status
+ -> [ArgRep]
+ -> Bool -- args in regs?
+ -> Doc -- fun_info_label
+ -> StackUsage
+ -> Doc
+stackCheck regstatus args args_in_regs fun_info_label (prof_sp, norm_sp) =
+ let
+ (reg_locs, leftovers, sp_offset) = assignRegs regstatus 1 args
+
+ cmp_sp n
+ | n > 0 =
+ text "if (Sp - WDS(" <> int n <> text ") < SpLim) {" $$
+ nest 4 (vcat [
+ if args_in_regs
+ then
+ text "Sp_adj" <> parens (int (-sp_offset)) <> semi $$
+ saveRegOffs reg_locs
+ else
+ empty,
+ text "Sp(0) = " <> fun_info_label <> char ';',
+ mkJump regstatus (text "__stg_gc_enter_1") ["R1"] [] <> semi
+ ]) $$
+ char '}'
+ | otherwise = empty
+ in
+ vcat [ text "#ifdef PROFILING",
+ cmp_sp prof_sp,
+ text "#else",
+ cmp_sp norm_sp,
+ text "#endif"
+ ]
+
+genMkPAP :: RegStatus -- Register status
+ -> String -- Macro
+ -> String -- Jump target
+ -> [Reg] -- Registers that are definitely live
+ -> String -- Ticker
+ -> String -- Disamb
+ -> Bool -- Don't load argument registers before jump if True
+ -> Bool -- Arguments already in registers if True
+ -> Bool -- Is a PAP if True
+ -> [ArgRep] -- Arguments
+ -> Int -- Size of all arguments
+ -> Doc -- info label
+ -> Bool -- Is a function
+ -> (Doc, StackUsage)
+genMkPAP regstatus macro jump live ticker disamb
+ no_load_regs -- don't load argument regs before jumping
+ args_in_regs -- arguments are already in regs
+ is_pap args all_args_size fun_info_label
+ is_fun_case
+ = (doc, stack_usage)
+
+ where
+ doc = vcat smaller_arity_doc $$ exact_arity_case $$ larger_arity_doc
+
+ stack_usage = maxStack (larger_arity_stack : smaller_arity_stack)
+
+ n_args = length args
+
+ -- offset of arguments on the stack at slow apply calls.
+ stk_args_slow_offset = 1
+
+ stk_args_offset
+ | args_in_regs = 0
+ | otherwise = stk_args_slow_offset
+
+-- The SMALLER ARITY cases:
+-- if (arity == 1) {
+-- Sp[0] = Sp[1];
+-- Sp[1] = (W_)&stg_ap_1_info;
+-- JMP_(GET_ENTRY(R1.cl));
+ (smaller_arity_doc, smaller_arity_stack)
+ = unzip [ smaller_arity i | i <- [1..n_args-1] ]
+
+ smaller_arity arity = (doc, stack_usage)
+ where
+ (save_regs, stack_usage)
+ | overflow_regs = save_extra_regs
+ | otherwise = shuffle_extra_args
+
+ doc =
+ text "if (arity == " <> int arity <> text ") {" $$
+ nest 4 (vcat [
+ -- text "TICK_SLOW_CALL_" <> text ticker <> text "_TOO_MANY();",
+
+ -- load up regs for the call, if necessary
+ load_regs,
+
+ -- If we have more args in registers than are required
+ -- for the call, then we must save some on the stack,
+ -- and set up the stack for the follow-up call.
+ -- If the extra arguments are on the stack, then we must
+ -- instead shuffle them down to make room for the info
+ -- table for the follow-on call.
+ save_regs,
+
+ -- for a PAP, we have to arrange that the stack contains a
+ -- return address in the event that stg_PAP_entry fails its
+ -- heap check. See stg_PAP_entry in Apply.hc for details.
+ if is_pap
+ then text "R2 = " <> mkApplyInfoName this_call_args <> semi
+
+ else empty,
+ if is_fun_case then mb_tag_node arity else empty,
+ if overflow_regs
+ then text "jump_SAVE_CCCS" <> parens (text jump) <> semi
+ else mkJump regstatus (text jump) live (if no_load_regs then [] else args) <> semi
+ ]) $$
+ text "}"
+
+ -- offsets in case we need to save regs:
+ (reg_locs, _, _)
+ = assignRegs regstatus stk_args_offset args
+
+ -- register assignment for *this function call*
+ (reg_locs', reg_call_leftovers, reg_call_sp_stk_args)
+ = assignRegs regstatus stk_args_offset (take arity args)
+
+ load_regs
+ | no_load_regs || args_in_regs = empty
+ | otherwise = loadRegOffs reg_locs'
+
+ (this_call_args, rest_args) = splitAt arity args
+
+ -- the offset of the stack args from initial Sp
+ sp_stk_args
+ | args_in_regs = stk_args_offset
+ | no_load_regs = stk_args_offset
+ | otherwise = reg_call_sp_stk_args
+
+ -- the stack args themselves
+ this_call_stack_args
+ | args_in_regs = reg_call_leftovers -- sp offsets are wrong
+ | no_load_regs = this_call_args
+ | otherwise = reg_call_leftovers
+
+ stack_args_size = sum (map argSize this_call_stack_args)
+
+ overflow_regs = args_in_regs && length reg_locs > length reg_locs'
+
+ save_extra_regs = (doc, (size,size))
+ where
+ -- we have extra arguments in registers to save
+ extra_reg_locs = drop (length reg_locs') (reverse reg_locs)
+ adj_reg_locs = [ (reg, off - adj + 1) |
+ (reg,off) <- extra_reg_locs ]
+ adj = case extra_reg_locs of
+ (reg, fst_off):_ -> fst_off
+ size = snd (last adj_reg_locs) + 1
+
+ doc =
+ text "Sp_adj(" <> int (-size) <> text ");" $$
+ saveRegOffs adj_reg_locs $$
+ loadSpWordOff "W_" 0 <> text " = " <>
+ mkApplyInfoName rest_args <> semi
+
+ shuffle_extra_args = (doc, (shuffle_prof_stack, shuffle_norm_stack))
+ where
+ doc = vcat [ text "#ifdef PROFILING",
+ shuffle_prof_doc,
+ text "#else",
+ shuffle_norm_doc,
+ text "#endif"]
+
+ (shuffle_prof_doc, shuffle_prof_stack) = shuffle True
+ (shuffle_norm_doc, shuffle_norm_stack) = shuffle False
+
+ -- Sadly here we have to insert an stg_restore_cccs frame
+ -- just underneath the stg_ap_*_info frame if we're
+ -- profiling; see Note [jump_SAVE_CCCS]
+ shuffle prof = (doc, -sp_adj)
+ where
+ sp_adj = sp_stk_args - 1 - offset
+ offset = if prof then 2 else 0
+ doc =
+ vcat (map (shuffle_down (offset+1))
+ [sp_stk_args .. sp_stk_args+stack_args_size-1]) $$
+ (if prof
+ then
+ loadSpWordOff "W_" (sp_stk_args+stack_args_size-3)
+ <> text " = stg_restore_cccs_info;" $$
+ loadSpWordOff "W_" (sp_stk_args+stack_args_size-2)
+ <> text " = CCCS;"
+ else empty) $$
+ loadSpWordOff "W_" (sp_stk_args+stack_args_size-1)
+ <> text " = "
+ <> mkApplyInfoName rest_args <> semi $$
+ text "Sp_adj(" <> int sp_adj <> text ");"
+
+ shuffle_down j i =
+ loadSpWordOff "W_" (i-j) <> text " = " <>
+ loadSpWordOff "W_" i <> semi
+
+
+-- The EXACT ARITY case
+--
+-- if (arity == 1) {
+-- Sp++;
+-- JMP_(GET_ENTRY(R1.cl));
+
+ exact_arity_case
+ = text "if (arity == " <> int n_args <> text ") {" $$
+ let
+ (reg_doc, sp')
+ | no_load_regs || args_in_regs = (empty, stk_args_offset)
+ | otherwise = loadRegArgs regstatus stk_args_offset args
+ in
+ nest 4 (vcat [
+-- text "TICK_SLOW_CALL_" <> text ticker <> text "_CORRECT();",
+ reg_doc,
+ text "Sp_adj(" <> int sp' <> text ");",
+ if is_pap
+ then text "R2 = " <> fun_info_label <> semi
+ else empty,
+ if is_fun_case then mb_tag_node n_args else empty,
+ mkJump regstatus (text jump) live (if no_load_regs then [] else args) <> semi
+ ])
+
+-- The LARGER ARITY cases:
+--
+-- } else /* arity > 1 */ {
+-- BUILD_PAP(1,0,(W_)&stg_ap_v_info);
+-- }
+
+ (larger_arity_doc, larger_arity_stack) = (doc, stack)
+ where
+ -- offsets in case we need to save regs:
+ (reg_locs, leftovers, sp_offset)
+ = assignRegs regstatus stk_args_slow_offset args
+ -- BUILD_PAP assumes args start at offset 1
+
+ stack | args_in_regs = (sp_offset, sp_offset)
+ | otherwise = (0,0)
+
+ doc =
+ text "} else {" $$
+ let
+ save_regs
+ | args_in_regs =
+ text "Sp_adj(" <> int (-sp_offset) <> text ");" $$
+ saveRegOffs reg_locs
+ | otherwise =
+ empty
+ in
+ nest 4 (vcat [
+-- text "TICK_SLOW_CALL_" <> text ticker <> text "_TOO_FEW();",
+ save_regs,
+ -- Before building the PAP, tag the function closure pointer
+ if is_fun_case then
+ vcat [
+ text "if (arity < " <> int tAG_BITS_MAX <> text ") {",
+ text " R1 = R1 + arity" <> semi,
+ text "}"
+ ]
+ else empty
+ ,
+ text macro <> char '(' <> int n_args <> comma <>
+ int all_args_size <>
+ text "," <> fun_info_label <>
+ text "," <> text disamb <>
+ text ");"
+ ]) $$
+ char '}'
+
+
+-- Note [jump_SAVE_CCCS]
+
+-- when profiling, if we have some extra arguments to apply that we
+-- save to the stack, we must also save the current cost centre stack
+-- and restore it when applying the extra arguments. This is all
+-- handled by the macro jump_SAVE_CCCS(target), defined in
+-- rts/AutoApply.h.
+--
+-- At the jump, the stack will look like this:
+--
+-- ... extra args ...
+-- stg_ap_pp_info
+-- CCCS
+-- stg_restore_cccs_info
+
+-- --------------------------------------
+-- Examine tag bits of function pointer and enter it
+-- directly if needed.
+-- TODO: remove the redundant case in the original code.
+enterFastPath regstatus no_load_regs args_in_regs args
+ | Just tag <- tagForArity (length args)
+ = enterFastPathHelper tag regstatus no_load_regs args_in_regs args
+enterFastPath _ _ _ _ = empty
+
+-- Copied from Constants.lhs & CgUtils.hs, i'd rather have this imported:
+-- (arity,tag)
+tAG_BITS = (TAG_BITS :: Int)
+tAG_BITS_MAX = ((1 `shiftL` tAG_BITS) :: Int)
+
+tagForArity :: Int -> Maybe Int
+tagForArity i | i < tAG_BITS_MAX = Just i
+ | otherwise = Nothing
+
+enterFastPathHelper :: Int
+ -> RegStatus
+ -> Bool
+ -> Bool
+ -> [ArgRep]
+ -> Doc
+enterFastPathHelper tag regstatus no_load_regs args_in_regs args =
+ text "if (GETTAG(R1)==" <> int tag <> text ") {" $$
+ nest 4 (vcat [
+ reg_doc,
+ text "Sp_adj(" <> int sp' <> text ");",
+ -- enter, but adjust offset with tag
+ mkJump regstatus (text "%GET_ENTRY(R1-" <> int tag <> text ")") ["R1"] args <> semi
+ ]) $$
+ text "}"
+ -- I don't totally understand this code, I copied it from
+ -- exact_arity_case
+ -- TODO: refactor
+ where
+ -- offset of arguments on the stack at slow apply calls.
+ stk_args_slow_offset = 1
+
+ stk_args_offset
+ | args_in_regs = 0
+ | otherwise = stk_args_slow_offset
+
+ (reg_doc, sp')
+ | no_load_regs || args_in_regs = (empty, stk_args_offset)
+ | otherwise = loadRegArgs regstatus stk_args_offset args
+
+tickForArity arity
+ | True
+ = empty
+ | Just tag <- tagForArity arity
+ = vcat [
+ text "W_[TOTAL_CALLS] = W_[TOTAL_CALLS] + 1;",
+ text "W_[SLOW_CALLS_" <> int arity <> text "] = W_[SLOW_CALLS_" <> int arity <> text "] + 1;",
+ text "if (TO_W_(StgFunInfoExtra_arity(%FUN_INFO(%INFO_PTR(UNTAG(R1))))) == " <> int arity <> text " ) {",
+ text " W_[RIGHT_ARITY_" <> int arity <> text "] = W_[RIGHT_ARITY_" <> int arity <> text "] + 1;",
+ text " if (GETTAG(R1)==" <> int tag <> text ") {",
+ text " W_[TAGGED_PTR_" <> int arity <> text "] = W_[TAGGED_PTR_" <> int arity <> text "] + 1;",
+ text " } else {",
+ -- force a halt when not tagged!
+-- text " W_[0]=0;",
+ text " }",
+ text "}"
+ ]
+tickForArity _ = text "W_[TOTAL_CALLS] = W_[TOTAL_CALLS] + 1;"
+
+-- -----------------------------------------------------------------------------
+-- generate an apply function
+
+-- args is a list of 'p', 'n', 'f', 'd' or 'l'
+formalParam :: ArgRep -> Int -> Doc
+formalParam V _ = empty
+formalParam arg n =
+ formalParamType arg <> space <>
+ text "arg" <> int n <> text ", "
+formalParamType arg = argRep arg
+
+argRep F = text "F_"
+argRep D = text "D_"
+argRep L = text "L_"
+argRep P = text "gcptr"
+argRep V16 = text "V16_"
+argRep V32 = text "V32_"
+argRep V64 = text "V64_"
+argRep _ = text "W_"
+
+genApply regstatus args =
+ let
+ fun_ret_label = mkApplyRetName args
+ fun_info_label = mkApplyInfoName args
+ all_args_size = sum (map argSize args)
+
+ (bco_doc, bco_stack) =
+ genMkPAP regstatus "BUILD_PAP" "ENTRY_LBL(stg_BCO)" ["R1"] "FUN" "BCO"
+ True{-stack apply-} False{-args on stack-} False{-not a PAP-}
+ args all_args_size fun_info_label {- tag stmt -}False
+
+ (fun_doc, fun_stack) =
+ genMkPAP regstatus "BUILD_PAP" "%GET_ENTRY(UNTAG(R1))" ["R1"] "FUN" "FUN"
+ False{-reg apply-} False{-args on stack-} False{-not a PAP-}
+ args all_args_size fun_info_label {- tag stmt -}True
+
+ (pap_doc, pap_stack) =
+ genMkPAP regstatus "NEW_PAP" "stg_PAP_apply" ["R1", "R2"] "PAP" "PAP"
+ True{-stack apply-} False{-args on stack-} True{-is a PAP-}
+ args all_args_size fun_info_label {- tag stmt -}False
+
+ stack_usage = maxStack [bco_stack, fun_stack, pap_stack]
+ in
+ vcat [
+ text "INFO_TABLE_RET(" <> mkApplyName args <> text ", " <>
+ text "RET_SMALL, W_ info_ptr, " <> (cat $ zipWith formalParam args [1..]) <>
+ text ")\n{",
+ nest 4 (vcat [
+ text "W_ info;",
+ text "W_ arity;",
+ text "unwind Sp = Sp + WDS(" <> int (1+all_args_size) <> text ");",
+
+-- if fast == 1:
+-- print "static void *lbls[] ="
+-- print " { [FUN] &&fun_lbl,"
+-- print " [FUN_1_0] &&fun_lbl,"
+-- print " [FUN_0_1] &&fun_lbl,"
+-- print " [FUN_2_0] &&fun_lbl,"
+-- print " [FUN_1_1] &&fun_lbl,"
+-- print " [FUN_0_2] &&fun_lbl,"
+-- print " [FUN_STATIC] &&fun_lbl,"
+-- print " [PAP] &&pap_lbl,"
+-- print " [THUNK] &&thunk_lbl,"
+-- print " [THUNK_1_0] &&thunk_lbl,"
+-- print " [THUNK_0_1] &&thunk_lbl,"
+-- print " [THUNK_2_0] &&thunk_lbl,"
+-- print " [THUNK_1_1] &&thunk_lbl,"
+-- print " [THUNK_0_2] &&thunk_lbl,"
+-- print " [THUNK_STATIC] &&thunk_lbl,"
+-- print " [THUNK_SELECTOR] &&thunk_lbl,"
+-- print " [IND] &&ind_lbl,"
+-- print " [IND_STATIC] &&ind_lbl,"
+-- print " [IND_PERM] &&ind_lbl,"
+-- print " };"
+
+ tickForArity (length args),
+ text "",
+ text "IF_DEBUG(apply,foreign \"C\" debugBelch(\"" <> fun_ret_label <>
+ text "... \"); foreign \"C\" printClosure(R1 \"ptr\"));",
+
+ text "IF_DEBUG(sanity,foreign \"C\" checkStackFrame(Sp+WDS(" <> int (1 + all_args_size)
+ <> text ")\"ptr\"));",
+
+-- text "IF_DEBUG(sanity,checkStackChunk(Sp+" <> int (1 + all_args_size) <>
+-- text ", CurrentTSO->stack + CurrentTSO->stack_size));",
+
+-- text "TICK_SLOW_CALL(" <> int (length args) <> text ");",
+
+ let do_assert [] _ = []
+ do_assert (arg:args) offset
+ | isPtr arg = this : rest
+ | otherwise = rest
+ where this = text "ASSERT(LOOKS_LIKE_CLOSURE_PTR(Sp("
+ <> int offset <> text ")));"
+ rest = do_assert args (offset + argSize arg)
+ in
+ vcat (do_assert args 1),
+
+ text "again:",
+
+ -- if pointer is tagged enter it fast!
+ enterFastPath regstatus False False args,
+
+ stackCheck regstatus args False{-args on stack-}
+ fun_info_label stack_usage,
+
+ -- Functions can be tagged, so we untag them!
+ text "R1 = UNTAG(R1);",
+ text "info = %INFO_PTR(R1);",
+
+-- if fast == 1:
+-- print " goto *lbls[info->type];";
+-- else:
+ text "switch [INVALID_OBJECT .. N_CLOSURE_TYPES] (TO_W_(%INFO_TYPE(%STD_INFO(info)))) {",
+ nest 4 (vcat [
+
+-- if fast == 1:
+-- print " bco_lbl:"
+-- else:
+ text "case BCO: {",
+ nest 4 (vcat [
+ text "arity = TO_W_(StgBCO_arity(R1));",
+ text "ASSERT(arity > 0);",
+ bco_doc
+ ]),
+ text "}",
+
+-- if fast == 1:
+-- print " fun_lbl:"
+-- else:
+ text "case FUN,",
+ text " FUN_1_0,",
+ text " FUN_0_1,",
+ text " FUN_2_0,",
+ text " FUN_1_1,",
+ text " FUN_0_2,",
+ text " FUN_STATIC: {",
+ nest 4 (vcat [
+ text "arity = TO_W_(StgFunInfoExtra_arity(%FUN_INFO(info)));",
+ text "ASSERT(arity > 0);",
+ fun_doc
+ ]),
+ text "}",
+
+-- if fast == 1:
+-- print " pap_lbl:"
+-- else:
+
+ text "case PAP: {",
+ nest 4 (vcat [
+ text "arity = TO_W_(StgPAP_arity(R1));",
+ text "ASSERT(arity > 0);",
+ pap_doc
+ ]),
+ text "}",
+
+ text "",
+
+-- if fast == 1:
+-- print " thunk_lbl:"
+-- else:
+ text "case AP,",
+ text " AP_STACK,",
+ text " BLACKHOLE,",
+ text " WHITEHOLE,",
+ text " THUNK,",
+ text " THUNK_1_0,",
+ text " THUNK_0_1,",
+ text " THUNK_2_0,",
+ text " THUNK_1_1,",
+ text " THUNK_0_2,",
+ text " THUNK_STATIC,",
+ text " THUNK_SELECTOR: {",
+ nest 4 (vcat [
+-- text "TICK_SLOW_CALL_UNEVALD(" <> int (length args) <> text ");",
+ text "Sp(0) = " <> fun_info_label <> text ";",
+ -- CAREFUL! in SMP mode, the info table may already have been
+ -- overwritten by an indirection, so we must enter the original
+ -- info pointer we read, don't read it again, because it might
+ -- not be enterable any more.
+ text "jump_SAVE_CCCS(%ENTRY_CODE(info));",
+ -- see Note [jump_SAVE_CCCS]
+ text ""
+ ]),
+ text "}",
+
+-- if fast == 1:
+-- print " ind_lbl:"
+-- else:
+ text "case IND,",
+ text " IND_STATIC,",
+ text " IND_PERM: {",
+ nest 4 (vcat [
+ text "R1 = StgInd_indirectee(R1);",
+ -- An indirection node might contain a tagged pointer
+ text "goto again;"
+ ]),
+ text "}",
+ text "",
+
+-- if fast == 0:
+
+ text "default: {",
+ nest 4 (
+ text "foreign \"C\" barf(\"" <> fun_ret_label <> text "\") never returns;"
+ ),
+ text "}"
+
+ ]),
+ text "}"
+ ]),
+
+ text "}"
+ ]
+
+-- -----------------------------------------------------------------------------
+-- Making a fast unknown application, args are in regs
+
+genApplyFast regstatus args =
+ let
+ fun_fast_label = mkApplyFastName args
+ fun_ret_label = text "RET_LBL" <> parens (mkApplyName args)
+ fun_info_label = mkApplyInfoName args
+ all_args_size = sum (map argSize args)
+
+ (fun_doc, fun_stack) =
+ genMkPAP regstatus "BUILD_PAP" "%GET_ENTRY(UNTAG(R1))" ["R1"] "FUN" "FUN"
+ False{-reg apply-} True{-args in regs-} False{-not a PAP-}
+ args all_args_size fun_info_label {- tag stmt -}True
+
+ (reg_locs, leftovers, sp_offset) = assignRegs regstatus 1 args
+
+ stack_usage = maxStack [fun_stack, (sp_offset,sp_offset)]
+ in
+ vcat [
+ fun_fast_label,
+ char '{',
+ nest 4 (vcat [
+ text "W_ info;",
+ text "W_ arity;",
+
+ tickForArity (length args),
+
+ -- if pointer is tagged enter it fast!
+ enterFastPath regstatus False True args,
+
+ stackCheck regstatus args True{-args in regs-}
+ fun_info_label stack_usage,
+
+ -- Functions can be tagged, so we untag them!
+ text "R1 = UNTAG(R1);",
+ text "info = %GET_STD_INFO(R1);",
+ text "switch [INVALID_OBJECT .. N_CLOSURE_TYPES] (TO_W_(%INFO_TYPE(info))) {",
+ nest 4 (vcat [
+ text "case FUN,",
+ text " FUN_1_0,",
+ text " FUN_0_1,",
+ text " FUN_2_0,",
+ text " FUN_1_1,",
+ text " FUN_0_2,",
+ text " FUN_STATIC: {",
+ nest 4 (vcat [
+ text "arity = TO_W_(StgFunInfoExtra_arity(%GET_FUN_INFO(R1)));",
+ text "ASSERT(arity > 0);",
+ fun_doc
+ ]),
+ char '}',
+
+ text "default: {",
+ nest 4 (vcat [
+ text "Sp_adj" <> parens (int (-sp_offset)) <> semi,
+ saveRegOffs reg_locs,
+ mkJump regstatus fun_ret_label [] [] <> semi
+ ]),
+ char '}'
+ ]),
+
+ char '}'
+ ]),
+ char '}'
+ ]
+
+-- -----------------------------------------------------------------------------
+-- Making a stack apply
+
+-- These little functions are like slow entry points. They provide
+-- the layer between the PAP entry code and the function's fast entry
+-- point: namely they load arguments off the stack into registers (if
+-- available) and jump to the function's entry code.
+--
+-- On entry: R1 points to the function closure
+-- arguments are on the stack starting at Sp
+--
+-- Invariant: the list of arguments never contains void. Since we're only
+-- interested in loading arguments off the stack here, we can ignore
+-- void arguments.
+
+mkStackApplyEntryLabel:: [ArgRep] -> Doc
+mkStackApplyEntryLabel args = text "stg_ap_stk_" <> text (concatMap showArg args)
+
+genStackApply :: RegStatus -> [ArgRep] -> Doc
+genStackApply regstatus args =
+ let fn_entry_label = mkStackApplyEntryLabel args in
+ vcat [
+ fn_entry_label,
+ text "{", nest 4 body, text "}"
+ ]
+ where
+ (assign_regs, sp') = loadRegArgs regstatus 0 args
+ body = vcat [assign_regs,
+ text "Sp_adj" <> parens (int sp') <> semi,
+ mkJump regstatus (text "%GET_ENTRY(UNTAG(R1))") ["R1"] args <> semi
+ ]
+
+-- -----------------------------------------------------------------------------
+-- Stack save entry points.
+--
+-- These code fragments are used to save registers on the stack at a heap
+-- check failure in the entry code for a function. We also have to save R1
+-- and the return address (stg_gc_fun_info) on the stack. See stg_gc_fun_gen
+-- in HeapStackCheck.hc for more details.
+
+mkStackSaveEntryLabel :: [ArgRep] -> Doc
+mkStackSaveEntryLabel args = text "stg_stk_save_" <> text (concatMap showArg args)
+
+genStackSave :: RegStatus -> [ArgRep] -> Doc
+genStackSave regstatus args =
+ let fn_entry_label= mkStackSaveEntryLabel args in
+ vcat [
+ fn_entry_label,
+ text "{", nest 4 body, text "}"
+ ]
+ where
+ body = vcat [text "Sp_adj" <> parens (int (-sp_offset)) <> semi,
+ saveRegOffs reg_locs,
+ text "Sp(2) = R1;",
+ text "Sp(1) =" <+> int stk_args <> semi,
+ text "Sp(0) = stg_gc_fun_info;",
+ text "jump stg_gc_noregs [];"
+ ]
+
+ std_frame_size = 3 -- the std bits of the frame. See StgRetFun in Closures.h,
+ -- and the comment on stg_fun_gc_gen in HeapStackCheck.hc.
+ (reg_locs, leftovers, sp_offset) = assignRegs regstatus std_frame_size args
+
+ -- number of words of arguments on the stack.
+ stk_args = sum (map argSize leftovers) + sp_offset - std_frame_size
+
+-- -----------------------------------------------------------------------------
+-- The prologue...
+
+main = do
+ args <- getArgs
+ regstatus <- case args of
+ [] -> return Registerised
+ ["-u"] -> return Unregisterised
+ _other -> do hPutStrLn stderr "syntax: genapply [-u]"
+ exitWith (ExitFailure 1)
+ let the_code = vcat [
+ text "// DO NOT EDIT!",
+ text "// Automatically generated by GenApply.hs",
+ text "",
+ text "#include \"Cmm.h\"",
+ text "#include \"AutoApply.h\"",
+ text "",
+
+ vcat (intersperse (text "") $
+ map (genApply regstatus) applyTypes),
+ vcat (intersperse (text "") $
+ map (genStackFns regstatus) stackApplyTypes),
+
+ vcat (intersperse (text "") $
+ map (genApplyFast regstatus) applyTypes),
+
+ genStackApplyArray stackApplyTypes,
+ genStackSaveArray stackApplyTypes,
+ genBitmapArray stackApplyTypes,
+
+ text "" -- add a newline at the end of the file
+ ]
+ -- in
+ putStr (render the_code)
+
+-- These have been shown to cover about 99% of cases in practice...
+applyTypes = [
+ [V],
+ [F],
+ [D],
+ [L],
+ [V16],
+ [V32],
+ [V64],
+ [N],
+ [P],
+ [P,V],
+ [P,P],
+ [P,P,V],
+ [P,P,P],
+ [P,P,P,V],
+ [P,P,P,P],
+ [P,P,P,P,P],
+ [P,P,P,P,P,P]
+ ]
+
+-- No need for V args in the stack apply cases.
+-- ToDo: the stack apply and stack save code doesn't make a distinction
+-- between N and P (they both live in the same register), only the bitmap
+-- changes, so we could share the apply/save code between lots of cases.
+--
+-- NOTE: other places to change if you change stackApplyTypes:
+-- - includes/rts/storage/FunTypes.h
+-- - compiler/codeGen/CgCallConv.lhs: stdPattern
+stackApplyTypes = [
+ [],
+ [N],
+ [P],
+ [F],
+ [D],
+ [L],
+ [V16],
+ [V32],
+ [V64],
+ [N,N],
+ [N,P],
+ [P,N],
+ [P,P],
+ [N,N,N],
+ [N,N,P],
+ [N,P,N],
+ [N,P,P],
+ [P,N,N],
+ [P,N,P],
+ [P,P,N],
+ [P,P,P],
+ [P,P,P,P],
+ [P,P,P,P,P],
+ [P,P,P,P,P,P],
+ [P,P,P,P,P,P,P],
+ [P,P,P,P,P,P,P,P]
+ ]
+
+genStackFns regstatus args
+ = genStackApply regstatus args
+ $$ genStackSave regstatus args
+
+
+genStackApplyArray types =
+ vcat [
+ text "section \"relrodata\" {",
+ text "stg_ap_stack_entries:",
+ text "W_ 0; W_ 0; W_ 0;", -- ARG_GEN, ARG_GEN_BIG, ARG_BCO
+ vcat (map arr_ent types),
+ text "}"
+ ]
+ where
+ arr_ent ty = text "W_" <+> mkStackApplyEntryLabel ty <> semi
+
+genStackSaveArray types =
+ vcat [
+ text "section \"relrodata\" {",
+ text "stg_stack_save_entries:",
+ text "W_ 0; W_ 0; W_ 0;", -- ARG_GEN, ARG_GEN_BIG, ARG_BCO
+ vcat (map arr_ent types),
+ text "}"
+ ]
+ where
+ arr_ent ty = text "W_" <+> mkStackSaveEntryLabel ty <> semi
+
+genBitmapArray :: [[ArgRep]] -> Doc
+genBitmapArray types =
+ vcat [
+ text "section \"rodata\" {",
+ text "stg_arg_bitmaps:",
+ text "W_ 0; W_ 0; W_ 0;", -- ARG_GEN, ARG_GEN_BIG, ARG_BCO
+ vcat (map gen_bitmap types),
+ text "}"
+ ]
+ where
+ gen_bitmap ty = text "W_" <+> int bitmap_val <> semi
+ where bitmap_val =
+ (fromIntegral (mkBitmap ty) `shiftL` BITMAP_BITS_SHIFT)
+ .|. sum (map argSize ty)