-
Notifications
You must be signed in to change notification settings - Fork 182
Expand file tree
/
Copy pathIExpand.hs
More file actions
5704 lines (5164 loc) · 255 KB
/
Copy pathIExpand.hs
File metadata and controls
5704 lines (5164 loc) · 255 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances, RelaxedPolyRec, PatternGuards, ScopedTypeVariables #-}
{-# LANGUAGE BangPatterns #-}
-- Todo
-- * Use a set to keep track of variable values to handle x==c1 || x==c2
-- * Don't generate x!=0 && x!=1 && x!= 2 ...
-- Can be done by generating the minimal test for an interval after processing.
-- * Generate a single decoder instead of many comparisons for equality?
-- * Truncate bit extraction positions to minimum width.
-- * Inline bit extraction?
-- * if x 0 maxBound --> {n-bits}x
-- * if c 0 e --> {n-bits}c & e
-- * flatten & (in asyntax)?
-- * check for name conflict in the generated IEFace
module IExpand(iExpand) where
#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 804)
import Prelude hiding ((<>))
#endif
import Data.List
import Data.Maybe
import Data.Foldable(foldrM)
import Numeric(showIntAtBase)
import Data.Char(intToDigit, ord, chr)
import Control.Monad(when, foldM, zipWithM, mapAndUnzipM)
import Control.Monad.Fix(mfix)
--import Control.Monad.Fix
import Control.Monad.State(State, evalState, liftIO, get, put)
import Data.Graph
import System.IO(Handle, BufferMode(..), IOMode(..), stdout, stderr,
hSetBuffering, hIsOpen, hIsClosed)
import System.FilePath(isRelative)
import qualified Data.Array as Array
import qualified Data.IntMap as IM
import qualified Data.Map as M
import qualified Data.Set as S
import Debug.Trace(traceM)
import FileIOUtil(openFileCatch, hCloseCatch, hFlushCatch, hGetBufferingCatch,
hSetBufferingCatch, hPutStrCatch, hGetLineCatch,
hGetCharCatch, hIsEOFCatch, hIsReadableCatch,
hIsWritableCatch)
import GraphWrapper(tSortInt)
import IntegerUtil(mask)
import Util
import PFPrint
import IdPrint
import IntLit
import Undefined
import FStringCompat
import PreStrings(fsUnderscore)
import PreIds
import Flags
import SymTab(SymTab)
import Error(internalError, EMsg, ErrMsg(..), ErrorHandle,
recordHandleOpen, recordHandleClose)
import Position
import Id
import Backend
import Prim
import IPrims(doPrimOp)
import CSyntax
import qualified TIMonad as TM
import TypeCheck(topExpr)
import VModInfo
import Pragma
import Changed(changedOrId)
import ISyntax
import ISyntaxSubst(eSubst, eSubstBatch, tSubstBatch)
import IConv(iConvT, iConvExpr)
import ISyntaxUtil
import IExpandUtils
import Wires
import IWireSet
import qualified IfcBetterInfo as BetterInfo
import ITransform(iTransExpr)
import IOUtil(progArgs)
import ISyntaxXRef(mapIExprPosition)
import IStateLoc
-----------------------------------------------------------------------------
-- Trace Flags
-- doProfile
-- Keep a map of the number of times a top-level def is entered in evalAp
-- (the number of unfolding steps), indexed by the position of the call Id,
-- and display the map on each step warning and at the end of evaluation.
doProfile :: Bool
doProfile = elem "-trace-profile" progArgs
-- doDebug
-- A generic trace flag that has been added to over the years.
-- Includes some basic call flow at the top of evaluating a module,
-- and debug info when evaluating various structures and primitives.
doDebug :: Bool
doDebug = elem "-trace-debug" progArgs
-- doFunExpand
-- On evaluating a top-level function call, display the call.
-- If the flag is used twice, the result of evaluation is displayed.
doFunExpand, doFunExpand2 :: Bool
doFunExpand = elem "-trace-fun-expand" progArgs
doFunExpand2 = length (filter (== "-trace-fun-expand") progArgs) > 1
-- doConAp
-- Trace the entrance and exit to "conAp" (called when evaluating ICon)
-- and when that calls "bldAp'" or "bldApUH'" (rebuilding the original expr)
doConAp :: Bool
doConAp = elem "-trace-conAp" progArgs
-- doTrans
-- Trace the entrance and exit to iTransExpr (when iExpand resorts
-- to iTransform for optimization)
doTrans :: Bool
doTrans = elem "-trace-eval-trans" progArgs
-- doTraceClock
-- Trace the creation and analysis of Clock and Reset (and some Inout),
-- here and in IExpandUtils.
doTraceClock :: Bool
doTraceClock = elem "-trace-clock" progArgs
-- doTraceSteps
-- At the end of evaluation, display the total number of evaluation steps
-- (the number of times a top-level function was evaluated, including
-- when using a cached value)
doTraceSteps :: Bool
doTraceSteps = elem "-trace-eval-steps" progArgs
-- doTraceHeap
-- In IExpandUtils, trace when adding an expr to the heap or updating a
-- heap value. If the flag is used twice, the overwritten value is shown.
doTraceHeap :: Bool
doTraceHeap = elem "-trace-heap" progArgs
-- doTraceHeapAlloc
-- In IExpandUtils, trace when a heap cell is created.
-- And trace in "evalUH" when the value to be unheaped is a reference
-- (which is considered a "wasted re-heap").
doTraceHeapAlloc :: Bool
doTraceHeapAlloc = elem "-trace-heap-alloc" progArgs
-- doTraceHeapSize
-- At the end of evaluation, print the size of the heap (number of cells).
doTraceHeapSize :: Bool
doTraceHeapSize = elem "-trace-heap-size" progArgs
-- doTraceDefCache
-- In IExpandUtils, trace cache hit/miss for top-level functions.
doTraceDefCache :: Bool
doTraceDefCache = elem "-trace-def-cache" progArgs
-- doTraceCExprCache
-- In evalCExpr, trace cache hit/miss for dynamically evaluated CExprs
doTraceCExprCache :: Bool
doTraceCExprCache = elem "-trace-cexpr-cache" progArgs
-- doTraceNF
-- Trace the entrance and exit of "walkNF".
doTraceNF :: Bool
doTraceNF = elem "-trace-eval-nf" progArgs
-- doTracePortTypes
-- In IExpandUtils, trace the saving of port types, via the primitive and
-- for the module arguments and interface.
doTracePortTypes :: Bool
doTracePortTypes = elem "-trace-port-types" progArgs
-- doTraceIf
-- Trace the "doIf" and "improveIf" functions for evaluating if exprs.
doTraceIf :: Bool
doTraceIf = elem "-trace-eval-if" progArgs
-- doTraceTypes
-- Add a check that the type of an expression before and after evaluation
-- (by "evalAp") doesn't match, with an internal error if not.
doTraceTypes :: Bool
doTraceTypes = elem "-trace-eval-types" progArgs
-- doTraceLoc
-- Here and in IExpandUtil, trace some operations on IStateLoc, such as
-- adding hierarchy for instantiation, rules, and leaf modules.
doTraceLoc :: Bool
doTraceLoc = elem "-trace-state-loc" progArgs
-- doDebugFreeVars
-- Add a check that expressions added to the heap do not have free variables,
-- with an internal error if so.
doDebugFreeVars :: Bool
doDebugFreeVars = elem "-debug-eval-free-vars" progArgs
-- Trace batched substitutions in IExpand
doTraceExpandBatchSubst :: Bool
doTraceExpandBatchSubst = elem "-trace-expand-batch-subst" progArgs
-- We need to set the buffering of stdout and stderr
-- if any trace is on (including a trace only used in IExpandUtils)
-- so make sure that all trace flags are included in this list
doAnyTrace :: Bool
doAnyTrace = doProfile ||
doDebug ||
doFunExpand ||
doConAp ||
doTrans ||
doTraceClock ||
doTraceSteps ||
doTraceHeap ||
doTraceHeapAlloc ||
doTraceHeapSize ||
doTraceDefCache ||
doTraceCExprCache ||
doTraceNF ||
doTracePortTypes ||
doTraceIf ||
doTraceTypes ||
doTraceLoc ||
doTraceExpandBatchSubst ||
doDebugFreeVars
-- Before we had attributes for controlling whether input clocks have gates,
-- these backdoor flags were used to add gates.
-- XXX These can probably be retired now
gateClockInputs :: Bool
gateClockInputs = elem "-hack-gate-clock-inputs" progArgs
gateDefaultClock :: Bool
gateDefaultClock = elem "-hack-gate-default-clock" progArgs
iExpandPref :: String
iExpandPref = "__h"
-----------------------------------------------------------------------------
-- iExpand
-- Elaborate a toplevel module definition (for which RTL is being generated).
-- Heap references become local defs in the module (named with "__h"),
-- except when they are simple enough to be inlined.
-- The actual elaboration work is done by iExpandModuleDef
iExpand :: ErrorHandle -> Flags ->
SymTab -> M.Map Id HExpr ->
IATFCache ->
Bool -> [PProp] -> HDef ->
IO (IModule HeapData)
iExpand errh flags symt alldefs atf_cache is_noinlined_func pps def@(IDef mi _ _ _) = do
-- unbuffer output if we're tracing
when (doAnyTrace || showElabProgress flags) $
do hSetBuffering stdout LineBuffering
hSetBuffering stderr LineBuffering
-- execute the static elaboration
-- go is of type GOutput X, where X is the large tuple output of iExpandModuleDef
go <- runG errh flags symt alldefs atf_cache mi is_noinlined_func pps $
iExpandModuleDef def
-- trace the steps and heap size
when doTraceSteps $ putStrLn ("expansion steps: " ++ (show (go_steps go)))
when doTraceHeapSize $ putStrLn ("heap size: " ++ (show (go_hp go)))
let (iks, args, varginfo, ifc) = goutput go
let rules = go_rules go
let insts = go_state_vars go
let vclockinfo = go_vclockinfo go
let vresetinfo = go_vresetinfo go
chkIfcPortNames errh args ifc vclockinfo vresetinfo
let norm = fullTypeNormalizer flags symt $ go_atfCache go
-- turn heap into IDef definitions
let
-- collect a map of the heap pointers reachable from insts, rules, and ifc
-- these will become defs in the resulting IModule
-- Position information has been clobbered in iheap
iheap = collPtrs (insts, rules, ifc) IM.empty
-- a list of just the pointers
ptrs0 = IM.keys iheap
-- CSE the pointers and return a map from old pointers to the remaining
-- canonical ones. The pointers are returned in tsorted order.
-- The tsort does a non-circularity check, which is a property we
-- expect in IModule, but the function "pDef" below also relies on it
-- (since "pDef" and "m" are recursively built)
(tsorted_cse_ptrs, ptr_map) = eqPtrs iheap ptrs0
-- function for translating old pointers to new ones
ptran p = IM.findWithDefault p p ptr_map
-- from a heap pointer, get the expression and its name info
-- Note that wire info in dropped. In the MCD merge (rev 6456),
-- Ravi considered a new IEDef type that would contain the wires
-- but that would require updating ITransform to handle it.
heapOf :: Int -> (HExpr, NameInfo)
heapOf p = case (IM.lookup p iheap) of
Just (HNF { hc_pexpr = P _ e, hc_name = name })
-> (e, legalizeNameInfo name)
hres -> internalError ("heapOf " ++ ppReadable (p, hres))
pDef :: HeapPointer -> (HExpr, Maybe HDef)
pDef p0 =
let -- translate the old pointer to the new pointer
p = ptran p0
-- get the expression and its name info
(e, expr_name) = heapOf p
-- the type for the new IDef being created
t = iGetTypeNorm norm e
-- the name for the new IDef being created
i = case expr_name of
Just name ->
setKeepId $
mkIdPost name (mkFString (iExpandPref ++ show p))
_ ->
setBadId $
mkId noPosition (mkFString (iExpandPref ++ show p))
-- update the body of the IDef that we're creating
-- (this is recursive, since "m" is built with "pDef")
e' = hToDef m e
-- whether the expression is "simple" and therefore doesn't
-- need to be lifted to an IDef
simple (ICon _ _) = True
simple _ = False
in
-- return the expression that should replace the heap pointer,
-- and maybe a Def, if the expression is a def reference
if simple e || isActionType t || isPairType t then
-- inline the expression, no def is created for this heap ptr
(e', Nothing)
else
-- assign the expr to a def, and replace the ptr reference
-- with a module def reference (which is what ICValue is)
(ICon i (ICValue t e'), Just (IDef i t e' []))
-- a map from the new pointers to their expressions
-- Actually, a map to a pair of an expression and maybe an IDef;
-- if the expr is a def reference, the maybe contains the def.
ptr_info = [ (p, pDef p) | p <- tsorted_cse_ptrs ]
-- a lookup function for the "ptr_info" map,
-- returning just the expression to replace the ptr reference
ptr_expr_map = IM.fromList [(p, e) | (p, (e, _)) <- ptr_info ]
mget p = fromJustOrErr "iExpand.mget" (IM.lookup p ptr_expr_map)
-- a map that is used by "hToDef" to convert expressions,
-- replacing the old pointers with the expressions of the new pointers
m = IM.fromList [(p, mget (ptran p)) | p <- ptrs0 ]
-- replaces pointers in the insts, rules, and ifcs
insts' = hToDef m insts
rules_heap = hToDef m rules
ifc' = hToDef m ifc
-- the final defs for the IModule
-- (the entries in "ptr_info" that are not inlined, and contain a def)
defs1 = [ d | (_, (_, Just d)) <- ptr_info ]
-- XXX the rules names should be constructed properly
-- XXX to begin with rather than fixed up at the end
rules_cleanup = cleanupFinalRules flags rules_heap
mpdefs = generateMethodPreds ( methodConditions flags ) rules_cleanup
defs = defs1 ++ mpdefs
wi = WireInfo (go_vclockinfo go) (go_vresetinfo go) varginfo
comments_map = go_comments_map go
mbe = if (go_backend_specific go)
then Just $ fromJustOrErr "iExpand: backend" (backend flags)
else Nothing
-- the expanded module
imod0 = IModule mi is_noinlined_func mbe wi [] iks args
(go_clock_domains go) (go_resets go)
insts' (go_port_types go)
defs rules_cleanup ifc'
(go_ffcallNo go) comments_map
-- if preserving method conditions, then remove the book-keeping info
imod = removeInlinedPositions flags imod0
--let traceMethodPred (IDef i _ p props) =
-- traceM ("Def: " ++ ppReadable (i, getPosition i, getIdProps i, p, props))
--mapM_ traceMethodPred mpdefs
when doDebug $ do
traceM ("iExpand done\n" ++ ppReadable (sort (IM.toList iheap))
++ ppReadable rules
++ ppReadable ifc)
traceM ("iExpand done mod\n" ++ ppReadable imod)
when doProfile $
traceM ("iExpand profile\n" ++ ppReadable (go_profile go))
return imod
generateMethodPreds :: forall a . Bool -> IRules a -> [IDef a]
generateMethodPreds flag (IRules _ rs) =
if (not flag) then [] else concatMap one_rule rs where
one_rule :: IRule a -> [IDef a]
one_rule r = gen_defs (irule_name r) $ collect_ifs $ irule_body r
collect_ifs :: IExpr a ->
[(IExpr a, IExpr a)] -- pred, meth call
collect_ifs e = -- trace ("collect_ifs " ++ (show e) ) $
collect_ifs' e
collect_ifs' :: IExpr a ->
[(IExpr a, IExpr a)] -- pred, meth call
collect_ifs' (IAps (ICon _ (ICPrim { primOp = PrimIf })) _ [cnd, thn, els]) =
let true_branch = collect_ifs thn
false_branch = collect_ifs els
in ( map (add_to_pred cnd) true_branch) ++ (map (add_to_pred (ieNot cnd)) false_branch)
collect_ifs' (IAps (ICon _ (ICPrim { primOp = PrimCase }))
[sz_idx, elem_ty] (idx:dflt:ces)) =
-- XXX if the arms are not overlapping, we can simplify the conditions
let foldFn (v, e) false_branch =
let true_branch = collect_ifs e
c = iePrimEQ sz_idx idx v
in (map (add_to_pred c) true_branch) ++
(map (add_to_pred (ieNot c)) false_branch)
in foldr foldFn (collect_ifs dflt) (makePairs ces)
collect_ifs' (IAps (ICon i_sel (ICPrim { primOp = PrimArrayDynSelect }))
[elem_ty, sz_idx] [arr, idx]) =
case arr of
(IAps (ICon _ (ICPrim { primOp = PrimBuildArray })) _ es) ->
let pos = getPosition i_sel
ty_idx = aitBit sz_idx
mapFn (n, e) =
let n_lit = iMkLitAt pos ty_idx n
c = iePrimEQ sz_idx idx n_lit
in map (add_to_pred c) (collect_ifs e)
in concatMap mapFn (zip [0..] es)
_ -> internalError ("collect_ifs': PrimArrayDynSelect: " ++
ppReadable arr)
collect_ifs' (IAps (ICon join (ICPrim{ primOp = op})) _t es)
| join == idPrimJoinActions || op == PrimJoinActions
= concatMap collect_ifs es -- search further for unlifted Ifs inside an action block
collect_ifs' e@(IAps (ICon _method (ICSel { })) _t
((ICon _state (ICStateVar { })):_methodargs)) =
[(iTrue,e)] -- base case for unpack_method_call
collect_ifs' (IAps (ICon _ (ICPrim _ pi)) _t [e]) | isIfWrapper pi
= collect_ifs e
-- need to recurse further into e? XXX
collect_ifs' e = [(iTrue,e)]
add_to_pred :: IExpr a -> (IExpr a, IExpr a) -> (IExpr a, IExpr a)
add_to_pred new (old, action) = (ieAndOpt new old, action)
-- tiny state monad to get unique numbers. This is a lazy
-- hack to avoid name collisions. It is still
-- theoretically possible to get name collisions.
nextNumber :: State Integer Integer
nextNumber = do
n <- get
put $ n+1
return n
gen_defs :: Id -> [(IExpr a, IExpr a)] -> [IDef a]
gen_defs rulename predmethods = concat $ evalState (mapM (makeDef rulename) predmethods ) 1
makeDef :: Id -> (IExpr a, IExpr a) -> State Integer [IDef a]
makeDef rulename (predicate, expr)
| not $ isTrue predicate =
-- trace ("now evaluating " ++ ppReadable rulename ++ " " ++ ppReadable (predicate, expr)
-- ++ show predicate ++ (show expr)
-- ) $
mapM (onepred rulename predicate) (unpack_method_call expr)
makeDef _ _ = return [] -- isTrue, for actions with no conditions
onepred :: Id -> IExpr a -> (Id, Id) -> State Integer (IDef a)
onepred rulename predicate (state, method) = do
-- traceM $ "onepred " ++ ppReadable (predicate, state, method)
uniquenum <- nextNumber
let poss = case (getIdInlinedPositions method) of
Nothing ->
--trace("no inlined position: " ++ ppReadable (state, method)) $
[getIdPosition state]
Just jposs ->
--trace("inlined positions: " ++ ppReadable (state, method, jposs)) $
-- preserve all the positions,
jposs
let i = makename rulename state method uniquenum poss
return $ IDef i itBit1 predicate
[ DefP_NoCSE
-- ^ put this first as an optimization for the calls to elem in defPropsHasNoCSE
, DefP_Rule rulename
, DefP_Instance state
, DefP_Method method
]
makename :: Id -> Id -> Id -> Integer -> [Position] -> Id
makename rule state method num poss =
-- COND_<rule>_<state>_<method>_<uniqueID>
let fstr = concatFString [ mkFString "COND", fsUnderscore,
getIdBase rule, fsUnderscore,
getIdBase state, fsUnderscore,
getIdBase method, fsUnderscore,
mkFString $ show num ]
props = [ IdPMethodPredicate,
IdP_keep,
IdP_keepEvenUnused,
IdPInlinedPositions poss ]
in
addIdProps (mkId noPosition fstr) props
unpack_method_call :: IExpr a -> [(Id,Id)]
unpack_method_call e = -- trace ("unpack_method_call " ++ (show e)) $
unpack_method_call' [] e
-- This accumulates a list of positions along the way, to support "avAction_"
-- wrappers; in some places we sanity check that it's an empty list, because
-- positions are not expected to be accumulated in those situations.
unpack_method_call' :: [Position] -> IExpr a -> [(Id,Id)]
-- no action
unpack_method_call' _ (ICon _ (ICPrim { primOp = PrimNoActions })) =
[]
-- action method call
unpack_method_call' poss e@(IAps (ICon i_method (ICSel { })) _ts
((ICon i_state (ICStateVar { })):_methodargs))
= let i_method' = addIdInlinedPositions i_method poss
in [(i_state, i_method')]
-- Most action have methods have an "avAction_" wrapper, but some do not
-- (Reg write does not -- is the difference Classic vs BSV?)
unpack_method_call' poss (IAps (ICon i_av (ICSel { })) _ts [e])
| (i_av == idAVAction_)
= let av_poss = fromMaybe [] $ getIdInlinedPositions i_av
poss' = av_poss ++ poss
in unpack_method_call' poss' e
-- multiple actions
unpack_method_call' poss (IAps (ICon _ (ICPrim { primOp = PrimJoinActions }))
_ts es) =
case poss of
[] -> concatMap unpack_method_call es
_ -> internalError ("unpack_method_call': JoinActions: " ++
ppReadable poss)
-- function with arguments (such as $display)
unpack_method_call' poss (IAps (ICon i_function _) _ts _es) =
let i_function' = addIdInlinedPositions i_function poss
in [(mk_homeless_id "FUNCTION", i_function')]
-- function of no arguments
unpack_method_call' poss (ICon i_function _) =
let i_function' = addIdInlinedPositions i_function poss
in [(mk_homeless_id "FUNCTION", i_function')]
-- ^seen in Sudoku with high order functional programming (displayGrid)
unpack_method_call' _ e =
internalError("unpack_method_call': unknown: " ++ ppReadable e)
-- trace ("unpack_method_call unable to match " ++ show e) $
--[(mk_homeless_id "NOSTATE", mk_homeless_id "NOMETHOD")]
-- Apply 'removeIdInlinedPositions' to the Id of every ICon reachable
-- in the IModule. Heap references are leaves: IRefT payloads and the
-- cells of ICLazyArray sit behind IORefs and are not traversed.
-- Clock, reset and inout wires can be cyclic (a state variable's
-- output clock selects from the state variable itself), so the
-- rebuilding must stay lazy; consumers only force finite prefixes of
-- such wires. Subtrees that cannot contain an ICon are returned
-- unchanged, rather than reallocated.
removeInlinedPositions :: Flags -> IModule HeapData -> IModule HeapData
removeInlinedPositions flags imod0 | (not (methodConditions flags)) = imod0
removeInlinedPositions flags imod0 =
imod0 { imod_clock_domains =
[ (d, map rmClock cs) | (d, cs) <- imod_clock_domains imod0 ],
imod_resets = map rmReset (imod_resets imod0),
imod_state_insts =
[ (i, rmStateVar sv) | (i, sv) <- imod_state_insts imod0 ],
imod_local_defs = map rmDef (imod_local_defs imod0),
imod_rules = rmRules (imod_rules imod0),
imod_interface = map rmIFace (imod_interface imod0)
}
where
rmExpr :: HExpr -> HExpr
rmExpr (ILam i t e) = ILam i t (rmExpr e)
rmExpr (IAps f ts es) = IAps (rmExpr f) ts (map rmExpr es)
rmExpr e@(IVar _) = e
rmExpr (ILAM i k e) = ILAM i k (rmExpr e)
rmExpr (ICon i ic) = ICon (removeIdInlinedPositions i) (rmConInfo ic)
rmExpr e@(IRefT _ _ _ _) = e
-- only the payloads that can carry an IExpr are rebuilt;
-- all other constructors are returned unchanged
rmConInfo :: IConInfo HeapData -> IConInfo HeapData
rmConInfo (ICDef t d) = ICDef t (rmExpr d)
rmConInfo (ICUndet t k mv) = ICUndet t k (fmap rmExpr mv)
rmConInfo (ICStateVar t sv) = ICStateVar t (rmStateVar sv)
rmConInfo (ICValue t d) = ICValue t (rmExpr d)
rmConInfo (ICMethod t ins outs m) = ICMethod t ins outs (rmExpr m)
rmConInfo (ICClock t c) = ICClock t (rmClock c)
rmConInfo (ICReset t r) = ICReset t (rmReset r)
rmConInfo (ICInout t io) = ICInout t (rmInout io)
rmConInfo (ICLazyArray t arr mu) =
-- the array cells are heap references, thus leaves
ICLazyArray t arr (fmap (\ (e1, e2) -> (rmExpr e1, rmExpr e2)) mu)
rmConInfo (ICPred t p) = ICPred t (rmPred p)
rmConInfo ic@(ICPrim {}) = ic
rmConInfo ic@(ICForeign {}) = ic
rmConInfo ic@(ICCon {}) = ic
rmConInfo ic@(ICIs {}) = ic
rmConInfo ic@(ICOut {}) = ic
rmConInfo ic@(ICTuple {}) = ic
rmConInfo ic@(ICSel {}) = ic
rmConInfo ic@(ICVerilog {}) = ic
rmConInfo ic@(ICInt {}) = ic
rmConInfo ic@(ICReal {}) = ic
rmConInfo ic@(ICString {}) = ic
rmConInfo ic@(ICChar {}) = ic
rmConInfo ic@(ICHandle {}) = ic
rmConInfo ic@(ICMethArg {}) = ic
rmConInfo ic@(ICModPort {}) = ic
rmConInfo ic@(ICModParam {}) = ic
rmConInfo ic@(ICIFace {}) = ic
rmConInfo ic@(ICRuleAssert {}) = ic
rmConInfo ic@(ICSchedPragmas {}) = ic
rmConInfo ic@(ICName {}) = ic
rmConInfo ic@(ICAttrib {}) = ic
rmConInfo ic@(ICPosition {}) = ic
rmConInfo ic@(ICType {}) = ic
rmClock :: HClock -> HClock
rmClock c = c { ic_wires = rmExpr (ic_wires c) }
rmReset :: HReset -> HReset
rmReset r = r { ir_clock = rmClock (ir_clock r),
ir_wire = rmExpr (ir_wire r) }
rmInout :: HInout -> HInout
rmInout io = io { io_clock = rmClock (io_clock io),
io_reset = rmReset (io_reset io),
io_wire = rmExpr (io_wire io) }
rmStateVar :: HStateVar -> HStateVar
rmStateVar sv =
sv { isv_iargs = map rmExpr (isv_iargs sv),
isv_clocks = [ (i, rmClock c) | (i, c) <- isv_clocks sv ],
isv_resets = [ (i, rmReset r) | (i, r) <- isv_resets sv ] }
rmPred :: HPred -> HPred
rmPred (PConj ps) = PConj (S.map rmPTerm ps)
rmPTerm :: PTerm HeapData -> PTerm HeapData
rmPTerm (PAtom e) = PAtom (rmExpr e)
rmPTerm (PIf c t e) = PIf (rmExpr c) (rmPred t) (rmPred e)
rmPTerm (PSel idx sz ps) = PSel (rmExpr idx) sz (map rmPred ps)
rmDef :: HDef -> HDef
rmDef (IDef i t e p) = IDef i t (rmExpr e) p
rmRules :: HRules -> HRules
rmRules (IRules sps rs) = IRules sps (map rmRule rs)
rmRule :: HRule -> HRule
rmRule r = r { irule_pred = rmExpr (irule_pred r),
irule_body = rmExpr (irule_body r) }
rmIFace :: HEFace -> HEFace
rmIFace ief =
ief { ief_value = fmap (\ (e, t) -> (rmExpr e, t)) (ief_value ief),
ief_body = fmap rmRules (ief_body ief) }
-- -----
-- After the evaluator has run and the heap pointers have been collected,
-- this code does CSE and tsorts the defs, in preparation for converting
-- to IDefs in the resulting IModule.
-- eqPtrs has two outputs:
-- 1. a tsorted list of the heap pointers (used to build the output module)
-- 2. a heap-reference to heap-reference map that seems to be used to do
-- some sort of heap compression and/or CSE (is this still useful)?
eqPtrs :: IM.IntMap HeapCell -> [HeapPointer] ->
([HeapPointer], IM.IntMap HeapPointer)
eqPtrs heap ptrs =
let getHeapCell p = case (IM.lookup p heap) of
Just cell -> cell
Nothing -> internalError("eqPtrs.getHeapCell " ++ ppReadable p)
heapOf p =
case (getHeapCell p) of
HNF { hc_pexpr = P _ e } -> e
e -> internalError ("eqPtrs.heapOf " ++ ppReadable e)
hptrs (IAps f _ es) = foldr (union . hptrs) [] (f:es)
hptrs (ICon _ (ICStateVar { iVar = IStateVar { isv_iargs = es } }))
= foldr (union . hptrs) [] es
hptrs (IRefT _ p _ _) = [p]
hptrs _ = []
g = [(p, hptrs (heapOf p)) | p <- ptrs ]
-- tSortInt (Data.Graph), not SCC.tsort: the two sorts break ties
-- between independent nodes differently, and the tie order decides
-- which of two equal-expression heap cells pass 1 makes canonical
-- -- visibly so when one twin is named and one is not (the named
-- canonical becomes a Bluesim-symbol-table/VCD-visible def).
-- Keep upstream's sort so pass-1 canonical choice matches upstream;
-- pass 2 below then only improves named-vs-named choices.
ptrs' = case tSortInt g of
Left iss -> internalError ("eqPtrs: circular: " ++ ppReadable iss ++ "\n" ++
(concatMap (ppReadable . heapCellToHExpr . getHeapCell)
(concatMap id iss)))
Right ps -> ps
step p (!dsm, !ptrm) =
let e = sub (heapOf p)
sub (IAps f ts es) = IAps (sub f) ts (map sub es)
sub e@(IRefT t i _ _) =
case IM.lookup i ptrm of
Nothing -> e
-- hd_ref errors because we don't use it once we have the iheap
Just i' -> IRefT t i' (internalError "eqPtrs ref") (internalError "eqPtrs ref")
sub e = e
in case M.lookup e dsm of
Nothing -> (M.insert e p dsm, ptrm)
Just h -> (dsm, IM.insert p h ptrm)
(dsm0, ptrm0) = foldl' (flip step) (M.empty, IM.empty) ptrs'
-- Pass 2: pick the best-named pointer per equivalence class as
-- the canonical, instead of whichever pointer pass-1 happened to
-- see first. Uses the same Id-quality preference as
-- ITransform.rename_map (see Id.idQuality).
--
-- The class is stored as a Set of (quality, ptr) so S.findMax
-- returns the best canonical in O(log n).
rankOf p = idQuality (hc_name (getHeapCell p))
-- Reverse ptrm0: canonical -> set of (rank, redirected pointer)
revPtrm :: IM.IntMap (S.Set (Int, HeapPointer))
revPtrm = IM.foldlWithKey'
(\m src dst ->
let !cls = S.insert (rankOf src, src)
(IM.findWithDefault S.empty dst m)
in IM.insert dst cls m)
IM.empty ptrm0
-- Visibility guard: only re-pick among visibly-named pointers.
-- pDef hides nameless and bad-named defs but keeps well-named
-- ones, so promoting a well-named pointer over a hidden pass-1
-- canonical would flip the def from hidden to visible in the
-- Bluesim symbol table and VCD. A hidden canonical therefore
-- stays canonical; pass 2 may only improve WHICH visible name
-- is used, never create a newly-visible def.
improve e c0 (!dsm, !ptrm) | rankOf c0 < 1 = (dsm, ptrm)
improve e c0 (!dsm, !ptrm) =
let cls = S.insert (rankOf c0, c0)
(IM.findWithDefault S.empty c0 revPtrm)
(_, c) = S.findMax cls
in if c == c0
then (dsm, ptrm)
else ( M.insert e c dsm
, IM.delete c $
S.foldl' (\m (_, p) -> IM.insert p c m) ptrm cls )
(dsm, ptrm) = M.foldlWithKey'
(\acc e c0 -> improve e c0 acc)
(dsm0, ptrm0)
dsm0
in --traces (show (length ptrs, length (M.elems dsm))) $
--traces (show (IM.toList ptrm)) $
--traces (show (M.elems dsm)) $
(M.elems dsm, ptrm)
-----------------------------------------------------------------------------
-- convert top level module definition for which code is being generated
iExpandModuleDef ::
HDef ->
G ([(Id, IKind)], [IAbstractInput], [VArgInfo], [IEFace HeapData])
iExpandModuleDef (IDef i t e _) = do
-- this was to allow code-gen of modules with numeric parameters,
-- but we don't support that, so set the list of such params to null
let iks = []
when doDebug $ traceM "iExpandModuleDef: expanding"
let user_mod_name = init (getIdBaseString i)
showTopProgress ("Elaborating module " ++ quote user_mod_name)
let mod_id = i
mod_pos = getPosition i
-- create the initial scope for scheduling attributes
-- (putting the top-level method names into scope)
pushTopModuleSchedNameScope t
fmod <- isNoInlinedFunc
pps <- getPragmas
(clkRst, default_args, default_vargs) <-
if (fmod)
then return ((missingDefaultClock, missingDefaultReset), [], [])
else do
let def_clk_id = setIdPosition mod_pos idDefaultClock
def_rst_id = setIdPosition mod_pos idDefaultReset
has_clk = hasDefaultClk pps
has_rst = hasDefaultRst pps
gated = isGatedDefaultClk pps ||
-- continue to consult the trace flag
gateDefaultClock
(topClk, clockargs, vclkargs) <-
if has_clk
then do (c, a, v) <- makeInputClk gated def_clk_id
return (c, [a], [v])
else return (missingDefaultClock, [], [])
-- associate the default reset with the default clock
let reset_clock_family = if has_clk
then (Just def_clk_id)
else Nothing
(topRstn, resetargs, vrstargs) <-
if has_rst
then do (r, a, v) <- makeInputRstn def_rst_id reset_clock_family
return (r, [a], [v])
else return (missingDefaultReset, [], [])
return ((topClk, topRstn), clockargs ++ resetargs, vclkargs ++ vrstargs)
-- iExpandModuleLam elaborates the actual module
(args, vargs, (P p_ifc ifc)) <- iExpandModuleLam i clkRst e
showTopProgress "Elaborating interface"
pushIfcSchedNameScope
ifc' <- addPredG p_ifc (do (_, ifc_uh) <- evalUH ifc; return ifc_uh)
-- iExpandIface:
-- turns a struct of ILams into IEFace: evaluates method bodies and takes
-- the methods out of the interface struct
ifcs <- iExpandIface mod_id clkRst ifc'
popIfcSchedNameScope
let args' = args ++ default_args
let vargs' = vargs ++ default_vargs
-- XXX Assert here that args' and vargs' are the same length?
chkInputClockPragmas vargs'
showTopProgress ("Finished elaborating module " ++ quote user_mod_name)
return (iks, args', vargs', ifcs)
-- ----------
-- Elaborate a (top-level) module with arguments
iExpandModuleLam :: Id -> (HClock, HReset) -> HExpr
-> G ([IAbstractInput], [VArgInfo], PExpr)
iExpandModuleLam i clkRst e = do
-- the separation of the core expression from the ILam
-- relies on the property that none of the ILam ids are the same
-- (so the substitution is safe to do on just the core expression)
-- (alternatively, we could have created an iExpandModuleArgsWith
-- function which takes a function for clocks or resets or ports
-- and walks the IExpr each time)
-- evaluate the initial expression to resolve top-level
-- dictionary bindings that weren't inlined
e0 <- evaleUH e
(its, mod_expr0) <- extractModuleArgs i e0
-- use Either type to record the progress
let as0 = map Right its
-- process clocks
(as1, mod_expr1) <- iExpandModuleClockArgs i as0 mod_expr0
-- process resets
(as2, mod_expr2) <- iExpandModuleResetArgs i as1 mod_expr1
-- process inouts
(as3, mod_expr3) <- iExpandModuleInoutArgs i clkRst as2 mod_expr2
-- process ifc args
(as4, mod_expr4) <- --iExpandModuleIfcArgs i as3 mod_expr3
return (as3, mod_expr3)
-- process remaining args
(as5, mod_expr5) <- iExpandModulePortArgs i clkRst as4 mod_expr4
-- extract the results
let mod_expr = mod_expr5
let (absinps, vargs) =
case (separate as5) of
(processed_as, []) -> unzip processed_as
(_, unprocessed_as) ->
internalError ("iExpandModuleLam: unprocessed args: " ++
ppReadable unprocessed_as)
-- elaborate the module body
e' <- iExpandModule False clkRst [] pTrue mod_expr
-- return the results
return (absinps, vargs, e')
type ArgState = Either (IAbstractInput, VArgInfo) (Id, IType)
extractModuleArgs :: Id -> HExpr -> G ([(Id, IType)], HExpr)
extractModuleArgs i e@(ILAM {}) =
-- If a module to be generated has type parameters, then it means that
-- an argument to the module was not bitifiable (and thus typechecking
-- left in a proviso, which was unsatisfied). Report it as an error.
errG (reportNonSynthTypeInModuleArg i e)
extractModuleArgs i
lam@(ILam li t@(ITCon ti _ (TIstruct SInterface{} fs)) e) = do
-- This should not be reached, since GenWrap now reports this error
errG (getIExprPosition lam, EInterfaceArg (pfpString i))
extractModuleArgs i (ILam li t e) = do
let a = (li, t)
(as, e') <- extractModuleArgs i e
return (a:as, e')
extractModuleArgs i (IAps (ICon _ (ICPrim _ PrimPoisonedDef)) _ _) =
-- report without package qualifier to match other extractModuleArgs errors
errG (getIdPosition i, EPoisonedDef (text (init (getIdBaseString i))))
extractModuleArgs i e = return ([], e)
iExpandModuleClockArgs :: Id -> [ArgState] -> HExpr -> G ([ArgState], HExpr)
iExpandModuleClockArgs i ((Right (clk_id,t)):as) e | t == itClock = do
when (clk_id == emptyId) $
internalError ("iExpandModuleClockArgs: empty clock argument name")
-- XXX reserved clocks should be prohibited earlier
when ((clk_id == idDefaultClock) || (getIdString clk_id == "no_clock")) $
errG (getPosition clk_id, EReservedClock (getIdString clk_id))
pps <- getPragmas
let gated = isGatedInputClk pps (unQualId clk_id) ||
-- continue to consult the trace flag
gateClockInputs
(clock, abs_input, varginfo) <- makeInputClk gated clk_id
let a' = Left (abs_input, varginfo)
let e' = eSubst clk_id (icClock i clock) e
(as', e'') <- iExpandModuleClockArgs i as e'
return (a':as', e'')
iExpandModuleClockArgs i (a:as) e = do
(as', e') <- iExpandModuleClockArgs i as e
return (a:as', e')
iExpandModuleClockArgs i [] e = return ([], e)
iExpandModuleResetArgs :: Id -> [ArgState] -> HExpr ->
G ([ArgState], HExpr)
iExpandModuleResetArgs i ((Right (rst_id,t)):as) e | t == itReset = do
when (rst_id == emptyId) $
internalError ("iExpandModuleResetArgs: empty reset argument name")
-- XXX reserved resets should be prohibited earlier
when ((rst_id == idDefaultReset) || (getIdString rst_id == "no_reset")) $
errG (getPosition rst_id, EReservedReset (getIdString rst_id))
pps <- getPragmas
-- find the associated reset for the port, from any attributes
-- (no_clock is assumed if no attributes are given, and the clock
-- will be derived from the uses of the reset)
let (has_attr, mclk_name) = findModArgClockedByAttr pps rst_id
(reset, abs_input, varginfo) <- makeInputRstn rst_id mclk_name
-- if the user specified a clock_by attribute, then mark that clock domain
-- in the deriving information (we don't want deriving to give it a domain)
when (has_attr) $ let cd = getClockDomain (getResetClock reset)
in setInputResetClockDomain reset cd
let a' = Left (abs_input, varginfo)
let e' = eSubst rst_id (icReset i reset) e
(as', e'') <- iExpandModuleResetArgs i as e'
return (a':as', e'')
iExpandModuleResetArgs i (a:as) e = do
(as', e') <- iExpandModuleResetArgs i as e
return (a:as', e')
iExpandModuleResetArgs i [] e = return ([], e)
iExpandModuleInoutArgs :: Id -> (HClock, HReset) -> [ArgState] -> HExpr
-> G ([ArgState], HExpr)
iExpandModuleInoutArgs i clkRst ((Right (iot_id,t@(ITAp tc (ITNum sz)))):as) e
| tc == itInout_ = do
-- find the associated clock/reset for the port, from any attributes
(mclk_name, mrst_name) <- findModArgClockReset clkRst iot_id
(inout, abs_input, varginfo) <- makeArgInout iot_id sz mclk_name mrst_name
let a' = Left (abs_input, varginfo)
let e' = eSubst iot_id (icInout iot_id sz inout) e
(as', e'') <- iExpandModuleInoutArgs i clkRst as e'
return (a':as', e'')
iExpandModuleInoutArgs i clkRst ((Right (iot_id,(ITAp tc _)):as)) e
| tc == itInout_ = do
internalError("IexpandmoduleInoutArgs")
iExpandModuleInoutArgs i clkRst (a:as) e = do
(as', e') <- iExpandModuleInoutArgs i clkRst as e
return (a:as', e')
iExpandModuleInoutArgs i clkRst [] e = return ([], e)
-- module arguments which are not clock, reset, or ifc arg
iExpandModulePortArgs :: Id -> (HClock, HReset) -> [ArgState] -> HExpr ->
G ([ArgState], HExpr)
iExpandModulePortArgs i clkRst ((Right (li,t)):as) e = do
-- turn the argument into an ICModPort (module port), or into an
-- ICModParam if the user specified that it be a module parameter
pps <- getPragmas
let isParam = isParamModArg pps li
arg_id = if isParam
then makeArgParamId pps li
else makeArgPortId pps li
arg_str = getIdBaseString arg_id
iconinfo = if isParam
then ICModParam t
else ICModPort t
e'= eSubst li (ICon arg_id iconinfo) e
when (isParamOnlyType t && not isParam) $
deferErrors [(getPosition li, EParamOnlyType (getIdBaseString li) (pfpString t))]
-- find the associated clock/reset for the port, from any attributes
(mclk_name, mrst_name) <-
if (isParam)
then return (Nothing, Nothing)
else findModArgClockReset clkRst arg_id
let vname = VName arg_str
-- XXX support VeriPortProps on arguments
varginfo = if isParam
then Param vname
else Port (vname,[]) mclk_name mrst_name
abs_input = IAI_Port (arg_id, t)
let a' = Left (abs_input, varginfo)
(as', e'') <- iExpandModulePortArgs i clkRst as e'
return (a':as', e'')
iExpandModulePortArgs i clkRst (a:as) e = do
(as', e') <- iExpandModulePortArgs i clkRst as e
return (a:as', e')
iExpandModulePortArgs i clkRst [] e = return ([], e)
{- -- obsoleted support for interface arguments
iExpandModuleIfcArgs :: (?pps :: [PProp])
=> Id -> [ArgState] -> HExpr
-> G ([ArgState], HExpr)
iExpandModuleIfcArgs i
((Right (li, arginfo,
t@(ITCon ti _ (TIstruct SInterface{} fs))), a):as) e =
do
addMessage (getIdPosition i, swarning ++ ": " ++ prError (WInterfaceArg (pfpString i)))
flags <- getFlags
symt <- getSymTab