1 -----------------------------------------------------------------------------
3 -- Building info tables.
5 -- (c) The University of Glasgow 2004-2006
7 -----------------------------------------------------------------------------
10 -- The above warning supression flag is a temporary kludge.
11 -- While working on this module you are encouraged to remove it and fix
12 -- any warnings in the module. See
13 -- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
20 emitClosureProcAndInfoTable,
21 emitClosureAndInfoTable,
25 mkVirtHeapOffsets, getHpRelOffset, hpRel,
28 entryCode, closureInfoPtr,
31 infoTable, infoTableClosureType,
32 infoTablePtrs, infoTableNonPtrs,
33 funInfoTable, makeRelativeRefTo
37 #include "HsVersions.h"
54 import TyCon ( PrimRep(..) )
56 import BasicTypes ( Arity )
66 import FastString ( mkFastString, LitString, sLit )
68 ------------------------------------------------------------------------
69 -- Call and return sequences
70 ------------------------------------------------------------------------
72 emitReturn :: [CmmExpr] -> FCode ()
73 -- Return multiple values to the sequel
75 -- If the sequel is Return
77 -- If the sequel is AssignTo [p,q]
80 = do { sequel <- getSequel;
81 ; updfr_off <- getUpdFrameOff
82 ; emit $ mkComment $ mkFastString ("emitReturn: " ++ show sequel)
85 do { adjustHpBackwards
86 ; emit (mkReturnSimple results updfr_off) }
87 AssignTo regs adjust ->
88 do { if adjust then adjustHpBackwards else return ()
89 ; emit (mkMultiAssign regs results) }
92 emitCall :: (Convention, Convention) -> CmmExpr -> [CmmExpr] -> FCode ()
93 -- (cgCall fun args) makes a call to the entry-code of 'fun',
94 -- passing 'args', and returning the results to the current sequel
95 emitCall convs@(callConv, _) fun args
96 = do { adjustHpBackwards
98 ; updfr_off <- getUpdFrameOff
99 ; emit $ mkComment $ mkFastString ("emitCall: " ++ show sequel)
101 Return _ -> emit (mkForeignJump callConv fun args updfr_off)
102 AssignTo res_regs _ -> emit (mkCall fun convs res_regs args updfr_off)
105 adjustHpBackwards :: FCode ()
106 -- This function adjusts and heap pointers just before a tail call or
107 -- return. At a call or return, the virtual heap pointer may be less
108 -- than the real Hp, because the latter was advanced to deal with
109 -- the worst-case branch of the code, and we may be in a better-case
110 -- branch. In that case, move the real Hp *back* and retract some
111 -- ticky allocation count.
113 -- It *does not* deal with high-water-mark adjustment.
114 -- That's done by functions which allocate heap.
116 = do { hp_usg <- getHpUsage
117 ; let rHp = realHp hp_usg
119 adjust_words = vHp -rHp
120 ; new_hp <- getHpRelOffset vHp
122 ; emit (if adjust_words == 0
124 else mkAssign hpReg new_hp) -- Generates nothing when vHp==rHp
126 ; tickyAllocHeap adjust_words -- ...ditto
132 -------------------------------------------------------------------------
133 -- Making calls: directCall and slowCall
134 -------------------------------------------------------------------------
136 directCall :: CLabel -> Arity -> [StgArg] -> FCode ()
137 -- (directCall f n args)
138 -- calls f(arg1, ..., argn), and applies the result to the remaining args
139 -- The function f has arity n, and there are guaranteed at least n args
140 -- Both arity and args include void args
141 directCall lbl arity stg_args
142 = do { cmm_args <- getNonVoidArgAmodes stg_args
143 ; direct_call "directCall" lbl arity cmm_args (argsLReps stg_args) }
145 slowCall :: CmmExpr -> [StgArg] -> FCode ()
146 -- (slowCall fun args) applies fun to args, returning the results to Sequel
147 slowCall fun stg_args
148 = do { cmm_args <- getNonVoidArgAmodes stg_args
149 ; slow_call fun cmm_args (argsLReps stg_args) }
152 direct_call :: String -> CLabel -> Arity -> [CmmExpr] -> [LRep] -> FCode ()
153 -- NB1: (length args) may be less than (length reps), because
154 -- the args exclude the void ones
155 -- NB2: 'arity' refers to the *reps*
156 direct_call caller lbl arity args reps
157 | debugIsOn && arity > length reps -- Too few args
158 = -- Caller should ensure that there enough args!
159 pprPanic "direct_call" (text caller <+> ppr arity <+> ppr lbl <+> ppr (length reps)
160 <+> ppr args <+> ppr reps )
162 | null rest_reps -- Precisely the right number of arguments
163 = emitCall (NativeDirectCall, NativeReturn) target args
165 | otherwise -- Over-saturated call
166 = ASSERT( arity == length initial_reps )
167 do { pap_id <- newTemp gcWord
168 ; withSequel (AssignTo [pap_id] True)
169 (emitCall (NativeDirectCall, NativeReturn) target fast_args)
170 ; slow_call (CmmReg (CmmLocal pap_id))
171 rest_args rest_reps }
173 target = CmmLit (CmmLabel lbl)
174 (initial_reps, rest_reps) = splitAt arity reps
175 arg_arity = count isNonV initial_reps
176 (fast_args, rest_args) = splitAt arg_arity args
179 slow_call :: CmmExpr -> [CmmExpr] -> [LRep] -> FCode ()
180 slow_call fun args reps
181 = do call <- getCode $ direct_call "slow_call" (mkRtsApFastLabel rts_fun) arity args reps
182 emit $ mkComment $ mkFastString ("slow_call for " ++ showSDoc (ppr fun) ++
183 " with pat " ++ showSDoc (ptext rts_fun))
184 emit (mkAssign nodeReg fun <*> call)
186 (rts_fun, arity) = slowCallPattern reps
188 -- These cases were found to cover about 99% of all slow calls:
189 slowCallPattern :: [LRep] -> (LitString, Arity)
190 -- Returns the generic apply function and arity
191 slowCallPattern (P: P: P: P: P: P: _) = (sLit "stg_ap_pppppp", 6)
192 slowCallPattern (P: P: P: P: P: _) = (sLit "stg_ap_ppppp", 5)
193 slowCallPattern (P: P: P: P: _) = (sLit "stg_ap_pppp", 4)
194 slowCallPattern (P: P: P: V: _) = (sLit "stg_ap_pppv", 4)
195 slowCallPattern (P: P: P: _) = (sLit "stg_ap_ppp", 3)
196 slowCallPattern (P: P: V: _) = (sLit "stg_ap_ppv", 3)
197 slowCallPattern (P: P: _) = (sLit "stg_ap_pp", 2)
198 slowCallPattern (P: V: _) = (sLit "stg_ap_pv", 2)
199 slowCallPattern (P: _) = (sLit "stg_ap_p", 1)
200 slowCallPattern (V: _) = (sLit "stg_ap_v", 1)
201 slowCallPattern (N: _) = (sLit "stg_ap_n", 1)
202 slowCallPattern (F: _) = (sLit "stg_ap_f", 1)
203 slowCallPattern (D: _) = (sLit "stg_ap_d", 1)
204 slowCallPattern (L: _) = (sLit "stg_ap_l", 1)
205 slowCallPattern [] = (sLit "stg_ap_0", 0)
208 -------------------------------------------------------------------------
209 -- Classifying arguments: LRep
210 -------------------------------------------------------------------------
212 -- LRep is not exported (even abstractly)
213 -- It's a local helper type for classification
215 data LRep = P -- GC Ptr
216 | N -- One-word non-ptr
217 | L -- Two-word non-ptr (long)
221 instance Outputable LRep where
229 toLRep :: PrimRep -> LRep
240 isNonV :: LRep -> Bool
244 argsLReps :: [StgArg] -> [LRep]
245 argsLReps = map (toLRep . argPrimRep)
247 lRepSizeW :: LRep -> WordOff -- Size in words
251 lRepSizeW L = wORD64_SIZE `quot` wORD_SIZE
252 lRepSizeW D = dOUBLE_SIZE `quot` wORD_SIZE
255 -------------------------------------------------------------------------
256 ---- Laying out objects on the heap and stack
257 -------------------------------------------------------------------------
259 -- The heap always grows upwards, so hpRel is easy
260 hpRel :: VirtualHpOffset -- virtual offset of Hp
261 -> VirtualHpOffset -- virtual offset of The Thing
262 -> WordOff -- integer word offset
263 hpRel hp off = off - hp
265 getHpRelOffset :: VirtualHpOffset -> FCode CmmExpr
266 getHpRelOffset virtual_offset
267 = do { hp_usg <- getHpUsage
268 ; return (cmmRegOffW hpReg (hpRel (realHp hp_usg) virtual_offset)) }
271 :: Bool -- True <=> is a thunk
272 -> [(PrimRep,a)] -- Things to make offsets for
273 -> (WordOff, -- _Total_ number of words allocated
274 WordOff, -- Number of words allocated for *pointers*
275 [(NonVoid a, VirtualHpOffset)])
277 -- Things with their offsets from start of object in order of
278 -- increasing offset; BUT THIS MAY BE DIFFERENT TO INPUT ORDER
279 -- First in list gets lowest offset, which is initial offset + 1.
281 -- Void arguments are removed, so output list may be shorter than
284 -- mkVirtHeapOffsets always returns boxed things with smaller offsets
285 -- than the unboxed things
287 mkVirtHeapOffsets is_thunk things
288 = let non_void_things = filterOut (isVoidRep . fst) things
289 (ptrs, non_ptrs) = partition (isGcPtrRep . fst) non_void_things
290 (wds_of_ptrs, ptrs_w_offsets) = mapAccumL computeOffset 0 ptrs
291 (tot_wds, non_ptrs_w_offsets) = mapAccumL computeOffset wds_of_ptrs non_ptrs
293 (tot_wds, wds_of_ptrs, ptrs_w_offsets ++ non_ptrs_w_offsets)
295 hdr_size | is_thunk = thunkHdrSize
296 | otherwise = fixedHdrSize
298 computeOffset wds_so_far (rep, thing)
299 = (wds_so_far + lRepSizeW (toLRep rep),
300 (NonVoid thing, hdr_size + wds_so_far))
303 -------------------------------------------------------------------------
305 -- Making argument descriptors
307 -- An argument descriptor describes the layout of args on the stack,
308 -- both for * GC (stack-layout) purposes, and
309 -- * saving/restoring registers when a heap-check fails
311 -- Void arguments aren't important, therefore (contrast constructSlowCall)
313 -------------------------------------------------------------------------
315 -- bring in ARG_P, ARG_N, etc.
316 #include "../includes/rts/storage/FunTypes.h"
318 -------------------------
319 -- argDescrType :: ArgDescr -> StgHalfWord
320 -- -- The "argument type" RTS field type
321 -- argDescrType (ArgSpec n) = n
322 -- argDescrType (ArgGen liveness)
323 -- | isBigLiveness liveness = ARG_GEN_BIG
324 -- | otherwise = ARG_GEN
327 mkArgDescr :: Name -> [Id] -> FCode ArgDescr
329 = case stdPattern arg_reps of
330 Just spec_id -> return (ArgSpec spec_id)
331 Nothing -> do { liveness <- mkLiveness nm size bitmap
332 ; return (ArgGen liveness) }
334 arg_reps = filter isNonV (map (toLRep . idPrimRep) args)
335 -- Getting rid of voids eases matching of standard patterns
337 bitmap = mkBitmap arg_bits
338 arg_bits = argBits arg_reps
339 size = length arg_bits
341 argBits :: [LRep] -> [Bool] -- True for non-ptr, False for ptr
343 argBits (P : args) = False : argBits args
344 argBits (arg : args) = take (lRepSizeW arg) (repeat True) ++ argBits args
346 ----------------------
347 stdPattern :: [LRep] -> Maybe StgHalfWord
350 [] -> Just ARG_NONE -- just void args, probably
362 [N,N,N] -> Just ARG_NNN
363 [N,N,P] -> Just ARG_NNP
364 [N,P,N] -> Just ARG_NPN
365 [N,P,P] -> Just ARG_NPP
366 [P,N,N] -> Just ARG_PNN
367 [P,N,P] -> Just ARG_PNP
368 [P,P,N] -> Just ARG_PPN
369 [P,P,P] -> Just ARG_PPP
371 [P,P,P,P] -> Just ARG_PPPP
372 [P,P,P,P,P] -> Just ARG_PPPPP
373 [P,P,P,P,P,P] -> Just ARG_PPPPPP
377 -------------------------------------------------------------------------
381 -------------------------------------------------------------------------
383 -- TODO: This along with 'mkArgDescr' should be unified
384 -- with 'CmmInfo.mkLiveness'. However that would require
385 -- potentially invasive changes to the 'ClosureInfo' type.
386 -- For now, 'CmmInfo.mkLiveness' handles only continuations and
387 -- this one handles liveness everything else. Another distinction
388 -- between these two is that 'CmmInfo.mkLiveness' information
389 -- about the stack layout, and this one is information about
390 -- the heap layout of PAPs.
391 mkLiveness :: Name -> Int -> Bitmap -> FCode Liveness
392 mkLiveness name size bits
393 | size > mAX_SMALL_BITMAP_SIZE -- Bitmap does not fit in one word
394 = do { let lbl = mkBitmapLabel (getUnique name)
395 ; emitRODataLits lbl ( mkWordCLit (fromIntegral size)
396 : map mkWordCLit bits)
397 ; return (BigLiveness lbl) }
399 | otherwise -- Bitmap fits in one word
401 small_bits = case bits of
403 [b] -> fromIntegral b
404 _ -> panic "livenessToAddrMode"
406 return (smallLiveness size small_bits)
408 smallLiveness :: Int -> StgWord -> Liveness
409 smallLiveness size small_bits = SmallLiveness bits
410 where bits = fromIntegral size .|. (small_bits `shiftL` bITMAP_BITS_SHIFT)
413 -- isBigLiveness :: Liveness -> Bool
414 -- isBigLiveness (BigLiveness _) = True
415 -- isBigLiveness (SmallLiveness _) = False
418 -- mkLivenessCLit :: Liveness -> CmmLit
419 -- mkLivenessCLit (BigLiveness lbl) = CmmLabel lbl
420 -- mkLivenessCLit (SmallLiveness bits) = mkWordCLit bits
423 -------------------------------------------------------------------------
425 -- Bitmap describing register liveness
426 -- across GC when doing a "generic" heap check
427 -- (a RET_DYN stack frame).
429 -- NB. Must agree with these macros (currently in StgMacros.h):
430 -- GET_NON_PTRS(), GET_PTRS(), GET_LIVENESS().
431 -------------------------------------------------------------------------
433 {- Not used in new code gen
434 mkRegLiveness :: [(Id, GlobalReg)] -> Int -> Int -> StgWord
435 mkRegLiveness regs ptrs nptrs
436 = (fromIntegral nptrs `shiftL` 16) .|.
437 (fromIntegral ptrs `shiftL` 24) .|.
438 all_non_ptrs `xor` reg_bits regs
443 reg_bits ((id, VanillaReg i) : regs) | isGcPtrRep (idPrimRep id)
444 = (1 `shiftL` (i - 1)) .|. reg_bits regs
449 -------------------------------------------------------------------------
451 -- Generating the info table and code for a closure
453 -------------------------------------------------------------------------
455 -- Here we make an info table of type 'CmmInfo'. The concrete
456 -- representation as a list of 'CmmAddr' is handled later
457 -- in the pipeline by 'cmmToRawCmm'.
458 -- When loading the free variables, a function closure pointer may be tagged,
459 -- so we must take it into account.
461 emitClosureProcAndInfoTable :: Bool -- top-level?
462 -> Id -- name of the closure
463 -> ClosureInfo -- lots of info abt the closure
464 -> [NonVoid Id] -- incoming arguments
465 -> ((LocalReg, [LocalReg]) -> FCode ()) -- function body
467 emitClosureProcAndInfoTable top_lvl bndr cl_info args body
468 = do { let lf_info = closureLFInfo cl_info
469 -- Bind the binder itself, but only if it's not a top-level
470 -- binding. We need non-top let-bindings to refer to the
471 -- top-level binding, which this binding would incorrectly shadow.
472 ; node <- if top_lvl then return $ idToReg (NonVoid bndr)
473 else bindToReg (NonVoid bndr) lf_info
474 ; let node_points = nodeMustPointToIt lf_info
475 ; arg_regs <- bindArgsToRegs args
476 ; let args' = if node_points then (node : arg_regs) else arg_regs
477 ; emitClosureAndInfoTable cl_info args' $ body (node, arg_regs)
480 -- Data constructors need closures, but not with all the argument handling
481 -- needed for functions. The shared part goes here.
482 emitClosureAndInfoTable :: ClosureInfo -> [LocalReg] -> FCode () -> FCode ()
483 emitClosureAndInfoTable cl_info args body
484 = do { info <- mkCmmInfo cl_info
485 ; blks <- getCode body
486 ; let conv = if nodeMustPointToIt (closureLFInfo cl_info) then NativeNodeCall
487 else NativeDirectCall
488 ; emitProcWithConvention conv info (infoLblToEntryLbl info_lbl) args blks
491 info_lbl = infoTableLabelFromCI cl_info
493 -- Convert from 'ClosureInfo' to 'CmmInfo'.
494 -- Not used for return points. (The 'smRepClosureTypeInt' call would panic.)
495 mkCmmInfo :: ClosureInfo -> FCode CmmInfo
497 = do { info <- closureTypeInfo cl_info k_with_con_name return
498 ; prof <- if opt_SccProfilingOn then
499 do fd_lit <- mkStringCLit (closureTypeDescr cl_info)
500 ad_lit <- mkStringCLit (closureValDescr cl_info)
501 return $ ProfilingInfo fd_lit ad_lit
502 else return $ ProfilingInfo (mkIntCLit 0) (mkIntCLit 0)
503 ; return (CmmInfo gc_target Nothing
504 (CmmInfoTable (isStaticClosure cl_info) prof cl_type info)) }
506 k_with_con_name con_info con info_lbl =
507 do cstr <- mkByteStringCLit $ dataConIdentity con
508 return $ con_info $ makeRelativeRefTo info_lbl cstr
509 cl_type = smRepClosureTypeInt (closureSMRep cl_info)
511 -- The gc_target is to inform the CPS pass when it inserts a stack check.
512 -- Since that pass isn't used yet we'll punt for now.
513 -- When the CPS pass is fully integrated, this should
514 -- be replaced by the label that any heap check jumped to,
515 -- so that branch can be shared by both the heap (from codeGen)
516 -- and stack checks (from the CPS pass).
517 -- JD: Actually, we've decided to go a different route here:
518 -- the code generator is now responsible for producing the
519 -- stack limit check explicitly, so this field is now obsolete.
522 -----------------------------------------------------------------------------
524 -- Info table offsets
526 -----------------------------------------------------------------------------
528 stdInfoTableSizeW :: WordOff
529 -- The size of a standard info table varies with profiling/ticky etc,
530 -- so we can't get it from Constants
531 -- It must vary in sync with mkStdInfoTable
533 = size_fixed + size_prof
535 size_fixed = 2 -- layout, type
536 size_prof | opt_SccProfilingOn = 2
539 stdInfoTableSizeB :: ByteOff
540 stdInfoTableSizeB = stdInfoTableSizeW * wORD_SIZE :: ByteOff
542 stdSrtBitmapOffset :: ByteOff
543 -- Byte offset of the SRT bitmap half-word which is
544 -- in the *higher-addressed* part of the type_lit
545 stdSrtBitmapOffset = stdInfoTableSizeB - hALF_WORD_SIZE
547 stdClosureTypeOffset :: ByteOff
548 -- Byte offset of the closure type half-word
549 stdClosureTypeOffset = stdInfoTableSizeB - wORD_SIZE
551 stdPtrsOffset, stdNonPtrsOffset :: ByteOff
552 stdPtrsOffset = stdInfoTableSizeB - 2*wORD_SIZE
553 stdNonPtrsOffset = stdInfoTableSizeB - 2*wORD_SIZE + hALF_WORD_SIZE
555 -------------------------------------------------------------------------
557 -- Accessing fields of an info table
559 -------------------------------------------------------------------------
561 closureInfoPtr :: CmmExpr -> CmmExpr
562 -- Takes a closure pointer and returns the info table pointer
563 closureInfoPtr e = CmmLoad e bWord
565 entryCode :: CmmExpr -> CmmExpr
566 -- Takes an info pointer (the first word of a closure)
567 -- and returns its entry code
568 entryCode e | tablesNextToCode = e
569 | otherwise = CmmLoad e bWord
571 getConstrTag :: CmmExpr -> CmmExpr
572 -- Takes a closure pointer, and return the *zero-indexed*
573 -- constructor tag obtained from the info table
574 -- This lives in the SRT field of the info table
575 -- (constructors don't need SRTs).
576 getConstrTag closure_ptr
577 = CmmMachOp (MO_UU_Conv halfWordWidth wordWidth) [infoTableConstrTag info_table]
579 info_table = infoTable (closureInfoPtr closure_ptr)
581 cmmGetClosureType :: CmmExpr -> CmmExpr
582 -- Takes a closure pointer, and return the closure type
583 -- obtained from the info table
584 cmmGetClosureType closure_ptr
585 = CmmMachOp (MO_UU_Conv halfWordWidth wordWidth) [infoTableClosureType info_table]
587 info_table = infoTable (closureInfoPtr closure_ptr)
589 infoTable :: CmmExpr -> CmmExpr
590 -- Takes an info pointer (the first word of a closure)
591 -- and returns a pointer to the first word of the standard-form
592 -- info table, excluding the entry-code word (if present)
594 | tablesNextToCode = cmmOffsetB info_ptr (- stdInfoTableSizeB)
595 | otherwise = cmmOffsetW info_ptr 1 -- Past the entry code pointer
597 infoTableConstrTag :: CmmExpr -> CmmExpr
598 -- Takes an info table pointer (from infoTable) and returns the constr tag
599 -- field of the info table (same as the srt_bitmap field)
600 infoTableConstrTag = infoTableSrtBitmap
602 infoTableSrtBitmap :: CmmExpr -> CmmExpr
603 -- Takes an info table pointer (from infoTable) and returns the srt_bitmap
604 -- field of the info table
605 infoTableSrtBitmap info_tbl
606 = CmmLoad (cmmOffsetB info_tbl stdSrtBitmapOffset) bHalfWord
608 infoTableClosureType :: CmmExpr -> CmmExpr
609 -- Takes an info table pointer (from infoTable) and returns the closure type
610 -- field of the info table.
611 infoTableClosureType info_tbl
612 = CmmLoad (cmmOffsetB info_tbl stdClosureTypeOffset) bHalfWord
614 infoTablePtrs :: CmmExpr -> CmmExpr
615 infoTablePtrs info_tbl
616 = CmmLoad (cmmOffsetB info_tbl stdPtrsOffset) bHalfWord
618 infoTableNonPtrs :: CmmExpr -> CmmExpr
619 infoTableNonPtrs info_tbl
620 = CmmLoad (cmmOffsetB info_tbl stdNonPtrsOffset) bHalfWord
622 funInfoTable :: CmmExpr -> CmmExpr
623 -- Takes the info pointer of a function,
624 -- and returns a pointer to the first word of the StgFunInfoExtra struct
625 -- in the info table.
626 funInfoTable info_ptr
628 = cmmOffsetB info_ptr (- stdInfoTableSizeB - sIZEOF_StgFunInfoExtraRev)
630 = cmmOffsetW info_ptr (1 + stdInfoTableSizeW)
631 -- Past the entry code pointer
633 -------------------------------------------------------------------------
635 -- Static reference tables
637 -------------------------------------------------------------------------
639 -- srtLabelAndLength :: C_SRT -> CLabel -> (CmmLit, StgHalfWord)
640 -- srtLabelAndLength NoC_SRT _
642 -- srtLabelAndLength (C_SRT lbl off bitmap) info_lbl
643 -- = (makeRelativeRefTo info_lbl $ cmmLabelOffW lbl off, bitmap)
645 -------------------------------------------------------------------------
647 -- Position independent code
649 -------------------------------------------------------------------------
650 -- In order to support position independent code, we mustn't put absolute
651 -- references into read-only space. Info tables in the tablesNextToCode
652 -- case must be in .text, which is read-only, so we doctor the CmmLits
653 -- to use relative offsets instead.
655 -- Note that this is done even when the -fPIC flag is not specified,
656 -- as we want to keep binary compatibility between PIC and non-PIC.
658 makeRelativeRefTo :: CLabel -> CmmLit -> CmmLit
660 makeRelativeRefTo info_lbl (CmmLabel lbl)
662 = CmmLabelDiffOff lbl info_lbl 0
663 makeRelativeRefTo info_lbl (CmmLabelOff lbl off)
665 = CmmLabelDiffOff lbl info_lbl off
666 makeRelativeRefTo _ lit = lit