1 {-# OPTIONS -cpp -fglasgow-exts #-}
3 -- The above warning supression flag is a temporary kludge.
4 -- While working on this module you are encouraged to remove it and fix
5 -- any warnings in the module. See
6 -- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
8 module Main(main) where
10 #include "../../includes/ghcconfig.h"
11 #include "../../includes/stg/MachRegs.h"
12 #include "../../includes/rts/Constants.h"
14 -- Needed for TAG_BITS
15 #include "../../includes/MachDeps.h"
17 import Text.PrettyPrint
20 import Data.List ( intersperse )
22 import System.Environment
25 -- -----------------------------------------------------------------------------
26 -- Argument kinds (rougly equivalent to PrimRep)
36 -- size of a value in *words*
37 argSize :: ArgRep -> Int
42 argSize D = (SIZEOF_DOUBLE `quot` SIZEOF_VOID_P :: Int)
43 argSize L = (8 `quot` SIZEOF_VOID_P :: Int)
45 showArg :: ArgRep -> Char
53 -- is a value a pointer?
54 isPtr :: ArgRep -> Bool
58 -- -----------------------------------------------------------------------------
61 data RegStatus = Registerised | Unregisterised
65 availableRegs :: RegStatus -> ([Reg],[Reg],[Reg],[Reg])
66 availableRegs Unregisterised = ([],[],[],[])
67 availableRegs Registerised =
68 ( vanillaRegs MAX_REAL_VANILLA_REG,
69 floatRegs MAX_REAL_FLOAT_REG,
70 doubleRegs MAX_REAL_DOUBLE_REG,
71 longRegs MAX_REAL_LONG_REG
74 vanillaRegs, floatRegs, doubleRegs, longRegs :: Int -> [Reg]
75 vanillaRegs n = [ "R" ++ show m | m <- [2..n] ] -- never use R1
76 floatRegs n = [ "F" ++ show m | m <- [1..n] ]
77 doubleRegs n = [ "D" ++ show m | m <- [1..n] ]
78 longRegs n = [ "L" ++ show m | m <- [1..n] ]
80 -- -----------------------------------------------------------------------------
81 -- Loading/saving register arguments to the stack
83 loadRegArgs :: RegStatus -> Int -> [ArgRep] -> (Doc,Int)
84 loadRegArgs regstatus sp args
85 = (loadRegOffs reg_locs, sp')
86 where (reg_locs, _, sp') = assignRegs regstatus sp args
88 loadRegOffs :: [(Reg,Int)] -> Doc
89 loadRegOffs = vcat . map (uncurry assign_stk_to_reg)
91 saveRegOffs :: [(Reg,Int)] -> Doc
92 saveRegOffs = vcat . map (uncurry assign_reg_to_stk)
94 -- a bit like assignRegs in CgRetConv.lhs
96 :: RegStatus -- are we registerised?
97 -> Int -- Sp of first arg
99 -> ([(Reg,Int)], -- regs and offsets to load
100 [ArgRep], -- left-over args
101 Int) -- Sp of left-over args
102 assignRegs regstatus sp args = assign sp args (availableRegs regstatus) []
104 assign sp [] regs doc = (doc, [], sp)
105 assign sp (V : args) regs doc = assign sp args regs doc
106 assign sp (arg : args) regs doc
107 = case findAvailableReg arg regs of
108 Just (reg, regs') -> assign (sp + argSize arg) args regs'
110 Nothing -> (doc, (arg:args), sp)
112 findAvailableReg N (vreg:vregs, fregs, dregs, lregs) =
113 Just (vreg, (vregs,fregs,dregs,lregs))
114 findAvailableReg P (vreg:vregs, fregs, dregs, lregs) =
115 Just (vreg, (vregs,fregs,dregs,lregs))
116 findAvailableReg F (vregs, freg:fregs, dregs, lregs) =
117 Just (freg, (vregs,fregs,dregs,lregs))
118 findAvailableReg D (vregs, fregs, dreg:dregs, lregs) =
119 Just (dreg, (vregs,fregs,dregs,lregs))
120 findAvailableReg L (vregs, fregs, dregs, lreg:lregs) =
121 Just (lreg, (vregs,fregs,dregs,lregs))
122 findAvailableReg _ _ = Nothing
124 assign_reg_to_stk reg sp
125 = loadSpWordOff (regRep reg) sp <> text " = " <> text reg <> semi
127 assign_stk_to_reg reg sp
128 = text reg <> text " = " <> loadSpWordOff (regRep reg) sp <> semi
130 regRep ('F':_) = "F_"
131 regRep ('D':_) = "D_"
132 regRep ('L':_) = "L_"
135 loadSpWordOff :: String -> Int -> Doc
136 loadSpWordOff rep off = text rep <> text "[Sp+WDS(" <> int off <> text ")]"
138 -- make a ptr/non-ptr bitmap from a list of argument types
139 mkBitmap :: [ArgRep] -> Word32
140 mkBitmap args = foldr f 0 args
141 where f arg bm | isPtr arg = bm `shiftL` 1
142 | otherwise = (bm `shiftL` size) .|. ((1 `shiftL` size) - 1)
143 where size = argSize arg
145 -- -----------------------------------------------------------------------------
146 -- Generating the application functions
148 -- A SUBTLE POINT about stg_ap functions (can't think of a better
149 -- place to put this comment --SDM):
151 -- The entry convention to an stg_ap_ function is as follows: all the
152 -- arguments are on the stack (we might revisit this at some point,
153 -- but it doesn't make any difference on x86), and THERE IS AN EXTRA
154 -- EMPTY STACK SLOT at the top of the stack.
156 -- Why? Because in several cases, stg_ap_* will need an extra stack
157 -- slot, eg. to push a return address in the THUNK case, and this is a
158 -- way of pushing the stack check up into the caller which is probably
159 -- doing one anyway. Allocating the extra stack slot in the caller is
160 -- also probably free, because it will be adjusting Sp after pushing
161 -- the args anyway (this might not be true of register-rich machines
162 -- when we start passing args to stg_ap_* in regs).
165 = text "stg_ap_" <> text (map showArg args)
168 = mkApplyName args <> text "_ret"
171 = mkApplyName args <> text "_fast"
174 = mkApplyName args <> text "_info"
176 mb_tag_node arity | Just tag <- tagForArity arity = mkTagStmt tag <> semi
179 mkTagStmt tag = text ("R1 = R1 + "++ show tag)
181 genMkPAP regstatus macro jump ticker disamb
182 no_load_regs -- don't load argumnet regs before jumping
183 args_in_regs -- arguments are already in regs
184 is_pap args all_args_size fun_info_label
186 = smaller_arity_cases
193 -- offset of arguments on the stack at slow apply calls.
194 stk_args_slow_offset = 1
198 | otherwise = stk_args_slow_offset
200 -- The SMALLER ARITY cases:
203 -- Sp[1] = (W_)&stg_ap_1_info;
204 -- JMP_(GET_ENTRY(R1.cl));
205 smaller_arity_cases = vcat [ smaller_arity i | i <- [1..n_args-1] ]
208 = text "if (arity == " <> int arity <> text ") {" $$
210 -- text "TICK_SLOW_CALL_" <> text ticker <> text "_TOO_MANY();",
212 -- load up regs for the call, if necessary
215 -- If we have more args in registers than are required
216 -- for the call, then we must save some on the stack,
217 -- and set up the stack for the follow-up call.
218 -- If the extra arguments are on the stack, then we must
219 -- instead shuffle them down to make room for the info
220 -- table for the follow-on call.
223 else shuffle_extra_args,
225 -- for a PAP, we have to arrange that the stack contains a
226 -- return address in the even that stg_PAP_entry fails its
227 -- heap check. See stg_PAP_entry in Apply.hc for details.
229 then text "R2 = " <> mkApplyInfoName this_call_args <> semi
232 if is_fun_case then mb_tag_node arity else empty,
233 text "jump " <> text jump <> semi
238 -- offsets in case we need to save regs:
240 = assignRegs regstatus stk_args_offset args
242 -- register assignment for *this function call*
243 (reg_locs', reg_call_leftovers, reg_call_sp_stk_args)
244 = assignRegs regstatus stk_args_offset (take arity args)
247 | no_load_regs || args_in_regs = empty
248 | otherwise = loadRegOffs reg_locs'
250 (this_call_args, rest_args) = splitAt arity args
252 -- the offset of the stack args from initial Sp
254 | args_in_regs = stk_args_offset
255 | no_load_regs = stk_args_offset
256 | otherwise = reg_call_sp_stk_args
258 -- the stack args themselves
260 | args_in_regs = reg_call_leftovers -- sp offsets are wrong
261 | no_load_regs = this_call_args
262 | otherwise = reg_call_leftovers
264 stack_args_size = sum (map argSize this_call_stack_args)
266 overflow_regs = args_in_regs && length reg_locs > length reg_locs'
269 = -- we have extra arguments in registers to save
271 extra_reg_locs = drop (length reg_locs') (reverse reg_locs)
272 adj_reg_locs = [ (reg, off - adj + 1) |
273 (reg,off) <- extra_reg_locs ]
274 adj = case extra_reg_locs of
275 (reg, fst_off):_ -> fst_off
276 size = snd (last adj_reg_locs)
278 text "Sp_adj(" <> int (-size - 1) <> text ");" $$
279 saveRegOffs adj_reg_locs $$
280 loadSpWordOff "W_" 0 <> text " = " <>
281 mkApplyInfoName rest_args <> semi
284 = vcat (map shuffle_down
285 [sp_stk_args .. sp_stk_args+stack_args_size-1]) $$
286 loadSpWordOff "W_" (sp_stk_args+stack_args_size-1)
288 <> mkApplyInfoName rest_args <> semi $$
289 text "Sp_adj(" <> int (sp_stk_args - 1) <> text ");"
292 loadSpWordOff "W_" (i-1) <> text " = " <>
293 loadSpWordOff "W_" i <> semi
295 -- The EXACT ARITY case
299 -- JMP_(GET_ENTRY(R1.cl));
302 = text "if (arity == " <> int n_args <> text ") {" $$
305 | no_load_regs || args_in_regs = (empty, stk_args_offset)
306 | otherwise = loadRegArgs regstatus stk_args_offset args
309 -- text "TICK_SLOW_CALL_" <> text ticker <> text "_CORRECT();",
311 text "Sp_adj(" <> int sp' <> text ");",
313 then text "R2 = " <> fun_info_label <> semi
315 if is_fun_case then mb_tag_node n_args else empty,
316 text "jump " <> text jump <> semi
319 -- The LARGER ARITY cases:
321 -- } else /* arity > 1 */ {
322 -- BUILD_PAP(1,0,(W_)&stg_ap_v_info);
330 text "Sp_adj(" <> int (-sp_offset) <> text ");" $$
336 -- text "TICK_SLOW_CALL_" <> text ticker <> text "_TOO_FEW();",
338 -- Before building the PAP, tag the function closure pointer
341 text "if (arity < " <> int tAG_BITS_MAX <> text ") {",
342 text " R1 = R1 + arity" <> semi,
347 text macro <> char '(' <> int n_args <> comma <>
349 text "," <> fun_info_label <>
350 text "," <> text disamb <>
355 -- offsets in case we need to save regs:
356 (reg_locs, leftovers, sp_offset)
357 = assignRegs regstatus stk_args_slow_offset args
358 -- BUILD_PAP assumes args start at offset 1
360 -- --------------------------------------
361 -- Examine tag bits of function pointer and enter it
362 -- directly if needed.
363 -- TODO: remove the redundant case in the original code.
364 enterFastPath regstatus no_load_regs args_in_regs args
365 | Just tag <- tagForArity (length args)
366 = enterFastPathHelper tag regstatus no_load_regs args_in_regs args
367 enterFastPath _ _ _ _ = empty
369 -- Copied from Constants.lhs & CgUtils.hs, i'd rather have this imported:
371 tAG_BITS = (TAG_BITS :: Int)
372 tAG_BITS_MAX = ((1 `shiftL` tAG_BITS) :: Int)
374 tagForArity :: Int -> Maybe Int
375 tagForArity i | i < tAG_BITS_MAX = Just i
376 | otherwise = Nothing
378 enterFastPathHelper tag regstatus no_load_regs args_in_regs args =
379 vcat [text "if (GETTAG(R1)==" <> int tag <> text ") {",
381 text " Sp_adj(" <> int sp' <> text ");",
382 -- enter, but adjust offset with tag
383 text " jump " <> text "%GET_ENTRY(R1-" <> int tag <> text ");",
386 -- I don't totally understand this code, I copied it from
390 -- offset of arguments on the stack at slow apply calls.
391 stk_args_slow_offset = 1
395 | otherwise = stk_args_slow_offset
398 | no_load_regs || args_in_regs = (empty, stk_args_offset)
399 | otherwise = loadRegArgs regstatus stk_args_offset args
404 | Just tag <- tagForArity arity
406 text "W_[TOTAL_CALLS] = W_[TOTAL_CALLS] + 1;",
407 text "W_[SLOW_CALLS_" <> int arity <> text "] = W_[SLOW_CALLS_" <> int arity <> text "] + 1;",
408 text "if (TO_W_(StgFunInfoExtra_arity(%FUN_INFO(%INFO_PTR(UNTAG(R1))))) == " <> int arity <> text " ) {",
409 text " W_[RIGHT_ARITY_" <> int arity <> text "] = W_[RIGHT_ARITY_" <> int arity <> text "] + 1;",
410 text " if (GETTAG(R1)==" <> int tag <> text ") {",
411 text " W_[TAGGED_PTR_" <> int arity <> text "] = W_[TAGGED_PTR_" <> int arity <> text "] + 1;",
413 -- force a halt when not tagged!
418 tickForArity _ = text "W_[TOTAL_CALLS] = W_[TOTAL_CALLS] + 1;"
420 -- -----------------------------------------------------------------------------
421 -- generate an apply function
423 -- args is a list of 'p', 'n', 'f', 'd' or 'l'
424 formalParam :: ArgRep -> Int -> Doc
425 formalParam V _ = empty
427 formalParamType arg <> space <>
428 text "arg" <> int n <> text ", "
429 formalParamType arg = argRep arg
434 argRep P = text "gcptr"
437 genApply regstatus args =
439 fun_ret_label = mkApplyRetName args
440 fun_info_label = mkApplyInfoName args
441 all_args_size = sum (map argSize args)
444 text "INFO_TABLE_RET(" <> mkApplyName args <> text ", " <>
445 text "RET_SMALL, " <> (cat $ zipWith formalParam args [1..]) <>
452 -- print "static void *lbls[] ="
453 -- print " { [FUN] &&fun_lbl,"
454 -- print " [FUN_1_0] &&fun_lbl,"
455 -- print " [FUN_0_1] &&fun_lbl,"
456 -- print " [FUN_2_0] &&fun_lbl,"
457 -- print " [FUN_1_1] &&fun_lbl,"
458 -- print " [FUN_0_2] &&fun_lbl,"
459 -- print " [FUN_STATIC] &&fun_lbl,"
460 -- print " [PAP] &&pap_lbl,"
461 -- print " [THUNK] &&thunk_lbl,"
462 -- print " [THUNK_1_0] &&thunk_lbl,"
463 -- print " [THUNK_0_1] &&thunk_lbl,"
464 -- print " [THUNK_2_0] &&thunk_lbl,"
465 -- print " [THUNK_1_1] &&thunk_lbl,"
466 -- print " [THUNK_0_2] &&thunk_lbl,"
467 -- print " [THUNK_STATIC] &&thunk_lbl,"
468 -- print " [THUNK_SELECTOR] &&thunk_lbl,"
469 -- print " [IND] &&ind_lbl,"
470 -- print " [IND_OLDGEN] &&ind_lbl,"
471 -- print " [IND_STATIC] &&ind_lbl,"
472 -- print " [IND_PERM] &&ind_lbl,"
473 -- print " [IND_OLDGEN_PERM] &&ind_lbl"
476 tickForArity (length args),
478 text "IF_DEBUG(apply,foreign \"C\" debugBelch(\"" <> fun_ret_label <>
479 text "... \"); foreign \"C\" printClosure(R1 \"ptr\"));",
481 text "IF_DEBUG(sanity,foreign \"C\" checkStackFrame(Sp+WDS(" <> int (1 + all_args_size)
482 <> text ")\"ptr\"));",
484 -- text "IF_DEBUG(sanity,checkStackChunk(Sp+" <> int (1 + all_args_size) <>
485 -- text ", CurrentTSO->stack + CurrentTSO->stack_size));",
487 -- text "TICK_SLOW_CALL(" <> int (length args) <> text ");",
489 let do_assert [] _ = []
490 do_assert (arg:args) offset
491 | isPtr arg = this : rest
493 where this = text "ASSERT(LOOKS_LIKE_CLOSURE_PTR(Sp("
494 <> int offset <> text ")));"
495 rest = do_assert args (offset + argSize arg)
497 vcat (do_assert args 1),
501 -- if pointer is tagged enter it fast!
502 enterFastPath regstatus False False args,
504 -- Functions can be tagged, so we untag them!
505 text "R1 = UNTAG(R1);",
506 text "info = %INFO_PTR(R1);",
509 -- print " goto *lbls[info->type];";
511 text "switch [INVALID_OBJECT .. N_CLOSURE_TYPES] (TO_W_(%INFO_TYPE(%STD_INFO(info)))) {",
519 text "arity = TO_W_(StgBCO_arity(R1));",
520 text "ASSERT(arity > 0);",
521 genMkPAP regstatus "BUILD_PAP" "ENTRY_LBL(stg_BCO)" "FUN" "BCO"
522 True{-stack apply-} False{-args on stack-} False{-not a PAP-}
523 args all_args_size fun_info_label {- tag stmt -}False
536 text " FUN_STATIC: {",
538 text "arity = TO_W_(StgFunInfoExtra_arity(%FUN_INFO(info)));",
539 text "ASSERT(arity > 0);",
540 genMkPAP regstatus "BUILD_PAP" "%GET_ENTRY(UNTAG(R1))" "FUN" "FUN"
541 False{-reg apply-} False{-args on stack-} False{-not a PAP-}
542 args all_args_size fun_info_label {- tag stmt -}True
552 text "arity = TO_W_(StgPAP_arity(R1));",
553 text "ASSERT(arity > 0);",
554 genMkPAP regstatus "NEW_PAP" "stg_PAP_apply" "PAP" "PAP"
555 True{-stack apply-} False{-args on stack-} True{-is a PAP-}
556 args all_args_size fun_info_label {- tag stmt -}False
563 -- print " thunk_lbl:"
567 text " CAF_BLACKHOLE,",
575 text " THUNK_STATIC,",
576 text " THUNK_SELECTOR: {",
578 -- text "TICK_SLOW_CALL_UNEVALD(" <> int (length args) <> text ");",
579 text "Sp(0) = " <> fun_info_label <> text ";",
580 -- CAREFUL! in SMP mode, the info table may already have been
581 -- overwritten by an indirection, so we must enter the original
582 -- info pointer we read, don't read it again, because it might
583 -- not be enterable any more.
584 text "jump %ENTRY_CODE(info);",
596 text " IND_OLDGEN_PERM: {",
598 text "R1 = StgInd_indirectee(R1);",
599 -- An indirection node might contain a tagged pointer
609 text "foreign \"C\" barf(\"" <> fun_ret_label <> text "\") never returns;"
619 -- -----------------------------------------------------------------------------
620 -- Making a fast unknown application, args are in regs
622 genApplyFast regstatus args =
624 fun_fast_label = mkApplyFastName args
625 fun_ret_label = text "RET_LBL" <> parens (mkApplyName args)
626 fun_info_label = mkApplyInfoName args
627 all_args_size = sum (map argSize args)
636 tickForArity (length args),
638 -- if pointer is tagged enter it fast!
639 enterFastPath regstatus False True args,
641 -- Functions can be tagged, so we untag them!
642 text "R1 = UNTAG(R1);",
643 text "info = %GET_STD_INFO(R1);",
644 text "switch [INVALID_OBJECT .. N_CLOSURE_TYPES] (TO_W_(%INFO_TYPE(info))) {",
652 text " FUN_STATIC: {",
654 text "arity = TO_W_(StgFunInfoExtra_arity(%GET_FUN_INFO(R1)));",
655 text "ASSERT(arity > 0);",
656 genMkPAP regstatus "BUILD_PAP" "%GET_ENTRY(UNTAG(R1))" "FUN" "FUN"
657 False{-reg apply-} True{-args in regs-} False{-not a PAP-}
658 args all_args_size fun_info_label {- tag stmt -}True
664 (reg_locs, leftovers, sp_offset) = assignRegs regstatus 1 args
665 -- leave a one-word space on the top of the stack when
666 -- calling the slow version
669 text "Sp_adj" <> parens (int (-sp_offset)) <> semi,
670 saveRegOffs reg_locs,
671 text "jump" <+> fun_ret_label <> semi
680 -- -----------------------------------------------------------------------------
681 -- Making a stack apply
683 -- These little functions are like slow entry points. They provide
684 -- the layer between the PAP entry code and the function's fast entry
685 -- point: namely they load arguments off the stack into registers (if
686 -- available) and jump to the function's entry code.
688 -- On entry: R1 points to the function closure
689 -- arguments are on the stack starting at Sp
691 -- Invariant: the list of arguments never contains void. Since we're only
692 -- interested in loading arguments off the stack here, we can ignore
695 mkStackApplyEntryLabel:: [ArgRep] -> Doc
696 mkStackApplyEntryLabel args = text "stg_ap_stk_" <> text (map showArg args)
698 genStackApply :: RegStatus -> [ArgRep] -> Doc
699 genStackApply regstatus args =
700 let fn_entry_label = mkStackApplyEntryLabel args in
703 text "{", nest 4 body, text "}"
706 (assign_regs, sp') = loadRegArgs regstatus 0 args
707 body = vcat [assign_regs,
708 text "Sp_adj" <> parens (int sp') <> semi,
709 text "jump %GET_ENTRY(UNTAG(R1));"
712 -- -----------------------------------------------------------------------------
713 -- Stack save entry points.
715 -- These code fragments are used to save registers on the stack at a heap
716 -- check failure in the entry code for a function. We also have to save R1
717 -- and the return address (stg_gc_fun_info) on the stack. See stg_gc_fun_gen
718 -- in HeapStackCheck.hc for more details.
720 mkStackSaveEntryLabel :: [ArgRep] -> Doc
721 mkStackSaveEntryLabel args = text "stg_stk_save_" <> text (map showArg args)
723 genStackSave :: RegStatus -> [ArgRep] -> Doc
724 genStackSave regstatus args =
725 let fn_entry_label= mkStackSaveEntryLabel args in
728 text "{", nest 4 body, text "}"
731 body = vcat [text "Sp_adj" <> parens (int (-sp_offset)) <> semi,
732 saveRegOffs reg_locs,
734 text "Sp(1) =" <+> int stk_args <> semi,
735 text "Sp(0) = stg_gc_fun_info;",
736 text "jump stg_gc_noregs;"
739 std_frame_size = 3 -- the std bits of the frame. See StgRetFun in Closures.h,
740 -- and the comment on stg_fun_gc_gen in HeapStackCheck.hc.
741 (reg_locs, leftovers, sp_offset) = assignRegs regstatus std_frame_size args
743 -- number of words of arguments on the stack.
744 stk_args = sum (map argSize leftovers) + sp_offset - std_frame_size
746 -- -----------------------------------------------------------------------------
751 regstatus <- case args of
752 [] -> return Registerised
753 ["-u"] -> return Unregisterised
754 _other -> do hPutStrLn stderr "syntax: genapply [-u]"
755 exitWith (ExitFailure 1)
756 let the_code = vcat [
757 text "// DO NOT EDIT!",
758 text "// Automatically generated by GenApply.hs",
760 text "#include \"Cmm.h\"",
761 text "#include \"AutoApply.h\"",
764 vcat (intersperse (text "") $
765 map (genApply regstatus) applyTypes),
766 vcat (intersperse (text "") $
767 map (genStackFns regstatus) stackApplyTypes),
769 vcat (intersperse (text "") $
770 map (genApplyFast regstatus) applyTypes),
772 genStackApplyArray stackApplyTypes,
773 genStackSaveArray stackApplyTypes,
774 genBitmapArray stackApplyTypes,
776 text "" -- add a newline at the end of the file
779 putStr (render the_code)
781 -- These have been shown to cover about 99% of cases in practice...
799 -- No need for V args in the stack apply cases.
800 -- ToDo: the stack apply and stack save code doesn't make a distinction
801 -- between N and P (they both live in the same register), only the bitmap
802 -- changes, so we could share the apply/save code between lots of cases.
829 genStackFns regstatus args
830 = genStackApply regstatus args
831 $$ genStackSave regstatus args
834 genStackApplyArray types =
836 text "section \"relrodata\" {",
837 text "stg_ap_stack_entries:",
838 text "W_ 0; W_ 0; W_ 0;", -- ARG_GEN, ARG_GEN_BIG, ARG_BCO
839 vcat (map arr_ent types),
843 arr_ent ty = text "W_" <+> mkStackApplyEntryLabel ty <> semi
845 genStackSaveArray types =
847 text "section \"relrodata\" {",
848 text "stg_stack_save_entries:",
849 text "W_ 0; W_ 0; W_ 0;", -- ARG_GEN, ARG_GEN_BIG, ARG_BCO
850 vcat (map arr_ent types),
854 arr_ent ty = text "W_" <+> mkStackSaveEntryLabel ty <> semi
856 genBitmapArray :: [[ArgRep]] -> Doc
857 genBitmapArray types =
859 text "section \"rodata\" {",
860 text "stg_arg_bitmaps:",
861 text "W_ 0; W_ 0; W_ 0;", -- ARG_GEN, ARG_GEN_BIG, ARG_BCO
862 vcat (map gen_bitmap types),
866 gen_bitmap ty = text "W_" <+> int bitmap_val <> semi
868 (fromIntegral (mkBitmap ty) `shiftL` BITMAP_BITS_SHIFT)
869 .|. sum (map argSize ty)