-
Notifications
You must be signed in to change notification settings - Fork 182
Expand file tree
/
Copy pathIExpandUtils.hs
More file actions
3748 lines (3334 loc) · 149 KB
/
Copy pathIExpandUtils.hs
File metadata and controls
3748 lines (3334 loc) · 149 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 FlexibleInstances, TypeSynonymInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ImplicitParams #-}
module IExpandUtils(
HPred, PExpr(..), pExprToHExpr, predToIExpr, pConj, pConjs,
pAtom, pIf, pSel, normPConj,
unsafeDerefHeap, HeapCell(..), uniqueStateName, newStateNo, getStateNo,
HExpr, HeapData(..), HClock, HReset, HInout, HStateVar, HRules, HWireSet, HDef, HRule, HEFace,
runG, G, GOutput(..),
errG, errsG, deferErrors, eWarning, eWarnings, getElabProgressContext,
eNoNF, addRules,
pushTopModuleSchedNameScope, pushModuleSchedNameScope,
popModuleSchedNameScope,
pushRuleSchedNameScope, popRuleSchedNameScope,
setRuleSchedNameScopeProgress, RuleElabProgress(..),
pushIfcSchedNameScope, popIfcSchedNameScope,
setIfcSchedNameScopeProgress, IfcElabProgress(..),
addSubmodComments, {-getSubmodComments,-}
addPort, getPortWires, savePortType,
saveRules, getSavedRules, clearSavedRules, replaceSavedRules,
setBackendSpecific, cacheDef, lookupCExprCache, insertCExprCache,
addStateVar, step, updHeap, getHeap, {- filterHeapPtrs, -}
getSymTab, getDefEnv, getFlags, getErrHandle, getModuleName,
getBNotCache, updBNotCache,
getTypeNormalizer, getTypeNormalizerC, fullTypeNormalizer, mergeATFCache,
instFunType,
getNewRuleSuffix, updNewRuleSuffix,
mapPExprPosition,
chkClockDomain, chkResetDomain, fixupActionWireSet,
chkModuleArgument, chkModuleArgumentClkRst,
getMethodsByClockDomain, getMethodsByReset,
extractWireSet,
addHeapCell, addHeapUnev, newClock, newReset,
addHeapPred,
addInputClock, addOutputClock, addInputReset, addOutputReset,
getClkGateUses, clearClkGateUses, setClkGateUses,
assertNoClkGateUses, addClkGateUse, addInhighClkGate,
addGateUsesToInhigh, addGateInhighAttributes,
chkClkArgGateWires, chkClkAncestry, chkClkSiblings,
getInputResetClockDomain, setInputResetClockDomain,
chkInputClockPragmas, chkIfcPortNames,
getBoundaryClock, getBoundaryClocks, boundaryClockToName,
getBoundaryReset, getBoundaryResets, boundaryResetToName, getInputResets,
makeInputClk, makeInputRstn, makeOutputClk, makeOutputRstn,
makeArgInout, makeIfcInout, getInoutWires,
makeDomainToBoundaryIdsMap, getDomainToBoundaryIdsMap,
findBoundaryClock, isClockAncestor,
isPrimType, isParamOnlyType,
HeapPointer, unheap, unheapU, shallowUnheap, unheapAll,
toHeap, toHeapCon, toHeapWHNFCon,
toHeapWHNF,
realPrimOp,
integerPrim, realPrim, stringPrim, charPrim, handleBoolPrim,
strictPrim, condPrim,
NameInfo, legalizeNameInfo,
newIStateLoc, newIStateLocForRule,
isNoInlinedFunc, isAggressive, setAggressive,
getPragmas, setPragmas,
cleanupFinalRules,
heapCellToHExpr,
canLiftCond,
newFFCallNo,
makeArgPortId, makeArgParamId,
showTopProgress, showModProgress, showRuleProgress
) where
import Control.Monad(when, liftM)
import Control.Monad.State(StateT, runStateT, evalStateT, lift, liftIO,
gets, get, put, modify)
import Data.IORef
import System.IO.Unsafe
import Data.List
import Data.Maybe
import Data.Char(isAlphaNum)
import System.Time -- XXX: from old-time package
import Debug.Trace(traceM)
import qualified Data.Array as Array
import qualified Data.Map as M
import qualified Data.Set as S
import Eval
import PPrint
-- import PVPrint
import PFPrint
import Flags
import Error(internalError, EMsg, ErrMsg(..), ErrorHandle, MsgContext,
bsError, bsWarning, bsErrorWithContext, bsWarningWithContext,
bsErrorWithContextNoExit, exitFail, closeOpenHandles)
import Position
import SymTab(SymTab, getIfcFlatMethodNames)
import PreStrings(s_unnamed)
import FStringCompat
import Id
import PreIds
import CSyntax(CExpr)
import CType(TISort(..), StructSubType(..))
import VModInfo
import ISyntax
import ISyntaxUtil
import Prim
import Wires
import IWireSet
import Pragma(PProp(..), SPIdMap, substSchedPragmaIds,
extractSchedPragmaIds, removeSchedPragmaIds)
import Util
import Verilog(vKeywords, vIsValidIdent)
import Changed
import IConv(iConvT)
import IOUtil(progArgs)
import ISyntaxXRef(mapIExprPosition2)
import IStateLoc(IStateLoc, IStateLocPathComponent(..), StateLocMap,
newIStateLocTop, hasIgnore, isAddRules, isLoop, extendStateLocMap,
stateLocToPrefix, createSuffixedId, hasHide, hasHideAll, stateLocToHierName)
{- DEBUGGING AIDS
import Util(getEnvDef,traces)
import System.IO.Unsafe(unsafePerformIO)
dump_hcells = unsafePerformIO $
do env_bs_dump_hcells <- getEnvDef "BS_DUMP_HCELLS" ""
return (read ("[" ++ env_bs_dump_hcells ++ "]"))
trace_hcell cell expr | cell `elem` dump_hcells = traceM ("hcell " ++ show cell ++ ": " ++ show expr)
| otherwise = return ()
-}
-----------------------------------------------------------------------------
-- Trace Flags
-- The comments for these flags are in IExpand
doProfile :: Bool
doProfile = elem "-trace-profile" progArgs
doTraceHeap :: Bool
doTraceHeap = elem "-trace-heap" progArgs
doTraceHeap2 :: Bool
doTraceHeap2 = length (filter (== "-trace-heap") progArgs) > 1
doTraceHeapAlloc :: Bool
doTraceHeapAlloc = elem "-trace-heap-alloc" progArgs
doTraceDefCache :: Bool
doTraceDefCache = elem "-trace-def-cache" progArgs
doTraceClock :: Bool
doTraceClock = elem "-trace-clock" progArgs
doDebugFreeVars :: Bool
doDebugFreeVars = elem "-debug-eval-free-vars" progArgs
doTracePortTypes :: Bool
doTracePortTypes = elem "-trace-port-types" progArgs
doTraceLoc :: Bool
doTraceLoc = elem "-trace-state-loc" progArgs
doTraceATFCache :: Bool
doTraceATFCache = elem "-trace-atf-cache" progArgs
doTraceATFCacheMiss :: Bool
doTraceATFCacheMiss = doTraceATFCache || elem "-trace-atf-cache-miss" progArgs
-----------------------------------------------------------------------------
type HPred = Pred HeapData
pAtom :: IExpr a -> Pred a
pAtom e = if isTrue e then pTrue else PConj (S.singleton (PAtom e))
-- we're wrapping this in the G monad because pIf' should be in IO
-- using unsafePeformIO inside of it is a performance hack
pIf :: HExpr -> HPred -> HPred -> G HPred
pIf _ t e | t == e = return t
pIf c t e | isTrue c = return t
pIf c t e | isFalse c = return e
pIf c t e = return $ pIf' c t e
-- expand heap references looking for PrimBNot to desugar
pIf' :: HExpr -> HPred -> HPred -> HPred
pIf' c@(IRefT _ _ _ (HeapData r)) t e =
let (P p e') = heapCellToPExpr (unsafePerformIO (readIORef r))
in case e' of
(IAps (ICon _ (ICPrim _ PrimBNot)) [] [c']) ->
pConj p (pIf' c' e t)
_ -> pIf'' c t e
pIf' c t e = pIf'' c t e
pIf'' :: HExpr -> HPred -> HPred -> HPred
pIf'' (IAps (ICon _ (ICPrim _ PrimBNot)) [] [c]) t e = pIf' c e t
pIf'' c t@(PConj ts) e@(PConj es) =
let te = ts `S.intersection` es
ts' = ts `S.difference` te
es' = es `S.difference` te
in if ts' == es' then t
else PConj (S.insert (PIf c (PConj ts') (PConj es')) te)
pSel :: HExpr -> Integer -> [HPred] -> HPred
pSel idx idx_sz es =
let getP (PConj p) = p
common_ps = foldr1 S.intersection (map getP es)
ps' = map (\ e -> (getP e) `S.difference` common_ps) es
in if (all S.null ps')
then PConj common_ps
else PConj (S.insert (PSel idx idx_sz (map PConj ps')) common_ps)
pConj :: Pred a -> Pred a -> Pred a
pConj p1@(PConj ts1) p2@(PConj ts2)
| S.null ts1 && S.null ts2 = pTrue
| S.null ts1 = p2
| S.null ts2 = p1
| otherwise = PConj (ts1 `S.union` ts2)
pConjs :: [Pred a] -> Pred a
pConjs ps
| S.null pset = pTrue
| otherwise = PConj pset
where pset = S.unions [ ts | PConj ts <- ps, not (S.null ts) ]
isPAtom :: PTerm a -> Bool
isPAtom (PAtom _) = True
isPAtom _ = False
pairConj :: (Pred a, Pred b) -> (Pred a, Pred b) -> (Pred a, Pred b)
pairConj (t1, f1) (t2, f2) = (t1 `pConj` t2, f1 `pConj` f2)
-- like zipWith, but if one list is longer, keep those elements
listConj :: [Pred a] -> [Pred a] -> [Pred a]
listConj xs [] = xs
listConj [] ys = ys
listConj (x:xs) (y:ys) = (x `pConj` y) : listConj xs ys
-- normPConj inherits the performance hack of pIf
normPConj :: HPred -> G HPred
normPConj p = return $ normPConj' p
normPConj' :: HPred -> HPred
normPConj' (PConj ps) =
let
un (PConj ps) = S.toList ps
f p@(PAtom _) = if p `elem` as then [] else [p]
f (PIf c (PConj ts) (PConj es)) = un (pIf' c ts_norm es_norm)
where ts' = map f (S.toList ts)
ts_norm = PConj (S.fromList (concat ts'))
es' = map f (S.toList es)
es_norm = PConj (S.fromList (concat es'))
f (PSel idx idx_sz es) = un (pSel idx idx_sz es_norm)
where es' = map (map f . un) es
es_norm = map (PConj . S.fromList . concat) es'
-- the atoms
(as, if_or_sels) = partition isPAtom (S.toList ps)
-- merge all the PIf with common conditions
ifmap = M.fromListWith pairConj
[(c, (t, e)) | PIf c t e <- if_or_sels ]
mifs = [ PIf c t e | (c, (t, e)) <- M.toList ifmap ]
-- remove the atoms that are already covered, and simplify
mifs' = map f mifs
-- merge all the PSel with common indices
selmap = M.fromListWith listConj
[((idx, idx_sz), es) | PSel idx idx_sz es <- if_or_sels ]
msels = [ PSel idx idx_sz es | ((idx, idx_sz), es) <- M.toList selmap ]
-- remove the atoms that are already covered, and simplify
msels' = map f msels
in PConj (S.fromList (as ++ concat mifs' ++ concat msels'))
predToIExpr :: Pred a -> IExpr a
predToIExpr (PConj es) = foldr (ieAnd . pTermToIExpr) iTrue (S.toList es)
pTermToIExpr :: PTerm a -> IExpr a
pTermToIExpr (PAtom e) = e
pTermToIExpr (PIf c t e) = ieIfx itBit1 c (predToIExpr t) (predToIExpr e)
pTermToIExpr (PSel idx idx_sz es) =
-- the default for out of range selection is True
ieCase itBit1 idx_sz idx (map predToIExpr es) iTrue
-- An expression with an implicit condition.
data PExpr = P !HPred HExpr
deriving (Eq, Ord, Show)
instance PPrint PExpr where
pPrint d prec (P p e) = pPrint d prec (iePrimWhen (iGetType e) (predToIExpr p) e)
pExprToHExpr :: PExpr -> HExpr
pExprToHExpr (P (PConj s) e) | S.null s = e
pExprToHExpr (P p e) = iePrimWhenPred (iGetType e) p e
---------------------------------------------------------------------------
-- test whether an expression is a valid condition to be lifted to the
-- predicate as part of aggressive implicit conditions
-- (method arguments cannot be lited and the value result of ActionValue
-- methods or foreign functions cannot be lifted)
-- XXX In rev 13458, this function was changed from monadic to use
-- XXX unsafePerformIO, as a workarounk for a mem leak (the same as is
-- XXX done for pIf). Ravi suspected it might be a GHC bug?
-- Avoid exponential expansion by memoizing results.
-- XXX Perhaps better would be to generate this info along with the p-term.
canLiftCond :: HExpr -> G Bool
canLiftCond e = return $ fst $ canLiftCond' M.empty e
canLiftCond' :: M.Map Int Bool -> HExpr -> (Bool, M.Map Int Bool)
-- value portion of an ActionValue
-- the select should only survive if it is surrounding a method call
-- or foreign function call, so no check of "e" is needed
canLiftCond' m (IAps (ICon i_sel ICSel { }) _ [e])
| i_sel == idAVValue_ = (False, m)
-- dynamic selection from an array
-- (we could not treat this prim specially and instead just consider an
-- ICLazyArray liftable if all elements are liftable; but this is a more
-- aggressive optimization that only considers the selectable elems)
canLiftCond' m (IAps (ICon _ (ICPrim _ PrimArrayDynSelect))
[elem_ty, ITNum idx_sz] [arr_e, idx_e]) =
case arr_e of
ICon _ (ICLazyArray _ arr u) ->
if (isJust u)
then (False, m)
else let cells = Array.elems arr
reachable_cells = take (2 ^ idx_sz) cells
cellToExpr (ArrayCell ptr ref) = IRefT elem_ty ptr S.empty ref
-- check the index and the reachable cells
es = (idx_e : map cellToExpr reachable_cells)
in canLiftCond'_List m es
_ -> internalError ("canLiftCond': array: " ++ ppReadable arr_e)
canLiftCond' m (IAps f _ es) = canLiftCond'_List m (f:es)
-- method argument
canLiftCond' m (ICon _ (ICMethArg _)) = (False, m)
-- other arrays are unexpected
canLiftCond' m (ICon _ (ICLazyArray arr_ty arr u)) =
internalError ("IExpandUtils.canLiftCond: unexpected array")
canLiftCond' m (ICon _ _) = (True, m)
canLiftCond' m ref@(IRefT t p poss r) =
-- only follow references for which we haven't yet computed the answer
case M.lookup p m of
Nothing ->
-- implicit conditions will have been dealt with previously,
-- so we unheap with NoImp
let (b, m') = canLiftCond' m (unheapNFNoImpEvil ref)
in (b, M.insert p b m')
Just b -> (b, m)
canLiftCond' m e =
internalError ("IExpandUtils.canLiftCond unexpected " ++ (ppReadable e))
canLiftCond'_List :: M.Map Int Bool -> [HExpr] -> (Bool, M.Map Int Bool)
canLiftCond'_List m es =
let fn e (b, accum_m) = if b then canLiftCond' accum_m e else (b, accum_m)
in foldr fn (True, m) es
-----------------------------------------------------------------------------
-- Test if a type is allowed in the residual program.
-- Note that this is only used by walkNF in IExpand;
-- all residual type functions should be normalized at this point.
isPrimType :: IType -> Bool
-- Primitive interfaces
isPrimType (ITCon _ _ (TIstruct SInterface{} _)) = True
-- Primitive base types
isPrimType (ITCon i _ _) = i == idPrimAction ||
-- types allowed as module parameters
-- and foreign function arguments
i == idString ||
i == idReal ||
-- we could support Integer in some contexts
-- but choose not to
-- i == idInteger
i == idFmt || -- also not really a primitive
i == idClock ||
i == idReset ||
i == idPrimUnit
-- ActionValue_ must be applied to (a tuple of) Bit
isPrimType (ITAp (ITCon i _ _) t)
| i == idActionValue_ = t == itPrimUnit || isBitTupleType t
-- Primitive constructor applied to numeric type(s)
-- We normalize types so no unresolved numeric types should escape elaboration.
isPrimType (ITAp a (ITNum _)) = isPrimTAp a
-- Primitive arrays
isPrimType (ITAp (ITCon i _ _) elem_ty) | i == idPrimArray = isPrimType elem_ty
-- Tuples of bits
isPrimType t | isBitTupleType t = True
isPrimType _ = False
-- Primitive type applications
isPrimTAp :: IType -> Bool
isPrimTAp (ITCon _ _ (TIstruct SInterface{} _)) = True
isPrimTAp (ITCon i _ _) = i == idBit ||
i == idInout_
-- Again, no unresolved numeric types should escape elaboration.
isPrimTAp (ITAp a (ITNum _)) = isPrimTAp a
isPrimTAp _ = False
isParamOnlyType :: IType -> Bool
isParamOnlyType t = t == itString || t == itReal
-----------------------------------------------------------------------------
type NameInfo = Maybe Id
legalizeNameInfo :: NameInfo -> NameInfo
legalizeNameInfo = liftM filterId
where legalChar c = (isAlphaNum c) || (c == '_')
filterId i = let b = filterFString legalChar (getIdBase i)
q = filterFString legalChar (getIdQual i)
in setIdBase (setIdQual i q) b
type HeapPointer = Int
-- type Heap s = STArray s HeapPointer (Maybe HeapCell)
-- immutable heap used at the end of evaluation
-- type IHeap = Array HeapPointer (Maybe HeapCell)
data HeapCell = HUnev { hc_hexpr :: HExpr, hc_name :: NameInfo }
| HWHNF { hc_pexpr :: PExpr, hc_name :: NameInfo }
| HNF { hc_pexpr :: PExpr, hc_wire_set :: HWireSet,
hc_name :: NameInfo }
| HLoop { hc_name :: NameInfo }
deriving (Show, Eq, Ord)
-- should I drop the predicate for better printing of error messages?
heapCellToHExpr :: HeapCell -> HExpr
heapCellToHExpr (HUnev { hc_hexpr = e }) = e
-- heapCellToHExpr (Harray { hc_hexpr = e }) = e
heapCellToHExpr (HWHNF { hc_pexpr = p }) = (pExprToHExpr p)
heapCellToHExpr (HNF { hc_pexpr = p }) = (pExprToHExpr p)
heapCellToHExpr (HLoop mn) = internalError ("heapCellToHExpr.HLoop: " ++ ppReadable mn)
heapCellToPExpr :: HeapCell -> PExpr
heapCellToPExpr (HWHNF { hc_pexpr = p }) = p
heapCellToPExpr (HNF { hc_pexpr = p }) = p
heapCellToPExpr (HUnev { hc_hexpr = e }) = internalError("heapCellToPExpr.HUnev: " ++ ppReadable e)
heapCellToPExpr (HLoop mn) = internalError ("heapCellToPExpr.HLoop: " ++ ppReadable mn)
instance PPrint HeapCell where
pPrint d p (HUnev { hc_hexpr = e, hc_name = name}) =
text "HUnev" <+> pPrint d p e <+> pPrint d 0 name
-- pPrint d p (Harray { hc_hexpr = e, hc_name = name}) =
-- text "Harray" <+> pPrint d p e <+> pPrint d 0 name
pPrint d p (HWHNF { hc_pexpr = e, hc_name = name}) =
text "HWHNF" <+> pPrint d p e <+> pPrint d 0 name
pPrint d p (HNF { hc_pexpr = e, hc_name = name, hc_wire_set = ws}) =
text "HNF" <+> pPrint d p e <+> pPrint d 0 name <+> pPrint d 0 ws
pPrint d p (HLoop name) =
text "HLoop" <+> pPrint d 0 name
newtype HeapData = HeapData (IORef (HeapCell))
{-
instance Eq HeapData where
(==) a b = True
instance Ord HeapData where
compare a b = EQ
-}
instance Show HeapData where
show hd = ""
{-
instance PPrint HeapData where
pPrint d p hd = text (show hd)
-}
instance NFData HeapData where
rnf (HeapData r) = seq r ()
-- Heap expressions are IExprs with the real heap reference type filled in
type HExpr = IExpr HeapData
-- other useful synonyms
type HClock = IClock HeapData
type HReset = IReset HeapData
type HInout = IInout HeapData
type HStateVar = IStateVar HeapData
type HRules = IRules HeapData
type HWireSet = IWireSet HeapData
type HRule = IRule HeapData
type HDef = IDef HeapData
type HEFace = IEFace HeapData
type RulesBlobs = [(Bool, (HClock, HReset), IStateLoc, HPred, HExpr)]
-- G is the main evaluator monad
-- a writer monad for expanding rules lazily (for PrimModuleFix)
-- it has a state monad (for result state)
-- layered on top of IO for IORefs and errors/warnings/progress messages
type G = StateT GState IO
-- only used for post-evaluation heap traversals
-- so unsafePerformIO is fine (the refs are no longer being modified)
unsafeDerefHeap :: HeapData -> HeapCell
unsafeDerefHeap (HeapData ref) = unsafePerformIO (readIORef ref)
-- read-only evaluator state
data GStateRO = GStateRO {
errHandle :: !ErrorHandle,
symtab :: !SymTab,
-- lazy because computing the defenv may be expensive and (often) unnecessary
defenv :: M.Map Id HExpr,
checkMaxStep :: !Bool,
maxStep :: !Integer,
stepWarnInterval :: !Integer,
flags :: !Flags
}
-- full evaluator state
data GState = GState {
stepNo :: !Integer, -- evaluation step
nextWarnStep :: !Integer, -- next step to issue an evaluation warning at
profilingMap :: !(M.Map Id (M.Map Position Int)), -- map from definitions to number of entries for profiling
stateNo :: !Int, -- unique number for state variables
-- Stores the pair "(x, next unique number to start with)".
-- When "x" is instantiated a second time, to be named "x_1",
-- we update the map to contain both "(x, 2)" and "(x_1, 1)".
stateNameMap :: !(M.Map Id Int),
-- Track names used within hierarchy for unquification of name of instances & loops
stateLocMap :: !StateLocMap,
-- comments on submodule instances
-- mapping an instance name to its user-added comments
-- XXX we use Id, just to have a position around; String would do
commentsMap :: !(M.Map Id [String]),
ffcallNo :: !Int, -- to generate unique names for ActionValue foreign function calls
newClockId :: !ClockId, -- to generate unique ids for clocks
newClockDomain :: !ClockDomain, -- to generate unique ids for clock families
newResetId :: !ResetId, -- to generate unique ids for resets
hp :: !HeapPointer, -- next unique id for a new heap reference
-- This is a cache for "extractWires", so that it doesn't re-walk a
-- a heap reference that we've already walked.
-- XXX this could be stored in the heap cells?
heapWires :: !(M.Map HeapPointer HWireSet),
-- This is a cache for "pushBNot" (see IExpand), so that pushing a
-- negation through a shared if-DAG is O(cells), not O(paths).
heapBNots :: !(M.Map HeapPointer HExpr),
-- XXX what is the Id? flattened name?
vars :: [(Id, HStateVar)], -- instantiated verilog modules
portTypeMap :: PortTypeMap, -- map of state var -> port -> type
rules :: HRules, -- accumulated rules
newRuleSuffix :: Integer, -- store for creating unique names when ruleNameCheck is off
-- scope for scheduling attribute names
schedNameScope :: SchedNameScope,
clock_domains :: !(M.Map ClockDomain [HClock]), -- all clock objects
all_resets :: [HReset], -- all reset objects
in_clock_info :: [(HClock, InputClockInf)], -- input clock information
out_clock_info :: [(HClock, OutputClockInf)], -- output clock information
in_reset_info :: [(HReset, ResetInf)], -- input reset information
out_reset_info :: [(HReset, ResetInf)], -- output reset information
ro :: !GStateRO, -- read-only state
-- properties of the top-level def being compiled
-- (these three fields could be in GStateRO, except that "pragmas"
-- gets set when evaluating "primBuildModule" and we may eventually
-- structure the evaluator to operate on multiple modules)
mod_def_id :: Id,
-- is the evaluation a no-lined function
noinlined_func :: Bool,
-- the pragmas for the top-level module
pragmas :: [PProp],
-- record when the gate of a clock is used
used_clk_gates :: !(S.Set HClock),
clk_gates_inhigh :: !(S.Set HClock),
-- record when an input reset is used with a particular clock domain
in_reset_clk_info :: !(M.Map HReset ClockDomain),
-- map from clockdomain to the ids of boundary clocks in the domain
-- (constructed after all input and output clocks are added)
domain_to_boundary_id_map :: !(M.Map ClockDomain [Id]),
-- map of clock ancestry relationships
-- entries are parent -> list of children
clk_ancestry_map :: !(M.Map HClock [HClock]),
-- the clock/reset for input ports
port_wires :: !(M.Map Id (HClock, HReset)),
-- record whether the design is specific to a backend
backend_specific :: !Bool,
-- cache partially-evaluated top-level definitions
defCache :: !(M.Map Id HExpr),
-- cache dynamically evaluated CSyntax expressions
cexprCache :: !(M.Map (CExpr, IType) HExpr),
-- used for moduleFix
savedRules :: !RulesBlobs,
-- whether a deferable error was reported
badEvaluation :: !Bool,
-- aggressive conditions
aggressive_cond :: Bool,
atfCache :: !IATFCache
}
initGState :: ErrorHandle -> Flags ->
SymTab -> M.Map Id HExpr ->
IATFCache ->
Id -> Bool -> [PProp] ->
GState
initGState errh flags symt alldefs atf_cache defId is_noinlined_func pps =
let gsro = GStateRO { errHandle = errh,
symtab = symt,
checkMaxStep = redStepsMaxIntervals flags /= 0,
maxStep = redSteps flags,
stepWarnInterval = redStepsWarnInterval flags,
flags = flags,
defenv = alldefs
}
gs = GState { stepNo = 0,
nextWarnStep = redStepsWarnInterval flags,
stateNo = 0,
stateNameMap = M.empty,
stateLocMap = M.empty,
commentsMap = M.empty,
ffcallNo = 0,
newClockId = initClockId,
newClockDomain = initClockDomain,
newResetId = initResetId,
hp = 0,
heapWires = M.empty,
heapBNots = M.empty,
vars = [],
portTypeMap = M.empty,
rules = iREmpty,
newRuleSuffix = 0,
schedNameScope = emptySchedNameScope,
profilingMap = M.empty,
clock_domains = M.empty,
all_resets = [],
in_clock_info = [],
out_clock_info = [],
in_reset_info = [],
out_reset_info = [],
ro = gsro,
mod_def_id =
-- convert the raw defId to the user-recognizable Id
setIdBaseString defId (init (getIdBaseString defId)),
noinlined_func = is_noinlined_func,
pragmas = pps,
used_clk_gates = S.empty,
clk_gates_inhigh = S.empty,
in_reset_clk_info = M.empty,
domain_to_boundary_id_map = M.empty,
clk_ancestry_map = M.empty,
port_wires = M.empty,
backend_specific = False,
defCache = M.empty,
cexprCache = M.empty,
savedRules = [],
badEvaluation = False,
aggressive_cond = aggImpConds flags,
atfCache = atf_cache
}
in gs
data GOutput a = GOutput { go_clock_domains :: [(ClockDomain, [HClock])],
go_resets :: [HReset],
go_state_vars :: [(Id, HStateVar)],
go_port_types :: PortTypeMap,
go_vclockinfo :: VClockInfo,
go_vresetinfo :: VResetInfo,
go_rules :: HRules,
go_steps :: Integer,
go_hp :: HeapPointer,
go_profile :: M.Map Id (M.Map Position Int),
go_comments_map :: [(Id,[String])],
go_backend_specific :: Bool,
go_ffcallNo :: Int,
go_atfCache :: IATFCache,
goutput :: a }
runG :: ErrorHandle -> Flags ->
SymTab -> M.Map Id HExpr ->
IATFCache ->
Id -> Bool -> [PProp] -> G a ->
IO (GOutput a)
runG errh flags symt alldefs atf_cache defId is_noinlined_func pps gFn =
do let gs = initGState errh flags symt alldefs atf_cache defId is_noinlined_func pps
(retval, gs') <- runStateT gFn gs
-- convert the relevant info in the final GState into the GOutput
do
-- if any file handles were not closed, close them now
closeOpenHandles errh
-- this can produce warnings
vresetinfo <- make_vresetinfo errh
(in_reset_info gs')
(out_reset_info gs')
(in_reset_clk_info gs')
(domain_to_boundary_id_map gs')
let vclockinfo = make_vclockinfo (clk_gates_inhigh gs')
(in_clock_info gs')
(out_clock_info gs')
(domain_to_boundary_id_map gs')
(clk_ancestry_map gs')
-- check that any gates marked as unused are not in the inhigh
-- list (which accumulates gate uses)
let should_be_unused = concat [ is | PPgate_unused is <- pps ]
getClk hclk = do c <- lookup hclk (in_clock_info gs')
return (fst c)
actually_used = mapMaybe getClk (S.toList (clk_gates_inhigh gs'))
wrong_annotations = should_be_unused `intersect` actually_used
mk_annotation_msg i = (getPosition i, EClkGateNotUnused (getIdBaseString i))
when (not (null wrong_annotations)) $
bsError errh (map mk_annotation_msg wrong_annotations)
-- if there were deferred errors, they were reported to IO, so exit
when (badEvaluation gs') $
exitFail errh
-- check that there is no fixed-point state that was left unhandled
let rblobs = savedRules gs'
when (not (null rblobs)) $
internalError ("IExpandUtils.runG - unexpanded ruleblobs: " ++ ppReadable rblobs)
return $ (GOutput {
go_clock_domains = (M.toList (clock_domains gs')),
go_resets = (all_resets gs'),
go_state_vars = reverse (vars gs'),
go_port_types = portTypeMap gs',
go_vclockinfo = vclockinfo,
go_vresetinfo = vresetinfo,
go_rules = rules gs',
go_steps = stepNo gs',
go_hp = hp gs',
go_profile = profilingMap gs',
go_comments_map = M.toList (commentsMap gs'),
go_backend_specific = backend_specific gs',
go_ffcallNo = ffcallNo gs',
go_atfCache = atfCache gs',
goutput = retval
})
errG :: EMsg -> G a
errG emsg = errsG [emsg]
errsG :: [EMsg] -> G a
errsG emsgs = do
errh <- getErrHandle
ctx <- getElabProgressContext
liftIO $ bsErrorWithContext errh ctx emsgs
-- report evaluation problems
-- defer exit until the end of evaluation
-- if there are no errors, this is a no-op
deferErrors :: [EMsg] -> G ()
deferErrors emsgs = when (not (null emsgs)) $ do
s <- get
put s { badEvaluation = True }
let errh = errHandle (ro s)
ctx <- getElabProgressContext
liftIO $ bsErrorWithContextNoExit errh ctx emsgs
eWarning :: EMsg -> G ()
eWarning emsg = eWarnings [emsg]
eWarnings :: [EMsg] -> G ()
eWarnings emsgs = do
errh <- getErrHandle
ctx <- getElabProgressContext
liftIO $ bsWarningWithContext errh ctx emsgs
-- compile-time expression failed to evaluate error utility
eNoNF :: HExpr -> G a
eNoNF e =
let ty = iGetType e
pos = getIExprPosition e
in errG (pos, ENoNF (ppString ty) (ppReadable e))
-- in errG (pos, ENoNF (ppString ty) (show e))
-- depends on the invariant that
-- max steps is a multiple of stepWarnInterval (enforced by Flags)
-- and that stepWarnInterval > 0
step :: Id -> G ()
step i = do s <- get
-- do common update
let c = stepNo s
let s' = s { stepNo = c + 1 }
if (doProfile) then do
let prof = profilingMap s
let pos_map = M.singleton (getPosition i) 1
let prof' = M.insertWith (M.unionWith (+)) i pos_map prof
put (s' { profilingMap = prof' })
else put s'
let w = nextWarnStep s
when (c == w) $ do
let check = checkMaxStep (ro s)
let m = maxStep (ro s)
-- can only hit the max step if we would issue a warning
if (check && c == m) then
errG (getIdPosition i, ETooManySteps (pfpString i) (itos m))
else do let w_i = stepWarnInterval (ro s)
let w' = w + w_i
when (badEvaluation s) $
errG (getIdPosition i, EStepsIntervalError (pfpString i) c w_i)
eWarning (getIdPosition i, WTooManySteps (pfpString i) c w' m)
s' <- get
when doProfile $ liftIO (putStrLn (ppReadable (profilingMap s')))
put s' { nextWarnStep = w' }
-- ---------------
addRules :: HRules -> G ()
addRules rs = do
s <- get
--traceM("AddRules: " ++ ppReadable rs)
let curScope = schedNameScope s
(curFrame, frames) = case curScope of
(f:fs) -> (f, fs)
_ -> internalError ("addRules: curFrame")
cur_ns = snf_istateloc curFrame
cur_cnt = snf_ignoreCount curFrame
cur_nameMap = snf_nameMap curFrame
-- we should not be in a rule/interface scope
when (isJust (snf_elabProgress curFrame)) $
internalError ("IExpandUtils.addRules: invalid elabProgress")
flags <- getFlags
suf <- getNewRuleSuffix
-- this is like iRUnion, but with different attribute checking
let cur_rules@(IRules sps1 rs1) = (rules s)
(suf', rs') = uniquifyRules flags suf cur_rules rs
updNewRuleSuffix suf'
-- check the names in attributes of the new rules
-- (any errors are deferred and it returns the rules without bad Ids)
rs''@(IRules sps2 rs2) <- checkAddRulesAttributes cur_ns cur_nameMap rs'
-- add the new rule names to the scope
-- XXX The IRule should contain the right name to start with
-- XXX rather than using remIStateLocPrefix (like popModuleSchedNameScope)
let addFn (IRule { irule_name = rId } ) =
(remIStateLocPrefix cur_ns rId, SNI_Rule rId)
new_map_entries = map addFn rs2
-- there should be no conflicts, when using remIStateLocPrefix
conflictFn v1 v2 = internalError
("addRules: conflict: " ++ ppReadable (v1,v2))
cur_nameMap' = map_insertManyWith conflictFn new_map_entries cur_nameMap
curFrame' = SchedNameFrame cur_ns cur_cnt cur_nameMap' Nothing
newScope = (curFrame' : frames)
--traceM("ADD: rs2 = " ++ ppReadable rs2)
--traceM("ADD: new_map_entries = " ++ ppReadable new_map_entries)
--traceM("ADD: map = " ++ ppReadable cur_nameMap')
s <- get
put (s { schedNameScope = newScope })
-- add the rules themselves to the monad
s <- get
let new_rules =
-- As the size of rs1 grows, it's more efficient to add to the front
-- XXX We should consider a better data structure
if (ruleNameCheck flags)
then (IRules (sps1 ++ sps2) (rs1 ++ rs2))
else (IRules (sps2 ++ sps1) (rs2 ++ rs1))
put (s { rules = new_rules } )
checkAddRulesAttributes :: IStateLoc -> M.Map Id SchedNameInfo -> IRules a ->
G (IRules a)
checkAddRulesAttributes cur_ns idMap (IRules sps rs) =
let
mkErr i = (getIdPosition i, EUnknownRuleIdAttribute (pfpString i))
mkWarn i = (getIdPosition i, WInlinedMethodIdAttribute (pfpString i))
-- the rule Ids will be the global names (w/ hier and uniquified),
-- the attributes on those rules will also have hierarchy prefixed
-- to them, so we need to strip that off when checking for names
-- in the scope. (For names in "rs", use the global name, because
-- "sps" will have been adjusted for uniquifiers in sync with "rs".)
-- names of the rules being added (global names)
definedIds = S.fromList $ map getIRuleId rs
-- this folds over the attr names
checkFn i accum@(accum_warns, accum_errs, accum_badIds) =
if (S.member i definedIds)
then accum
else
-- remove the hierarchy prefix
let i' = remIStateLocPrefix cur_ns i
in case (M.lookup i' idMap) of
Nothing ->
(accum_warns, ((mkErr i'):accum_errs), (i:accum_badIds))
Just (SNI_Method False) ->
(((mkWarn i'):accum_warns), accum_errs, (i:accum_badIds))
Just _ -> accum
attrIds = extractSchedPragmaIds sps
(warns, errs, badIds) = foldr checkFn ([], [], []) attrIds
sps' = if (null badIds) then sps else removeSchedPragmaIds badIds sps
in
do --traceM("checkAddRules Attr: " ++ ppReadable (cur_ns, sps, rs))
when (not (null warns)) $ eWarnings warns
deferErrors errs
--traceM("definedIds: " ++ ppReadable definedIds)
--traceM("attrIds: " ++ ppReadable attrIds)
return (IRules sps' rs)
-- ---------------
-- XXX Consider unifying the IStateLoc stack with this stack
-- XXX We also use this stack to keep track of elab progress, so
-- XXX consider giving it a better name (it's not just the schedname scope)
-- the lookup result for names in the current context
data SchedNameInfo =
-- Method Name
-- The Bool indicates whether it is at a synthesized boundary
-- (if False, it is inlined, and we don't support references to
-- inlined boundaries yet, so warn and ignore).
-- If a rule is later added with the same name, warn about the
-- name clash and have the rule shadow the method.
SNI_Method Bool |
-- Rule Name
-- The Id is the qualified, uniquified name for the rule
-- (its global name). This is substituted into attributes in
-- place of the name as it is visible in the attribute's scope.
SNI_Rule Id
deriving (Show, Eq, Ord)
instance PPrint SchedNameInfo where
pPrint d p sni = text (show sni)
data SchedNameFrame =
SchedNameFrame {
-- hierarchy naming for this scope
snf_istateloc :: IStateLoc,
-- number of ignored levels that have been pushed
-- (the number of pops remaining until this frame can be popped)
-- (this is to avoid duplicating frames)
snf_ignoreCount :: Integer,
-- names available at this scope
-- (populated by the parent, if any names remain visible,
-- then with new names as they are added in the current scope)
snf_nameMap :: M.Map Id SchedNameInfo,
-- whether we are currently evaluating inside a rule/ifc;
-- this should be Nothing when pushing/popping a frame
snf_elabProgress :: Maybe ElabProgress
}
deriving (Show, Eq)
instance PPrint SchedNameFrame where
pPrint d p snf =
(text "SchedNameFrame" <+> pPrint d p ig <+> pPrint d p ns
<+> pPrint d p ep) $$
pPrint d p m
where ig = snf_ignoreCount snf
ns = snf_istateloc snf
m = M.toList $ snf_nameMap snf
ep = snf_elabProgress snf
data ElabProgress =
EPRule
Id {- rule name/position -}
Bool {- has the "hide" property -}
(Maybe RuleElabProgress) {- progress inside the rule -}
|
-- This should only occur at the top-level
EPInterface
(Maybe IfcElabProgress) {- progress inside the ifc -}
deriving (Show, Eq)
instance PPrint ElabProgress where
pPrint d p (EPRule i h rp) =
pparen (p>0) $ text "Rule" <+> pPrint d 0 i <+>
pPrint d 0 h <+> pPrint d 0 rp
pPrint d p (EPInterface ip) =
pparen (p>0) $ text "Interface" <+> pPrint d 0 ip
data RuleElabProgress = REP_ExplicitCond | REP_Body | REP_ImplicitCond
deriving (Show, Eq)
instance PPrint RuleElabProgress where
pPrint d p (REP_ExplicitCond) = text "ExplicitCond"
pPrint d p (REP_Body) = text "Body"
pPrint d p (REP_ImplicitCond) = text "ImplicitCond"
data IfcElabProgress = IEP_OutputClock Id
| IEP_OutputReset Id
| IEP_OutputInout Id
| IEP_Method Id Bool {- MethodElabProgress -}
deriving (Show, Eq)
instance PPrint IfcElabProgress where
pPrint d p (IEP_OutputClock i) =
pparen (p > 0) $ text "OutputClock" <+> pPrint d 0 i
pPrint d p (IEP_OutputReset i) =
pparen (p > 0) $ text "OutputReset" <+> pPrint d 0 i
pPrint d p (IEP_OutputInout i) =
pparen (p > 0) $ text "OutputInout" <+> pPrint d 0 i
pPrint d p (IEP_Method i mp) =