Skip to content

Commit 4f156a0

Browse files
committed
Allow multiple triggers with the same name. Refs #30.
`copilot-c99-4.2` (part of the overall Copilot 4.2 release) now allows multiple triggers with the same handler, provided that the triggers always have the same type signatures. See Copilot-Language/copilot#296 (as well as the fix in Copilot-Language/copilot#572). Since `copilot-bluespec` wishes to achieve feature parity with `copilot-c99`, we would like to add this ability to `copilot-bluespec` as well. This commit adds multiple triggers support for `copilot-bluespec`, piggybacking off of the implementation in Copilot-Language/copilot#572.
1 parent 3e880d3 commit 4f156a0

File tree

4 files changed

+96
-18
lines changed

4 files changed

+96
-18
lines changed

copilot-bluespec.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ library
5656
, Copilot.Compile.Bluespec.External
5757
, Copilot.Compile.Bluespec.FloatingPoint
5858
, Copilot.Compile.Bluespec.Name
59+
, Copilot.Compile.Bluespec.Representation
5960
, Copilot.Compile.Bluespec.Settings
6061
, Copilot.Compile.Bluespec.Type
6162

src/Copilot/Compile/Bluespec/CodeGen.hs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import Copilot.Core
3535
import Copilot.Compile.Bluespec.Expr
3636
import Copilot.Compile.Bluespec.External
3737
import Copilot.Compile.Bluespec.Name
38+
import Copilot.Compile.Bluespec.Representation
3839
import Copilot.Compile.Bluespec.Type
3940

4041
-- | Write a generator function for a stream.
@@ -151,22 +152,24 @@ mkSpecIfcFields triggers exts =
151152
mkField name $ tReg `BS.TAp` transType ty
152153

153154
-- | Define a rule for a trigger function.
154-
mkTriggerRule :: Trigger -> BS.CRule
155-
mkTriggerRule (Trigger name _ args) =
155+
mkTriggerRule :: UniqueTrigger -> BS.CRule
156+
mkTriggerRule (UniqueTrigger uniqueName (Trigger name _ args)) =
156157
BS.CRule
157158
[]
158-
(Just $ cLit $ BS.LString name)
159+
(Just $ cLit $ BS.LString uniqueName)
159160
[ BS.CQFilter $
160161
BS.CVar $ BS.mkId BS.NoPos $
161-
fromString $ guardName name
162+
fromString $ guardName uniqueName
162163
]
163164
(BS.CApply nameExpr args')
164165
where
165166
ifcArgId = BS.mkId BS.NoPos $ fromString ifcArgName
167+
-- Note that we use 'name' here instead of 'uniqueName', as 'name' is the
168+
-- name of the actual external function.
166169
nameId = BS.mkId BS.NoPos $ fromString $ lowercaseName name
167170
nameExpr = BS.CSelect (BS.CVar ifcArgId) nameId
168171

169-
args' = take (length args) (map argCall (argNames name))
172+
args' = take (length args) (map argCall (argNames uniqueName))
170173
argCall = BS.CVar . BS.mkId BS.NoPos . fromString
171174

172175
-- | Writes the @step@ rule that updates all streams.

src/Copilot/Compile/Bluespec/Compile.hs

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ module Copilot.Compile.Bluespec.Compile
88
) where
99

1010
-- External imports
11-
import Data.List (nub, union)
11+
import Data.List (nub, nubBy, union)
1212
import Data.Maybe (catMaybes, maybeToList)
1313
import Data.String (IsString (..))
14+
import Data.Type.Equality (testEquality, (:~:)(Refl))
1415
import Data.Typeable (Typeable)
1516
import qualified Language.Bluespec.Classic.AST as BS
1617
import qualified Language.Bluespec.Classic.AST.Builtin.Ids as BS
@@ -29,6 +30,7 @@ import Copilot.Compile.Bluespec.CodeGen
2930
import Copilot.Compile.Bluespec.External
3031
import Copilot.Compile.Bluespec.FloatingPoint
3132
import Copilot.Compile.Bluespec.Name
33+
import Copilot.Compile.Bluespec.Representation
3234
import Copilot.Compile.Bluespec.Settings
3335

3436
-- | Compile a specification to a Bluespec file.
@@ -39,12 +41,20 @@ import Copilot.Compile.Bluespec.Settings
3941
-- that are generated.
4042
compileWith :: BluespecSettings -> String -> Spec -> IO ()
4143
compileWith bsSettings prefix spec
42-
| null (specTriggers spec)
44+
| null triggers
4345
= do hPutStrLn stderr $
4446
"Copilot error: attempt at compiling empty specification.\n"
4547
++ "You must define at least one trigger to generate Bluespec monitors."
4648
exitFailure
4749

50+
| incompatibleTriggers triggers
51+
= do hPutStrLn stderr $
52+
"Copilot error: attempt at compiling specification with conflicting "
53+
++ "trigger definitions.\n"
54+
++ "Multiple triggers have the same name, but different argument "
55+
++ "types.\n"
56+
exitFailure
57+
4858
| otherwise
4959
= do let typesBsFile = render $ pPrint $ compileTypesBS bsSettings prefix spec
5060
ifcBsFile = render $ pPrint $ compileIfcBS bsSettings prefix spec
@@ -57,6 +67,24 @@ compileWith bsSettings prefix spec
5767
writeFile (dir </> "bs_fp.c") copilotBluespecFloatingPointC
5868
writeFile (dir </> "BluespecFP.bsv") copilotBluespecFloatingPointBSV
5969
writeFile (dir </> prefix ++ ".bs") bsFile
70+
where
71+
triggers = specTriggers spec
72+
73+
-- Check that two triggers do no conflict, that is: if their names are
74+
-- equal, the types of their arguments should be equal as well.
75+
incompatibleTriggers :: [Trigger] -> Bool
76+
incompatibleTriggers = pairwiseAny conflict
77+
where
78+
conflict :: Trigger -> Trigger -> Bool
79+
conflict t1@(Trigger name1 _ _) t2@(Trigger name2 _ _) =
80+
name1 == name2 && not (compareTrigger t1 t2)
81+
82+
-- True if the function holds for any pair of elements. We assume that
83+
-- the function is commutative.
84+
pairwiseAny :: (a -> a -> Bool) -> [a] -> Bool
85+
pairwiseAny _ [] = False
86+
pairwiseAny _ (_:[]) = False
87+
pairwiseAny f (x:xs) = any (f x) xs || pairwiseAny f xs
6088

6189
-- | Compile a specification to a Bluespec.
6290
--
@@ -136,11 +164,12 @@ compileBS _bsSettings prefix spec =
136164
ifcModId = BS.mkId BS.NoPos "ifcMod"
137165

138166
rules :: [BS.CRule]
139-
rules = map mkTriggerRule triggers ++ maybeToList (mkStepRule streams)
167+
rules = map mkTriggerRule uniqueTriggers ++ maybeToList (mkStepRule streams)
140168

141-
streams = specStreams spec
142-
triggers = specTriggers spec
143-
exts = gatherExts streams triggers
169+
streams = specStreams spec
170+
triggers = specTriggers spec
171+
uniqueTriggers = mkUniqueTriggers triggers
172+
exts = gatherExts streams triggers
144173

145174
ifcId = BS.mkId BS.NoPos $ fromString $ specIfcName prefix
146175
ifcFields = mkSpecIfcFields triggers exts
@@ -169,19 +198,20 @@ compileBS _bsSettings prefix spec =
169198
genFuns :: [BS.CDefl]
170199
genFuns = map accessDecln streams
171200
++ map streamGen streams
172-
++ concatMap triggerGen triggers
201+
++ concatMap triggerGen uniqueTriggers
173202
where
174203
accessDecln :: Stream -> BS.CDefl
175204
accessDecln (Stream sId buff _ ty) = mkAccessDecln sId ty buff
176205

177206
streamGen :: Stream -> BS.CDefl
178207
streamGen (Stream sId _ expr ty) = mkGenFun (generatorName sId) expr ty
179208

180-
triggerGen :: Trigger -> [BS.CDefl]
181-
triggerGen (Trigger name guard args) = guardDef : argDefs
209+
triggerGen :: UniqueTrigger -> [BS.CDefl]
210+
triggerGen (UniqueTrigger uniqueName (Trigger _name guard args)) =
211+
guardDef : argDefs
182212
where
183-
guardDef = mkGenFun (guardName name) guard Bool
184-
argDefs = map argGen (zip (argNames name) args)
213+
guardDef = mkGenFun (guardName uniqueName) guard Bool
214+
argDefs = map argGen (zip (argNames uniqueName) args)
185215

186216
argGen :: (String, UExpr) -> BS.CDefl
187217
argGen (argName, UExpr ty expr) = mkGenFun argName expr ty
@@ -212,9 +242,11 @@ compileIfcBS _bsSettings prefix spec =
212242
ifcFields = mkSpecIfcFields triggers exts
213243

214244
streams = specStreams spec
215-
triggers = specTriggers spec
216245
exts = gatherExts streams triggers
217246

247+
-- Remove duplicates due to multiple guards for the same trigger.
248+
triggers = nubBy compareTrigger (specTriggers spec)
249+
218250
ifcDef :: BS.CDefn
219251
ifcDef = BS.Cstruct
220252
True
@@ -244,7 +276,9 @@ compileTypesBS _bsSettings prefix spec =
244276

245277
exprs = gatherExprs streams triggers
246278
streams = specStreams spec
247-
triggers = specTriggers spec
279+
280+
-- Remove duplicates due to multiple guards for the same trigger.
281+
triggers = nubBy compareTrigger (specTriggers spec)
248282

249283
-- Generate type declarations.
250284
mkTypeDeclns :: [UExpr] -> [BS.CDefn]
@@ -296,3 +330,21 @@ gatherExprs streams triggers = map streamUExpr streams
296330
where
297331
streamUExpr (Stream _ _ expr ty) = UExpr ty expr
298332
triggerUExpr (Trigger _ guard args) = UExpr Bool guard : args
333+
334+
-- | We consider triggers to be equal, if their names match and the number and
335+
-- types of arguments.
336+
compareTrigger :: Trigger -> Trigger -> Bool
337+
compareTrigger (Trigger name1 _ args1) (Trigger name2 _ args2)
338+
= name1 == name2 && compareArguments args1 args2
339+
340+
where
341+
compareArguments :: [UExpr] -> [UExpr] -> Bool
342+
compareArguments [] [] = True
343+
compareArguments [] _ = False
344+
compareArguments _ [] = False
345+
compareArguments (x:xs) (y:ys) = compareUExpr x y && compareArguments xs ys
346+
347+
compareUExpr :: UExpr -> UExpr -> Bool
348+
compareUExpr (UExpr ty1 _) (UExpr ty2 _)
349+
| Just Refl <- testEquality ty1 ty2 = True
350+
| otherwise = False
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- | Bluespec backend specific versions of selected `Copilot.Core` datatypes.
2+
module Copilot.Compile.Bluespec.Representation
3+
( UniqueTrigger (..)
4+
, UniqueTriggerId
5+
, mkUniqueTriggers
6+
)
7+
where
8+
9+
import Copilot.Core ( Trigger (..) )
10+
11+
-- | Internal unique name for a trigger.
12+
type UniqueTriggerId = String
13+
14+
-- | A `Copilot.Core.Trigger` with an unique name.
15+
data UniqueTrigger = UniqueTrigger UniqueTriggerId Trigger
16+
17+
-- | Given a list of triggers, make their names unique.
18+
mkUniqueTriggers :: [Trigger] -> [UniqueTrigger]
19+
mkUniqueTriggers ts = zipWith mkUnique ts [0..]
20+
where
21+
mkUnique :: Trigger -> Integer -> UniqueTrigger
22+
mkUnique t@(Trigger name _ _) n = UniqueTrigger (name ++ "_" ++ show n) t

0 commit comments

Comments
 (0)